diff --git a/examples/getstarted/config/server.js b/examples/getstarted/config/server.js
index 9ae03d02e7..33f7e1370f 100644
--- a/examples/getstarted/config/server.js
+++ b/examples/getstarted/config/server.js
@@ -1,3 +1,7 @@
+'use strict';
+
+const GoogleStrategy = require('passport-google-oauth2');
+
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
@@ -5,6 +9,32 @@ module.exports = ({ env }) => ({
// autoOpen: true,
auth: {
secret: env('ADMIN_JWT_SECRET', 'example-token'),
+ providers: [
+ {
+ uid: 'google',
+ displayName: 'Google',
+ icon: 'https://cdn2.iconfinder.com/data/icons/social-icons-33/128/Google-512.png',
+ createStrategy: strapi =>
+ new GoogleStrategy(
+ {
+ clientID: env('GOOGLE_CLIENT_ID'),
+ clientSecret: env('GOOGLE_CLIENT_SECRET'),
+ scope: [
+ 'https://www.googleapis.com/auth/userinfo.email',
+ 'https://www.googleapis.com/auth/userinfo.profile',
+ ],
+ callbackURL: strapi.admin.services.passport.getStrategyCallbackURL('google'),
+ },
+ (request, accessToken, refreshToken, profile, done) => {
+ done(null, {
+ email: profile.email,
+ firstname: profile.given_name,
+ lastname: profile.family_name,
+ });
+ }
+ ),
+ },
+ ],
},
},
});
diff --git a/examples/getstarted/package.json b/examples/getstarted/package.json
index 20fd014a86..dca7112dee 100644
--- a/examples/getstarted/package.json
+++ b/examples/getstarted/package.json
@@ -28,6 +28,7 @@
"@strapi/utils": "3.6.6",
"lodash": "4.17.21",
"mysql": "2.18.1",
+ "passport-google-oauth2": "0.2.0",
"pg": "8.6.0",
"sqlite3": "5.0.2",
"strapi-middleware-views": "3.6.6"
diff --git a/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js b/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js
index 6d99c91460..a44d088419 100644
--- a/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js
+++ b/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js
@@ -13,24 +13,34 @@ export const Column = styled(Row)`
flex-direction: column;
`;
+export const LayoutContent = ({ children }) => (
+
+ {children}
+
+);
+LayoutContent.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
const UnauthenticatedLayout = ({ children }) => {
return (
-
+
-
- {children}
-
+ {children}
);
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js b/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
index 6e65046efb..1ddb897b76 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
@@ -11,13 +11,15 @@ import {
TextInput,
Main,
FieldAction,
+ Row,
+ Link,
} from '@strapi/parts';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { useIntl } from 'react-intl';
import { Formik } from 'formik';
-import { Column } from '../../../../layouts/UnauthenticatedLayout';
+import { Column, LayoutContent } from '../../../../layouts/UnauthenticatedLayout';
import Form from './Form';
import Logo from '../Logo';
@@ -35,101 +37,113 @@ const FieldActionWrapper = styled(FieldAction)`
}
`;
-const Login = ({ onSubmit, schema }) => {
+const Login = ({ onSubmit, schema, children }) => {
const [passwordShown, setPasswordShown] = useState(false);
const { formatMessage } = useIntl();
return (
-
- {({ values, errors, handleChange }) => (
-
+ )}
+
+ {children}
+
+
+
+
+ {formatMessage({ id: 'Auth.link.forgot-password' })}
+
+
+
);
};
Login.defaultProps = {
+ children: null,
onSubmit: () => {},
};
Login.propTypes = {
+ children: PropTypes.node,
onSubmit: PropTypes.func,
schema: PropTypes.shape({
type: PropTypes.string.isRequired,
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js b/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js
index ee273000cb..0c6e8fc00b 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js
@@ -6,13 +6,7 @@ const FormWithFocus = props => {
useEffect(() => {
if (isSubmitting && !isValidating) {
- const errorNames = Object.keys(touched).reduce((prev, key) => {
- if (getIn(errors, key)) {
- prev.push(key);
- }
-
- return prev;
- }, []);
+ const errorNames = Object.keys(touched).filter(error => getIn(errors, error));
if (errorNames.length) {
let errorEl;
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 030477126d..42ba98db5b 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -6,7 +6,7 @@
"Auth.form.button.login": "Login",
"Auth.form.button.login.providers.error": "We cannot connect you through the selected provider.",
"Auth.form.button.login.providers.see-more": "See more",
- "Auth.form.button.login.strapi": "LOG IN VIA STRAPI",
+ "Auth.form.button.login.strapi": "Log in via Strapi",
"Auth.form.button.register": "LET'S START",
"Auth.form.button.reset-password": "Change password",
"Auth.form.confirmPassword.label": "Confirmation Password",
@@ -47,6 +47,8 @@
"Auth.link.signin": "Sign in",
"Auth.link.signin.account": "Already have an account?",
"Auth.login.sso.divider": "Or login with",
+ "Auth.login.sso.loading": "Loading providers...",
+ "Auth.login.sso.subtitle": "Login to your account via SSO",
"Auth.privacy-policy-agreement.policy": "privacy policy",
"Auth.privacy-policy-agreement.terms": "terms",
"Content Manager": "Content Manager",
diff --git a/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js b/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
index fefca630df..034684ab60 100644
--- a/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
+++ b/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
@@ -27,21 +27,22 @@ const Login = loginProps => {
return (
-
-
-
-
-
-
-
- {formatMessage({ id: 'Auth.login.sso.divider' })}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {formatMessage({ id: 'Auth.login.sso.divider' })}
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js
index 4c9620694e..6d9665601c 100644
--- a/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js
+++ b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js
@@ -84,7 +84,7 @@ const SSOProviders = ({ providers, displayAllProviders }) => {
})}
>
- •••
+ •••
diff --git a/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/index.js b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/index.js
index 920c245036..ac9d3d86de 100644
--- a/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/index.js
+++ b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/index.js
@@ -1,18 +1,35 @@
import React from 'react';
-import { Button, Flex, Padded, Separator } from '@buffetjs/core';
-import { LoadingIndicator } from '@buffetjs/styles';
import { Redirect, useHistory } from 'react-router-dom';
import styled from 'styled-components';
+import {
+ Divider,
+ Stack,
+ Row,
+ Box,
+ TableLabel,
+ Button,
+ Main,
+ Subtitle,
+ H1,
+ Link,
+ Text,
+ Loader,
+} from '@strapi/parts';
import { useIntl } from 'react-intl';
-import { BaselineAlignment } from '@strapi/helper-plugin';
-import Box from '../../../../../../admin/src/pages/AuthPage/components/Box';
-import Logo from '../../../../../../admin/src/pages/AuthPage/components/Logo';
-import Section from '../../../../../../admin/src/pages/AuthPage/components/Section';
-import ProviderButton from '../../../../components/ProviderButton';
import { useAuthProviders } from '../../../../hooks';
+import UnauthenticatedLayout, {
+ Column,
+ LayoutContent,
+} from '../../../../../../admin/src/layouts/UnauthenticatedLayout';
+import SSOProviders from './SSOProviders';
+import Logo from '../../../../../../admin/src/pages/AuthPage/components/Logo';
-const ProviderWrapper = styled.div`
- padding: 5px 4px;
+const DividerFull = styled(Divider)`
+ flex: 1;
+`;
+const AuthButton = styled(Button)`
+ display: inline-block;
+ width: 100%;
`;
const Providers = () => {
@@ -31,44 +48,49 @@ const Providers = () => {
}
return (
- <>
-
-
-
-
+
+
+
+
+
+
+ {formatMessage({ id: 'Auth.form.welcome.title' })}
+
+
+
+ {formatMessage({ id: 'Auth.login.sso.subtitle' })}
+
+
+
+
{isLoading ? (
-
+
+ {formatMessage({ id: 'Auth.login.sso.loading' })}
+
) : (
-
- {providers.map(provider => (
-
-
-
- ))}
-
+
)}
-
-
-
-
-
-
+
+
+
+ {formatMessage({ id: 'or' })}
+
+
+
+
+ {formatMessage({ id: 'Auth.form.button.login.strapi' })}
+
+
+
+
+
+
+ {formatMessage({ id: 'Auth.link.forgot-password' })}
+
-
-
- >
+
+
+
);
};
diff --git a/yarn.lock b/yarn.lock
index b158bdf6dc..222b1c3e21 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5558,6 +5558,11 @@ base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+base64url@3.x.x:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
+ integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
+
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@@ -15115,6 +15120,11 @@ oauth-sign@^0.9.0, oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+oauth@0.9.x:
+ version "0.9.15"
+ resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
+ integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
+
object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -15813,6 +15823,13 @@ pascalcase@^0.1.1:
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+passport-google-oauth2@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/passport-google-oauth2/-/passport-google-oauth2-0.2.0.tgz#fc9ea59e7091f02e24fd16d6be9257ea982ebbc3"
+ integrity sha512-62EdPtbfVdc55nIXi0p1WOa/fFMM8v/M8uQGnbcXA4OexZWCnfsEi3wo2buag+Is5oqpuHzOtI64JpHk0Xi5RQ==
+ dependencies:
+ passport-oauth2 "^1.1.2"
+
passport-local@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
@@ -15820,6 +15837,17 @@ passport-local@1.0.0:
dependencies:
passport-strategy "1.x.x"
+passport-oauth2@^1.1.2:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.6.0.tgz#5f599735e0ea40ea3027643785f81a3a9b4feb50"
+ integrity sha512-emXPLqLcVEcLFR/QvQXZcwLmfK8e9CqvMgmOFJxcNT3okSFMtUbRRKpY20x5euD+01uHsjjCa07DYboEeLXYiw==
+ dependencies:
+ base64url "3.x.x"
+ oauth "0.9.x"
+ passport-strategy "1.x.x"
+ uid2 "0.0.x"
+ utils-merge "1.x.x"
+
passport-strategy@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
@@ -20800,6 +20828,11 @@ uid-number@0.0.6:
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
+uid2@0.0.x:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82"
+ integrity sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=
+
umask@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
@@ -21139,7 +21172,7 @@ utila@~0.4:
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
-utils-merge@1.0.1:
+utils-merge@1.0.1, utils-merge@1.x.x:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=