Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy-playground-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
test:
needs: deploy
timeout-minutes: 60
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

steps:
- name: Checkout code
Expand Down
13 changes: 13 additions & 0 deletions packages/connect-react/src/components/CorbadoConnectDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Passkey } from '@corbado/web-core';
import type { FC } from 'react';
import React from 'react';

import AppendAfterError from './append/append-init/AppendAfterError';
import AppendInitLoaded2 from './append/append-init/AppendInitLoaded2';
import AppendInitLoading from './append/append-init/AppendInitLoading';
import AppendSuccessScreen from './append/AppendSuccessScreen';
Expand Down Expand Up @@ -247,6 +248,18 @@ const CorbadoConnectDemo: FC<CorbadoConnectDemoConfig> = _ => {
/>
),
},
{
headline: 'Append screen after error',
description: 'This screen is shown to the user after having issues with passkeys.',
reactElement: (
<AppendAfterError
errorMessage={''}
appendLoading={false}
handleSubmit={() => console.log('Submit')}
handleSkip={() => console.log('Skip')}
/>
),
},
];

const login: Element[] = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { ExcludeCredentialsMatchError, PasskeyChallengeCancelledError } from '@corbado/web-core';
import log from 'loglevel';
import React, { useState } from 'react';
import React, { useCallback, useState } from 'react';

import useAppendProcess from '../../hooks/useAppendProcess';
import useShared from '../../hooks/useShared';
import { AppendScreenType } from '../../types/screenTypes';
import { AppendSituationCode, getAppendErrorMessage } from '../../types/situations';
import { PasskeyIssueIcon } from '../shared/icons/PasskeyIssueIcon';
import { LinkButton } from '../shared/LinkButton';
import { Notification } from '../shared/Notification';
import { PrimaryButton } from '../shared/PrimaryButton';
import AppendAfterError from './append-init/AppendAfterError';

