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
5 changes: 5 additions & 0 deletions .changeset/expo-apple-verification-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/expo': patch
---

`useSignInWithApple` now throws a `ClerkAPIResponseError` when Sign in with Apple on an instance with restricted sign-ups needs the user to verify their email address first. Previously it returned with no session and no error.
46 changes: 45 additions & 1 deletion packages/expo/src/hooks/__tests__/useSignInWithApple.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClerkAPIResponseError } from '@clerk/shared/error';
import { ClerkAPIError, ClerkAPIResponseError, isClerkAPIResponseError } from '@clerk/shared/error';
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';

Expand Down Expand Up @@ -271,6 +271,50 @@ describe('useSignInWithApple', () => {
await expect(result.current.startAppleAuthenticationFlow()).rejects.toThrow('Sign-ups restricted');
});

test('should throw the first factor error when the fallback sign-in needs email verification', async () => {
mocks.signInAsync.mockResolvedValue({
identityToken: 'mock-identity-token',
fullName: null,
});

mockSignUp.create.mockRejectedValue(
new ClerkAPIResponseError('Sign-ups restricted', {
data: [{ code: 'sign_up_mode_restricted', message: 'Sign-ups restricted' }],
status: 403,
}),
);

const mockSignInNeedsVerification = {
...mockSignIn,
createdSessionId: null,
firstFactorVerification: {
status: 'unverified',
error: new ClerkAPIError({
code: 'external_account_email_address_verification_required',
message: 'Email address verification required',
long_message:
'Your associated email address is required to be verified, because it was initially created as unverified.',
}),
},
};
mocks.useSignIn.mockReturnValue({
signIn: mockSignInNeedsVerification,
setActive: mockSetActive,
isLoaded: true,
});

const { result } = renderHook(() => useSignInWithApple());

const error = await result.current.startAppleAuthenticationFlow().catch((e: unknown) => e);
expect(isClerkAPIResponseError(error)).toBe(true);
expect((error as ClerkAPIResponseError).errors[0].code).toBe(
'external_account_email_address_verification_required',
);
expect((error as ClerkAPIResponseError).message).toBe(
'Your associated email address is required to be verified, because it was initially created as unverified.',
);
});

test('should rethrow sign-up errors that are not sign-up restrictions', async () => {
mocks.signInAsync.mockResolvedValue({
identityToken: 'mock-identity-token',
Expand Down
16 changes: 15 additions & 1 deletion packages/expo/src/hooks/useSignInWithApple.ios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useSignIn, useSignUp } from '@clerk/react/legacy';
import { isClerkAPIResponseError } from '@clerk/shared/error';
import { ClerkAPIResponseError, isClerkAPIResponseError } from '@clerk/shared/error';
import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';

import { errorThrower } from '../utils/errors';
Expand Down Expand Up @@ -134,6 +134,20 @@ export function useSignInWithApple() {
throw signUpError;
}

const verificationError = signIn.firstFactorVerification.error;
if (verificationError) {
throw new ClerkAPIResponseError(verificationError.longMessage || verificationError.message, {
data: [
{
code: verificationError.code,
message: verificationError.message,
long_message: verificationError.longMessage,
},
],
status: 422,
});
}

return {
createdSessionId: signIn.createdSessionId,
setActive,
Expand Down
Loading