|
@@ -2,6 +2,7 @@ import React, {
|
|
|
useState, useEffect, useCallback,
|
|
useState, useEffect, useCallback,
|
|
|
} from 'react';
|
|
} from 'react';
|
|
|
|
|
|
|
|
|
|
+import type { IExternalAuthProviderType } from '@growi/core';
|
|
|
import { LoadingSpinner } from '@growi/ui/dist/components';
|
|
import { LoadingSpinner } from '@growi/ui/dist/components';
|
|
|
import { useTranslation } from 'next-i18next';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
import { useRouter } from 'next/router';
|
|
import { useRouter } from 'next/router';
|
|
@@ -20,6 +21,44 @@ import styles from './LoginForm.module.scss';
|
|
|
|
|
|
|
|
const moduleClass = styles['login-form'];
|
|
const moduleClass = styles['login-form'];
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+const ExternalAuthButton = ({ authType }: {authType: IExternalAuthProviderType}): JSX.Element => {
|
|
|
|
|
+ const { t } = useTranslation();
|
|
|
|
|
+
|
|
|
|
|
+ const authIcon = {
|
|
|
|
|
+ google: <span className="growi-custom-icons align-bottom">google</span>,
|
|
|
|
|
+ github: <span className="growi-custom-icons align-bottom">github</span>,
|
|
|
|
|
+ facebook: <span className="growi-custom-icons align-bottom">facebook</span>,
|
|
|
|
|
+ oidc: <span className="growi-custom-icons align-bottom">openid</span>,
|
|
|
|
|
+ saml: <span className="material-symbols-outlined align-bottom">key</span>,
|
|
|
|
|
+ };
|
|
|
|
|
+ const authBtn = `btn-auth-${authType.toString()}`;
|
|
|
|
|
+ const signin = {
|
|
|
|
|
+ google: 'Google',
|
|
|
|
|
+ github: 'GitHub',
|
|
|
|
|
+ facebook: 'Facebook',
|
|
|
|
|
+ oidc: 'OIDC',
|
|
|
|
|
+ saml: 'SAML',
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleLoginWithExternalAuth = () => {
|
|
|
|
|
+ window.location.href = `/passport/${authType.toString()}`;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <button
|
|
|
|
|
+ key={`btn-auth-${authType.toString()}`}
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ className={`btn btn-secondary ${authBtn} my-2 col-10 col-sm-7 mx-auto d-flex`}
|
|
|
|
|
+ onClick={handleLoginWithExternalAuth}
|
|
|
|
|
+ >
|
|
|
|
|
+ <span>{authIcon[authType.toString()]}</span>
|
|
|
|
|
+ <span className="flex-grow-1">{t('Sign in with External auth', { signin: signin[authType.toString()] })}</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
type LoginFormProps = {
|
|
type LoginFormProps = {
|
|
|
username?: string,
|
|
username?: string,
|
|
|
name?: string,
|
|
name?: string,
|
|
@@ -31,7 +70,7 @@ type LoginFormProps = {
|
|
|
isLocalStrategySetup: boolean,
|
|
isLocalStrategySetup: boolean,
|
|
|
isLdapStrategySetup: boolean,
|
|
isLdapStrategySetup: boolean,
|
|
|
isLdapSetupFailed: boolean,
|
|
isLdapSetupFailed: boolean,
|
|
|
- objOfIsExternalAuthEnableds?: any,
|
|
|
|
|
|
|
+ enabledExternalAuthType?: IExternalAuthProviderType[],
|
|
|
isMailerSetup?: boolean,
|
|
isMailerSetup?: boolean,
|
|
|
externalAccountLoginError?: IExternalAccountLoginError,
|
|
externalAccountLoginError?: IExternalAccountLoginError,
|
|
|
}
|
|
}
|
|
@@ -42,10 +81,10 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
|
|
|
|
|
|
|
|
const {
|
|
const {
|
|
|
isLocalStrategySetup, isLdapStrategySetup, isLdapSetupFailed, isPasswordResetEnabled,
|
|
isLocalStrategySetup, isLdapStrategySetup, isLdapSetupFailed, isPasswordResetEnabled,
|
|
|
- isEmailAuthenticationEnabled, registrationMode, registrationWhitelist, isMailerSetup, objOfIsExternalAuthEnableds,
|
|
|
|
|
|
|
+ isEmailAuthenticationEnabled, registrationMode, registrationWhitelist, isMailerSetup, enabledExternalAuthType,
|
|
|
} = props;
|
|
} = props;
|
|
|
const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
|
|
const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
|
|
|
- const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
|
|
|
|
|
|
|
+ const isSomeExternalAuthEnabled = enabledExternalAuthType != null && enabledExternalAuthType.length > 0;
|
|
|
|
|
|
|
|
// states
|
|
// states
|
|
|
const [isRegistering, setIsRegistering] = useState(false);
|
|
const [isRegistering, setIsRegistering] = useState(false);
|
|
@@ -81,12 +120,6 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
|
|
|
return t(key);
|
|
return t(key);
|
|
|
}, [t]);
|
|
}, [t]);
|
|
|
|
|
|
|
|
- const handleLoginWithExternalAuth = useCallback((e) => {
|
|
|
|
|
- const auth = e.currentTarget.id;
|
|
|
|
|
-
|
|
|
|
|
- window.location.href = `/passport/${auth}`;
|
|
|
|
|
- }, []);
|
|
|
|
|
-
|
|
|
|
|
const resetLoginErrors = useCallback(() => {
|
|
const resetLoginErrors = useCallback(() => {
|
|
|
if (loginErrors.length === 0) return;
|
|
if (loginErrors.length === 0) return;
|
|
|
setLoginErrors([]);
|
|
setLoginErrors([]);
|
|
@@ -265,38 +298,12 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
- const renderExternalAuthInput = useCallback((auth) => {
|
|
|
|
|
- const authIcon = {
|
|
|
|
|
- google: <span className="growi-custom-icons align-bottom">google</span>,
|
|
|
|
|
- github: <span className="growi-custom-icons align-bottom">github</span>,
|
|
|
|
|
- facebook: <span className="growi-custom-icons align-bottom">facebook</span>,
|
|
|
|
|
- oidc: <span className="growi-custom-icons align-bottom">openid</span>,
|
|
|
|
|
- saml: <span className="material-symbols-outlined align-bottom">key</span>,
|
|
|
|
|
- };
|
|
|
|
|
- const authBtn = `btn-auth-${auth}`;
|
|
|
|
|
- const signin = {
|
|
|
|
|
- google: 'Google',
|
|
|
|
|
- github: 'GitHub',
|
|
|
|
|
- facebook: 'Facebook',
|
|
|
|
|
- oidc: 'OIDC',
|
|
|
|
|
- saml: 'SAML',
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <button
|
|
|
|
|
- key={`btn-auth-${auth}`}
|
|
|
|
|
- type="button"
|
|
|
|
|
- className={`btn btn-secondary ${authBtn} my-2 col-10 col-sm-7 mx-auto d-flex`}
|
|
|
|
|
- onClick={handleLoginWithExternalAuth}
|
|
|
|
|
- >
|
|
|
|
|
- <span>{authIcon[auth]}</span>
|
|
|
|
|
- <span className="flex-grow-1">{t('Sign in with External auth', { signin: signin[auth] })}</span>
|
|
|
|
|
- </button>
|
|
|
|
|
- );
|
|
|
|
|
- }, [handleLoginWithExternalAuth, t]);
|
|
|
|
|
-
|
|
|
|
|
const renderExternalAuthLoginForm = useCallback(() => {
|
|
const renderExternalAuthLoginForm = useCallback(() => {
|
|
|
- const { objOfIsExternalAuthEnableds } = props;
|
|
|
|
|
|
|
+ const { enabledExternalAuthType } = props;
|
|
|
|
|
+
|
|
|
|
|
+ if (enabledExternalAuthType == null) {
|
|
|
|
|
+ return <></>;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
@@ -304,16 +311,11 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
|
|
|
<p className="text-white mb-0">{t('or')}</p>
|
|
<p className="text-white mb-0">{t('or')}</p>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="mt-2">
|
|
<div className="mt-2">
|
|
|
- {Object.keys(objOfIsExternalAuthEnableds).map((auth) => {
|
|
|
|
|
- if (!objOfIsExternalAuthEnableds[auth]) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- return renderExternalAuthInput(auth);
|
|
|
|
|
- })}
|
|
|
|
|
|
|
+ { enabledExternalAuthType.map(authType => <ExternalAuthButton authType={authType} />) }
|
|
|
</div>
|
|
</div>
|
|
|
</>
|
|
</>
|
|
|
);
|
|
);
|
|
|
- }, [props, t, renderExternalAuthInput]);
|
|
|
|
|
|
|
+ }, [props, t, enabledExternalAuthType]);
|
|
|
|
|
|
|
|
const resetRegisterErrors = useCallback(() => {
|
|
const resetRegisterErrors = useCallback(() => {
|
|
|
if (registerErrors.length === 0) return;
|
|
if (registerErrors.length === 0) return;
|