Skip to content

Commit b6f0613

Browse files
dstaleyLekoArts
andauthored
fix(clerk-js,elements,types,ui): Account for null in supported_first_factors (#3938)
Co-authored-by: Lennart <lekoarts@gmail.com>
1 parent 1305967 commit b6f0613

File tree

11 files changed

+40
-30
lines changed

11 files changed

+40
-30
lines changed

.changeset/tasty-dots-guess.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@clerk/clerk-js": patch
3+
"@clerk/elements": patch
4+
"@clerk/types": patch
5+
"@clerk/ui": patch
6+
---
7+
8+
In certain situations the Frontend API response contains [`supported_first_factors`](https://clerk.com/docs/reference/frontend-api/tag/Sign-Ins#operation/createSignIn!c=200&path=response/supported_first_factors&t=response) with a `null` value while the current code always assumed to receive an array. `SignInResource['supportedFirstFactors']` has been updated to account for that and any code accessing this value has been made more resilient against `null` values.

packages/clerk-js/src/core/resources/SignIn.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class SignIn extends BaseResource implements SignInResource {
5656
id?: string;
5757
status: SignInStatus | null = null;
5858
supportedIdentifiers: SignInIdentifier[] = [];
59-
supportedFirstFactors: SignInFirstFactor[] = [];
59+
supportedFirstFactors: SignInFirstFactor[] | null = [];
6060
supportedSecondFactors: SignInSecondFactor[] | null = null;
6161
firstFactorVerification: VerificationResource = new Verification(null);
6262
secondFactorVerification: VerificationResource = new Verification(null);
@@ -230,7 +230,7 @@ export class SignIn extends BaseResource implements SignInResource {
230230

231231
await this.create({ identifier });
232232

233-
const web3FirstFactor = this.supportedFirstFactors.find(
233+
const web3FirstFactor = this.supportedFirstFactors?.find(
234234
f => f.strategy === 'web3_metamask_signature',
235235
) as Web3SignatureFactor;
236236

@@ -338,7 +338,7 @@ export class SignIn extends BaseResource implements SignInResource {
338338
this.status = data.status;
339339
this.supportedIdentifiers = data.supported_identifiers;
340340
this.identifier = data.identifier;
341-
this.supportedFirstFactors = deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[];
341+
this.supportedFirstFactors = deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[] | null;
342342
this.supportedSecondFactors = deepSnakeToCamel(data.supported_second_factors) as SignInSecondFactor[] | null;
343343
this.firstFactorVerification = new Verification(data.first_factor_verification);
344344
this.secondFactorVerification = new Verification(data.second_factor_verification);

packages/clerk-js/src/ui/components/SignIn/AlternativeMethods.tsx

+17-16
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,23 @@ const AlternativeMethodsList = (props: AlternativeMethodListProps) => {
8383
enableWeb3Providers
8484
enableOAuthProviders
8585
/>
86-
{firstPartyFactors.map((factor, i) => (
87-
<ArrowBlockButton
88-
leftIcon={getButtonIcon(factor)}
89-
textLocalizationKey={getButtonLabel(factor)}
90-
elementDescriptor={descriptors.alternativeMethodsBlockButton}
91-
textElementDescriptor={descriptors.alternativeMethodsBlockButtonText}
92-
arrowElementDescriptor={descriptors.alternativeMethodsBlockButtonArrow}
93-
key={i}
94-
textVariant='buttonLarge'
95-
isDisabled={card.isLoading}
96-
onClick={() => {
97-
card.setError(undefined);
98-
onFactorSelected(factor);
99-
}}
100-
/>
101-
))}
86+
{firstPartyFactors &&
87+
firstPartyFactors.map((factor, i) => (
88+
<ArrowBlockButton
89+
leftIcon={getButtonIcon(factor)}
90+
textLocalizationKey={getButtonLabel(factor)}
91+
elementDescriptor={descriptors.alternativeMethodsBlockButton}
92+
textElementDescriptor={descriptors.alternativeMethodsBlockButtonText}
93+
arrowElementDescriptor={descriptors.alternativeMethodsBlockButtonArrow}
94+
key={i}
95+
textVariant='buttonLarge'
96+
isDisabled={card.isLoading}
97+
onClick={() => {
98+
card.setError(undefined);
99+
onFactorSelected(factor);
100+
}}
101+
/>
102+
))}
102103
</Flex>
103104
)}
104105
{onBackLinkClick && (

packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export function _SignInStart(): JSX.Element {
263263
* For SAML enabled instances, perform sign in with password only when it is allowed for the identified user.
264264
*/
265265
const passwordField = fields.find(f => f.name === 'password')?.value;
266-
if (!passwordField || signInResource.supportedFirstFactors.some(ff => ff.strategy === 'saml')) {
266+
if (!passwordField || signInResource.supportedFirstFactors?.some(ff => ff.strategy === 'saml')) {
267267
return signInResource;
268268
}
269269
return signInResource.attemptFirstFactor({ strategy: 'password', password: passwordField });
@@ -277,7 +277,7 @@ export function _SignInStart(): JSX.Element {
277277
switch (res.status) {
278278
case 'needs_identifier':
279279
// Check if we need to initiate a saml flow
280-
if (res.supportedFirstFactors.some(ff => ff.strategy === 'saml')) {
280+
if (res.supportedFirstFactors?.some(ff => ff.strategy === 'saml')) {
281281
await authenticateWithSaml();
282282
}
283283
break;

packages/clerk-js/src/ui/components/SignIn/useResetPasswordFactor.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { isResetPasswordStrategy } from './utils';
66
export function useResetPasswordFactor() {
77
const signIn = useCoreSignIn();
88

9-
return signIn.supportedFirstFactors.find(({ strategy }) => isResetPasswordStrategy(strategy)) as
9+
return signIn.supportedFirstFactors?.find(({ strategy }) => isResetPasswordStrategy(strategy)) as
1010
| ResetPasswordCodeFactor
1111
| undefined;
1212
}

packages/clerk-js/src/ui/components/SignIn/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function determineStrategyWhenOTPIsPreferred(factors: SignInFactor[], identifier
103103
// The algorithm can be found at
104104
// https://www.notion.so/clerkdev/Implement-sign-in-alt-methods-e6e60ffb644645b3a0553b50556468ce
105105
export function determineStartingSignInFactor(
106-
firstFactors: SignInFactor[],
106+
firstFactors: SignInFactor[] | null,
107107
identifier: string | null,
108108
preferredSignInStrategy: PreferredSignInStrategy,
109109
): SignInFactor | null | undefined {

packages/clerk-js/src/ui/hooks/useAlternativeStrategies.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ export function useAlternativeStrategies({ filterOutFactor }: { filterOutFactor:
1111

1212
const { strategies: OAuthStrategies } = useEnabledThirdPartyProviders();
1313

14-
const firstFactors = supportedFirstFactors.filter(
14+
const firstFactors = supportedFirstFactors?.filter(
1515
f => f.strategy !== filterOutFactor?.strategy && !isResetPasswordStrategy(f.strategy),
1616
);
1717

18-
const shouldAllowForAlternativeStrategies = firstFactors.length + OAuthStrategies.length > 0;
18+
const shouldAllowForAlternativeStrategies = firstFactors && firstFactors.length + OAuthStrategies.length > 0;
1919

2020
const firstPartyFactors = supportedFirstFactors
21-
.filter(f => !f.strategy.startsWith('oauth_') && !(f.strategy === filterOutFactor?.strategy))
21+
?.filter(f => !f.strategy.startsWith('oauth_') && !(f.strategy === filterOutFactor?.strategy))
2222
.filter(factor => factorHasLocalStrategy(factor))
2323
// Only include passkey if the device supports it.
2424
// @ts-ignore Types are not public yet.

packages/elements/src/internals/machines/sign-in/utils/starting-factors.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const findFactorForIdentifier = (i: string | null) => (f: SignInFactor) => {
5656
// The algorithm can be found at
5757
// https://www.notion.so/clerkdev/Implement-sign-in-alt-methods-e6e60ffb644645b3a0553b50556468ce
5858
export function determineStartingSignInFactor(
59-
firstFactors: SignInFirstFactor[],
59+
firstFactors: SignInFirstFactor[] | null,
6060
identifier: string | null,
6161
preferredSignInStrategy?: PreferredSignInStrategy,
6262
) {

packages/elements/src/internals/machines/sign-in/verification.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ const SignInVerificationMachine = setup({
9696
// Only show these warnings in development!
9797
if (process.env.NODE_ENV === 'development') {
9898
if (
99+
clerk.client.signIn.supportedFirstFactors &&
99100
!clerk.client.signIn.supportedFirstFactors.every(factor => context.registeredStrategies.has(factor.strategy))
100101
) {
101102
console.warn(
@@ -121,7 +122,7 @@ const SignInVerificationMachine = setup({
121122
}
122123

123124
const strategiesUsedButNotActivated = Array.from(context.registeredStrategies).filter(
124-
strategy => !clerk.client.signIn.supportedFirstFactors.some(supported => supported.strategy === strategy),
125+
strategy => !clerk.client.signIn.supportedFirstFactors?.some(supported => supported.strategy === strategy),
125126
);
126127

127128
if (strategiesUsedButNotActivated.length > 0) {

packages/types/src/signIn.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface SignInResource extends ClerkResource {
7272
* @deprecated This attribute will be removed in the next major version
7373
*/
7474
supportedIdentifiers: SignInIdentifier[];
75-
supportedFirstFactors: SignInFirstFactor[];
75+
supportedFirstFactors: SignInFirstFactor[] | null;
7676
supportedSecondFactors: SignInSecondFactor[] | null;
7777
firstFactorVerification: VerificationResource;
7878
secondFactorVerification: VerificationResource;

packages/ui/src/hooks/use-reset-password-factor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const isResetPasswordStrategy = (strategy: SignInStrategy | string | null
88
export function useResetPasswordFactor() {
99
const clerk = useClerk();
1010

11-
return clerk.client.signIn.supportedFirstFactors.find(({ strategy }) => isResetPasswordStrategy(strategy)) as
11+
return clerk.client.signIn.supportedFirstFactors?.find(({ strategy }) => isResetPasswordStrategy(strategy)) as
1212
| ResetPasswordCodeFactor
1313
| undefined;
1414
}

0 commit comments

Comments
 (0)