Skip to content

Commit

Permalink
added project config usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Aby-JS committed Feb 1, 2024
1 parent f5c9ed9 commit a27ae18
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 34 deletions.
2 changes: 2 additions & 0 deletions packages/react-sdk/src/contexts/CorbadoAppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface CorbadoAppContextProps {
getUserAuthMethods: (email: string) => Promise<Result<UserAuthMethods, AuthMethodsListError | undefined>>;
userExists(email: string): Promise<Result<boolean, RecoverableError | undefined>>;
logout: () => void;
setGlobalError: (error: NonRecoverableError | undefined) => void;
}

const missingImplementation = (): never => {
Expand Down Expand Up @@ -86,6 +87,7 @@ export const initialContext: CorbadoAppContextProps = {
getUserAuthMethods: missingImplementation,
getProjectConfig: missingImplementation,
userExists: missingImplementation,
setGlobalError: missingImplementation,
};

export const CorbadoAppContext = createContext<CorbadoAppContextProps>(initialContext);
4 changes: 3 additions & 1 deletion packages/react-sdk/src/contexts/CorbadoAppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ type CorbadoAppProviderParams = PropsWithChildren<{
loading: boolean;
isAuthenticated: boolean;
globalError: NonRecoverableError | undefined;
setGlobalError: (error: NonRecoverableError | undefined) => void;
}>;

export const CorbadoAppProvider: FC<CorbadoAppProviderParams> = memo(
({ children, corbadoApp, loading, globalError, isAuthenticated }) => {
({ children, corbadoApp, loading, globalError, isAuthenticated, setGlobalError }) => {
/** Passkey Authentication APIs */
const signUpWithPasskey = useCallback(
(email: string, username: string) => {
Expand Down Expand Up @@ -149,6 +150,7 @@ export const CorbadoAppProvider: FC<CorbadoAppProviderParams> = memo(
getProjectConfig,
userExists,
logout,
setGlobalError,
}}
>
{children}
Expand Down
9 changes: 7 additions & 2 deletions packages/react-sdk/src/contexts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const CorbadoProvider: FC<CorbadoProviderParams> = ({ children, corbadoAp
const [corbadoApp] = useState(() => corbadoAppInstance ?? new CorbadoApp(corbadoParams));
const [loading, setLoading] = useState<boolean>(true);
const [user, setUser] = useState<SessionUser | undefined>();
const [globalError, setGlobalError] = useState<NonRecoverableError | undefined>();
const [globalError, setGlobalErrorState] = useState<NonRecoverableError | undefined>();
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const [shortSession, setShortSession] = useState<string | undefined>();
const initialized = useRef(false);
Expand All @@ -34,7 +34,7 @@ export const CorbadoProvider: FC<CorbadoProviderParams> = ({ children, corbadoAp
});

corbadoApp.globalErrors.subscribe(value => {
setGlobalError(value);
setGlobalErrorState(value);
});

corbadoApp.authService.authStateChanges.subscribe(value => {
Expand All @@ -46,6 +46,10 @@ export const CorbadoProvider: FC<CorbadoProviderParams> = ({ children, corbadoAp
});
}, []);

const setGlobalError = (error: NonRecoverableError | undefined) => {
corbadoApp.globalErrors.next(error);
};

return (
<CorbadoSessionProvider
loading={loading}
Expand All @@ -58,6 +62,7 @@ export const CorbadoProvider: FC<CorbadoProviderParams> = ({ children, corbadoAp
loading={loading}
isAuthenticated={isAuthenticated}
globalError={globalError}
setGlobalError={setGlobalError}
>
{children}
</CorbadoAppProvider>
Expand Down
2 changes: 2 additions & 0 deletions packages/react-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
InvalidFullnameError,
InvalidOtpInputError,
InvalidPasskeyError,
NonRecoverableError,
NoPasskeyAvailableError,
PasskeyChallengeCancelledError,
UnknownUserError,
Expand All @@ -20,4 +21,5 @@ export {
UserAlreadyExistsError,
InvalidFullnameError,
InvalidEmailError,
NonRecoverableError,
};
24 changes: 14 additions & 10 deletions packages/react/src/components/authentication/AuthFormScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { PrimaryButton } from '../ui/buttons/Button';
export interface AuthFormScreenProps extends CustomizableComponent {
headerText: string;
subHeaderText: string;
flowChangeButtonText: string;
flowChangeButtonText?: string;
submitButtonText: ReactNode;
loading: boolean;
onSubmit: () => void;
Expand All @@ -26,15 +26,19 @@ export const AuthFormScreen: FC<AuthFormScreenProps> = memo(
return (
<>
<Header>{headerText}</Header>
<SubHeader>
{subHeaderText}
<span
className='cb-link-secondary'
onClick={changeFlow}
>
{flowChangeButtonText}
</span>
</SubHeader>

{flowChangeButtonText ? (
<SubHeader>
{subHeaderText}
<span
className='cb-link-secondary'
onClick={changeFlow}
>
{flowChangeButtonText}
</span>
</SubHeader>
) : null}

<form
className='cb-form'
onSubmit={handleSubmit}
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/contexts/FlowHandlerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
VerificationMethods,
} from '@corbado/shared-ui';
import { ScreenNames } from '@corbado/shared-ui';
import type { ProjectConfig } from '@corbado/types';
import { createContext } from 'react';

export interface FlowHandlerContextProps {
Expand All @@ -16,6 +17,7 @@ export interface FlowHandlerContextProps {
currentUserState: UserState;
currentVerificationMethod: VerificationMethods | undefined;
initialized: boolean;
projectConfig: ProjectConfig | undefined;
navigateBack: () => ScreenNames;
emitEvent: (event?: FlowHandlerEvents, eventOptions?: FlowHandlerEventOptions) => Promise<void> | undefined;
changeFlow: () => void;
Expand All @@ -28,6 +30,7 @@ export const initialContext: FlowHandlerContextProps = {
currentUserState: {},
currentVerificationMethod: undefined,
initialized: false,
projectConfig: undefined,
navigateBack: () => ScreenNames.Start,
emitEvent: () => Promise.reject(),
changeFlow: () => void 0,
Expand Down
13 changes: 10 additions & 3 deletions packages/react/src/contexts/FlowHandlerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
VerificationMethods,
} from '@corbado/shared-ui';
import { FlowHandler, FlowHandlerEvents, ScreenNames } from '@corbado/shared-ui';
import type { ProjectConfig } from '@corbado/types';
import i18n from 'i18next';
import type { FC, PropsWithChildren } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
Expand All @@ -33,6 +34,7 @@ export const FlowHandlerProvider: FC<PropsWithChildren<Props>> = ({
const [currentUserState, setCurrentUserState] = useState<UserState>({});
const [currentFlow, setCurrentFlow] = useState<FlowNames>();
const [initialized, setInitialized] = useState(false);
const [projectConfig, setProjectConfig] = useState<ProjectConfig | undefined>(undefined);
const currentFlowType = useRef<FlowTypeText>();
const verificationMethod = useRef<VerificationMethods>();
const onFlowChangeCbId = useRef<number>(0);
Expand All @@ -44,12 +46,14 @@ export const FlowHandlerProvider: FC<PropsWithChildren<Props>> = ({
}

void (async () => {
const projectConfig = await getProjectConfig();
if (projectConfig.err) {
const projectConfigResult = await getProjectConfig();
if (projectConfigResult.err) {
// currently there are no errors that can be thrown here
return;
}
const flowHandler = new FlowHandler(projectConfig.val, onLoggedIn, initialFlowType);

const projectConfig = projectConfigResult.val;
const flowHandler = new FlowHandler(projectConfig, onLoggedIn, initialFlowType);

onFlowChangeCbId.current = flowHandler.onFlowChange(updates => {
updates.flowName && setCurrentFlow(updates.flowName);
Expand All @@ -69,6 +73,8 @@ export const FlowHandlerProvider: FC<PropsWithChildren<Props>> = ({
});

await flowHandler.init(corbadoApp, i18n);

setProjectConfig(projectConfig);
setFlowHandler(flowHandler);
setInitialized(true);
})();
Expand Down Expand Up @@ -106,6 +112,7 @@ export const FlowHandlerProvider: FC<PropsWithChildren<Props>> = ({
currentUserState,
currentVerificationMethod: verificationMethod.current,
initialized,
projectConfig,
changeFlow,
navigateBack,
emitEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AuthFormScreen, FormInput } from '../../../../components';
import useFlowHandler from '../../../../hooks/useFlowHandler';

export const Start = () => {
const { emitEvent, currentUserState } = useFlowHandler();
const { emitEvent, currentUserState, projectConfig } = useFlowHandler();
const { t } = useTranslation('translation', { keyPrefix: `authentication.login.start` });
const [loading, setLoading] = useState(false);
const initialized = useRef(false);
Expand All @@ -28,7 +28,10 @@ export const Start = () => {

const headerText = useMemo(() => t('header'), [t]);
const subHeaderText = useMemo(() => t('subheader'), [t]);
const flowChangeButtonText = useMemo(() => t('button_signup'), [t]);
const flowChangeButtonText = useMemo(
() => (projectConfig?.allowUserRegistration ? t('button_signup') : undefined),
[t],
);
const emailFieldLabel = useMemo(() => t('textField_email'), [t]);
const submitButtonText = useMemo(() => t('button_submit'), [t]);

Expand Down
21 changes: 13 additions & 8 deletions packages/react/src/screens/base/authentication/signup/Start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AuthFormScreen, FormInput } from '../../../../components';
import useFlowHandler from '../../../../hooks/useFlowHandler';

export const Start = () => {
const { currentUserState, emitEvent } = useFlowHandler();
const { currentUserState, projectConfig, emitEvent } = useFlowHandler();
const { t } = useTranslation('translation', { keyPrefix: `authentication.signup.start` });
const [emailError, setEmailError] = useState<RecoverableError | null>(null);
const [userNameError, setUserNameError] = useState<RecoverableError | null>(null);
Expand All @@ -30,8 +30,11 @@ export const Start = () => {

const handleSubmit = useCallback(() => {
setLoading(true);

const fullName = projectConfig?.userFullNameRequired ? fullNameRef.current?.value : emailRef.current?.value;

void emitEvent(FlowHandlerEvents.PrimaryButton, {
userStateUpdate: { email: emailRef.current?.value, fullName: fullNameRef.current?.value },
userStateUpdate: { email: emailRef.current?.value, fullName },
});
}, [emitEvent]);

Expand All @@ -45,12 +48,14 @@ export const Start = () => {
submitButtonText={submitButtonText}
loading={loading}
>
<FormInput
name='fullName'
label={nameFieldLabel}
error={userNameError?.translatedMessage}
ref={el => el && (fullNameRef.current = el)}
/>
{projectConfig?.userFullNameRequired && (
<FormInput
name='fullName'
label={nameFieldLabel}
error={userNameError?.translatedMessage}
ref={el => el && (fullNameRef.current = el)}
/>
)}
<FormInput
name='name'
type='email'
Expand Down
9 changes: 9 additions & 0 deletions packages/react/src/screens/core/SignUp.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { NonRecoverableError, useCorbado } from '@corbado/react-sdk';
import { FlowType } from '@corbado/shared-ui';
import type { CorbadoSignUpConfig } from '@corbado/types';
import type { FC } from 'react';
import React from 'react';

import { AuthFlow } from '../../components';
import FlowHandlerProvider from '../../contexts/FlowHandlerProvider';
import useFlowHandler from '../../hooks/useFlowHandler';

const SignUp: FC<CorbadoSignUpConfig> = ({ onSignedUp, navigateToLogin }) => {
const { projectConfig } = useFlowHandler();
const { setGlobalError } = useCorbado();

if (!projectConfig?.allowUserRegistration) {
setGlobalError(NonRecoverableError.userRegistrationNotAllowed());
}

return (
<div className='cb-container'>
<FlowHandlerProvider
Expand Down
4 changes: 4 additions & 0 deletions packages/shared-ui/src/flowHandler/flowHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export class FlowHandler {
* It sets the current flow to the specified flow, the current screen to the Start screen, and initializes the screen history as an empty array.
*/
constructor(projectConfig: ProjectConfig, onLoggedIn: () => void, initialFlowType: FlowType = FlowType.SignUp) {
if (!projectConfig.allowUserRegistration) {
initialFlowType = FlowType.Login;
}

this.#config = new FlowHandlerConfig(onLoggedIn, projectConfig, initialFlowType);
this.#screenHistory = [];
this.#currentScreen = this.#config.initialScreenName;
Expand Down
8 changes: 8 additions & 0 deletions packages/web-core/src/utils/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ export class NonRecoverableError extends CorbadoError {
static client(message: string, link: string) {
return new NonRecoverableError('client', message, link);
}

static userRegistrationNotAllowed() {
return new NonRecoverableError(
'server',
'User registration is not allowed for this project',
'https://docs.corbado.com/overview/sign-up-and-login-with-passkeys/user-flow-configuration#id-2.-public-sign-ups',
);
}
}

export class UserAlreadyExistsError extends RecoverableError {
Expand Down
10 changes: 2 additions & 8 deletions playground/react/.env
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# Flow: PasskeySignupWithEmailOTPFallback
REACT_APP_CORBADO_PROJECT_ID=pro-503401103218055321

# Flow: EmailOTPSignupWithPasskey
# REACT_APP_CORBADO_PROJECT_ID=pro-8793272752372175738
REACT_APP_CORBADO_PROJECT_ID=pro-8793272752372175738

# Flow: PasskeySignupWithEmailOTPFallback + Email Link verification method
# REACT_APP_CORBADO_PROJECT_ID=pro-423122463392265807

# Flow: EmailOTPSignupWithPasskey + Email Link verification method
# REACT_APP_CORBADO_PROJECT_ID=pro-3962864614985263396
# REACT_APP_CORBADO_PROJECT_ID=pro-423122463392265807

0 comments on commit a27ae18

Please sign in to comment.