const AppendAfterErrorScreen = ({ attestationOptions }: { attestationOptions: string }) => {
const { navigateToScreen, handleErrorSoft, handleErrorHard, handleCredentialExistsError, handleSkip } =
useAppendProcess();
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [loading, setLoading] = useState(false);
const [skipping, setSkipping] = useState(false);
const { getConnectService } = useShared();

const onSubmitClick = async () => {
Expand All @@ -39,7 +37,10 @@ const AppendAfterErrorScreen = ({ attestationOptions }: { attestationOptions: st
}

setLoading(false);
navigateToScreen(AppendScreenType.Success);
navigateToScreen(AppendScreenType.Success, {
aaguidName: res.val.passkeyOperation.aaguidDetails?.name,
aaguidIcon: res.val.passkeyOperation.aaguidDetails?.iconLight,
});
};

const handleSituation = (situationCode: AppendSituationCode) => {
Expand All @@ -57,7 +58,8 @@ const AppendAfterErrorScreen = ({ attestationOptions }: { attestationOptions: st
void handleErrorHard(situationCode, false);
break;
case AppendSituationCode.ClientPasskeyOperationCancelled:
void handleErrorSoft(situationCode, true);
setLoading(false);
void handleErrorSoft(situationCode, true, true);
break;
case AppendSituationCode.ClientExcludeCredentialsMatch:
void handleCredentialExistsError();
Expand All @@ -68,36 +70,22 @@ const AppendAfterErrorScreen = ({ attestationOptions }: { attestationOptions: st
}
};

const onSkip = useCallback(() => {
if (skipping || loading) {
return;
}

setSkipping(true);
void handleSituation(AppendSituationCode.ExplicitSkipByUser);
}, [skipping, loading]);

return (
<div className='cb-append-after-error-container cb-connect-append-border'>
<div className='cb-h2'>Issues using passkeys?</div>
{errorMessage ? (
<Notification
className='cb-error-notification'
message={errorMessage}
/>
) : null}
<div className='cb-append-after-error-icons'>
<PasskeyIssueIcon className='cb-append-after-error-icon' />
</div>
<div className='cb-p'>We detected you had an issue using your passkey.</div>
<div className='cb-p'>Try adding another passkey to resolve the problem.</div>
<div className='cb-append-after-error-cta'>
<PrimaryButton
isLoading={loading}
onClick={() => void onSubmitClick()}
className='cb-append-after-error-button'
>
Add passkey
</PrimaryButton>
<LinkButton
onClick={() => void handleSituation(AppendSituationCode.ExplicitSkipByUser)}
className='cb-append-after-error-fallback'
>
Skip
</LinkButton>
</div>
</div>
<AppendAfterError
appendLoading={loading}
errorMessage={errorMessage}
handleSubmit={() => void onSubmitClick()}
handleSkip={onSkip}
/>
);
};

Expand Down

This file was deleted.

75 changes: 50 additions & 25 deletions packages/connect-react/src/components/append/AppendInitScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';

import useAppendProcess from '../../hooks/useAppendProcess';
import useShared from '../../hooks/useShared';
import { Flags } from '../../types/flags';
import { AppendScreenType } from '../../types/screenTypes';
import { AppendSituationCode, getAppendErrorMessage } from '../../types/situations';
import { StatefulLoader } from '../../utils/statefulLoader';
Expand All @@ -27,8 +28,9 @@ const AppendInitScreen = () => {
handleSkip,
handleCredentialExistsError,
onReadMoreClick,
setFlags,
} = useAppendProcess();
const { getConnectService } = useShared();
const { sharedConfig, getConnectService } = useShared();
const [attestationOptions, setAttestationOptions] = useState('');
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [appendLoading, setAppendLoading] = useState(false);
Expand Down Expand Up @@ -85,6 +87,13 @@ const AppendInitScreen = () => {
return handleSituation(AppendSituationCode.CboApiNotAvailablePreAuthenticator);
}

// we load flags from backend first, then we override them with the ones that are specified in the component's config
const flags = new Flags(res.val.flags);
if (sharedConfig.flags) {
flags.addFlags(sharedConfig.flags);
}
setFlags(flags);

if (!res.val.appendAllowed) {
return handleSituation(AppendSituationCode.DeniedByPartialRollout);
}
Expand Down Expand Up @@ -123,6 +132,10 @@ const AppendInitScreen = () => {

setAttestationOptions(startAppendRes.val.attestationOptions);
statefulLoader.current.finish();

if (flags?.hasSupportForAutomaticAppend()) {
await handleSubmit(startAppendRes.val.attestationOptions, false);
}
};

log.debug('init AppendInitScreen');
Expand All @@ -137,33 +150,41 @@ const AppendInitScreen = () => {
};
}, []);

const handleSubmit = useCallback(async () => {
if (appendLoading || skipping) {
return;
}
const handleSubmit = useCallback(
async (attestationOptions: string, showErrorIfCancelled: boolean) => {
console.log('handleSubmit', attestationOptions);
if (appendLoading || skipping) {
return;
}

setAppendLoading(true);
setErrorMessage(undefined);
setAppendLoading(true);
setErrorMessage(undefined);

const res = await getConnectService().completeAppend(attestationOptions);
if (res.err) {
if (res.val instanceof ExcludeCredentialsMatchError) {
return handleSituation(AppendSituationCode.ClientExcludeCredentialsMatch);
}
const res = await getConnectService().completeAppend(attestationOptions);
if (res.err) {
if (res.val instanceof ExcludeCredentialsMatchError) {
return handleSituation(AppendSituationCode.ClientExcludeCredentialsMatch);
}

if (res.val instanceof PasskeyChallengeCancelledError) {
return handleSituation(AppendSituationCode.ClientPasskeyOperationCancelled);
}
if (res.val instanceof PasskeyChallengeCancelledError) {
if (showErrorIfCancelled) {
return handleSituation(AppendSituationCode.ClientPasskeyOperationCancelled);
} else {
return handleSituation(AppendSituationCode.ClientPasskeyOperationCancelledSilent);
}
}

return handleSituation(AppendSituationCode.CboApiNotAvailablePostAuthenticator);
}
return handleSituation(AppendSituationCode.CboApiNotAvailablePostAuthenticator);
}

setAppendLoading(false);
navigateToScreen(AppendScreenType.Success, {
aaguidName: res.val.passkeyOperation.aaguidDetails?.name,
aaguidIcon: res.val.passkeyOperation.aaguidDetails?.iconLight,
});
}, [attestationOptions, config, getConnectService, appendLoading, skipping]);
setAppendLoading(false);
navigateToScreen(AppendScreenType.Success, {
aaguidName: res.val.passkeyOperation.aaguidDetails?.name,
aaguidIcon: res.val.passkeyOperation.aaguidDetails?.iconLight,
});
},
[config, getConnectService, appendLoading, skipping],
);

const handleSituation = async (situationCode: AppendSituationCode) => {
log.debug(`situation: ${situationCode}`);
Expand All @@ -182,7 +203,11 @@ const AppendInitScreen = () => {
statefulLoader.current.finishWithError();
break;
case AppendSituationCode.ClientPasskeyOperationCancelled:
void handleErrorSoft(situationCode, true);
void handleErrorSoft(situationCode, true, true);
setAppendLoading(false);
break;
case AppendSituationCode.ClientPasskeyOperationCancelledSilent:
void handleErrorSoft(situationCode, true, false);
setAppendLoading(false);
break;
case AppendSituationCode.ClientExcludeCredentialsMatch:
Expand Down Expand Up @@ -224,7 +249,7 @@ const AppendInitScreen = () => {
void onReadMoreClick();
setAppendInitState(AppendInitState.ShowBenefits);
}}
handleSubmit={() => void handleSubmit()}
handleSubmit={() => void handleSubmit(attestationOptions, true)}
handleSkip={() => onSkip()}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useMemo } from 'react';
import useAppendProcess from '../../hooks/useAppendProcess';
import { AppendScreenType } from '../../types/screenTypes';
import AppendAfterErrorScreen from './AppendAfterErrorScreen';
import AppendAfterHybridLoginScreen from './AppendAfterHybridLoginScreen';
import AppendInitScreen from './AppendInitScreen';
import AppendSuccessScreen from './AppendSuccessScreen';

Expand All @@ -15,7 +14,7 @@ const CorbadoConnectAppendContainer = () => {
case AppendScreenType.Init:
return <AppendInitScreen />;
case AppendScreenType.AfterHybridLogin:
return <AppendAfterHybridLoginScreen {...currentScreenOptions} />;
return <AppendAfterErrorScreen {...currentScreenOptions} />;
case AppendScreenType.AfterError:
return <AppendAfterErrorScreen {...currentScreenOptions} />;
case AppendScreenType.Success:
Expand Down
Loading
Loading