Skip to content
Draft
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/fix-clear-jwt-on-signout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/expo': patch
---

Clear JWT from SecureStore when signing out via native components (AuthView, UserButton) to prevent "session_exists" / "already signed in" errors on subsequent sign-in attempts.
5 changes: 5 additions & 0 deletions packages/expo/src/native/UserButton.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useClerk, useUser } from '@clerk/react';
import * as SecureStore from 'expo-secure-store';
import { useEffect, useRef, useState } from 'react';
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

import { CLERK_CLIENT_JWT_KEY } from '../constants';
import { ClerkExpoModule as ClerkExpo, isNativeSupported } from '../utils/native-module';

// Raw result from native module (may vary by platform)
Expand Down Expand Up @@ -155,6 +157,9 @@ export function UserButton(_props: UserButtonProps) {
}
}

// Clear stale JWT from SecureStore to prevent "already signed in" errors
await SecureStore.deleteItemAsync(CLERK_CLIENT_JWT_KEY).catch(() => {});

// Sign out from JS SDK to update isSignedIn state
if (clerk?.signOut) {
try {
Expand Down
4 changes: 4 additions & 0 deletions packages/expo/src/provider/ClerkProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '../polyfills';

Check failure on line 1 in packages/expo/src/provider/ClerkProvider.tsx

View workflow job for this annotation

GitHub Actions / Static analysis

Run autofix to sort these imports!

import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';
import { InternalClerkProvider as ClerkReactProvider, type Ui } from '@clerk/react/internal';
Expand All @@ -10,6 +10,7 @@
import { useNativeAuthEvents } from '../hooks/useNativeAuthEvents';
import NativeClerkModule from '../specs/NativeClerkModule';
import { tokenCache as defaultTokenCache } from '../token-cache';
import * as SecureStore from 'expo-secure-store';
import { isNative, isWeb } from '../utils/runtime';
import { getClerkInstance } from './singleton';
import type { BuildClerkOptions } from './singleton/types';
Expand Down Expand Up @@ -254,6 +255,9 @@
if (!isMountedRef.current) {
return;
}
// Clear the JWT from SecureStore before signing out to prevent
// "session_exists" / "already signed in" errors on the next sign-in attempt.
await SecureStore.deleteItemAsync(CLERK_CLIENT_JWT_KEY).catch(() => {});
await clerkInstance.signOut();
Comment on lines +258 to 261
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add regression coverage for JWT cleanup sign-out flows before merge.

Line 258 changes auth-state behavior, and the PR also changes packages/expo/src/native/UserButton.tsx (Line 160), but no tests were added/updated. Please add regression tests that cover: sign-in → sign-out via both native flows → sign-in again, and assert no "session_exists" / "already signed in" failure plus token cleanup.

As per coding guidelines: "If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/expo/src/provider/ClerkProvider.tsx` around lines 258 - 261, Add
regression tests that exercise sign-in → sign-out → sign-in flows to ensure the
JWT cleanup prevents "session_exists"/"already signed in" errors: create tests
for both sign-out paths (calling clerkInstance.signOut and the native UserButton
sign-out flow from packages/expo/src/native/UserButton.tsx), mock
SecureStore.deleteItemAsync and CLERK_CLIENT_JWT_KEY behavior, simulate an
initial sign-in storing a JWT, invoke the sign-out flow and verify
SecureStore.deleteItemAsync was called and the token removed, then simulate a
subsequent sign-in and assert no "session_exists"/"already signed in" error
occurs; use mocks for clerkInstance methods and SecureStore so tests assert
token cleanup and successful re-sign-in.

}
} catch (error) {
Expand Down
Loading