Build a custom flow for displaying the last authentication method
The Client object includes a lastAuthenticationStrategy property that tracks the last authentication method used by the user. This is useful for improving the user experience by showing a "Last used" badge on OAuth buttons, helping returning users quickly identify their preferred sign-in method.
Access the last authentication strategy
The lastAuthenticationStrategy property is available on the Client object. You can access it through the client property of the Clerk instance.
The following example demonstrates how to:
- Access the
clientobject using the useClerk() hook. - Check the
lastAuthenticationStrategyproperty to identify which OAuth provider was last used. - Display a badge next to the corresponding OAuth button.
This example is written for Next.js App Router but it can be adapted for any React-based framework, such as React Router or Tanstack React Start.
'use client'
import * as React from 'react'
import { OAuthStrategy } from '@clerk/types'
import { useSignIn, useClerk } from '@clerk/nextjs'
export default function Page() {
const { signIn } = useSignIn()
const { client } = useClerk()
if (!signIn) return null
const lastStrategy = client?.lastAuthenticationStrategy
const signInWith = (strategy: OAuthStrategy) => {
return signIn.authenticateWithRedirect({
strategy,
redirectUrl: '/sign-in/sso-callback',
redirectUrlComplete: '/',
})
}
const providers = [
{ strategy: 'oauth_google' as const, name: 'Google' },
{ strategy: 'oauth_github' as const, name: 'GitHub' },
]
return (
<div>
<h1>Sign in</h1>
{providers.map((provider) => (
<button key={provider.strategy} onClick={() => signInWith(provider.strategy)}>
Sign in with {provider.name}
{lastStrategy === provider.strategy && <span> (Last used)</span>}
</button>
))}
</div>
)
}import { ThemedText } from '@/components/themed-text'
import { ThemedView } from '@/components/themed-view'
import { useClerk, useSSO } from '@clerk/clerk-expo'
import { OAuthStrategy } from '@clerk/types'
import * as AuthSession from 'expo-auth-session'
import { useRouter } from 'expo-router'
import * as WebBrowser from 'expo-web-browser'
import { useCallback, useEffect } from 'react'
import { Platform, Pressable, StyleSheet } from 'react-native'
// Preloads the browser for Android devices to reduce authentication load time
// See: https://docs.expo.dev/guides/authentication/#improving-user-experience
export const useWarmUpBrowser = () => {
useEffect(() => {
if (Platform.OS !== 'android') return
void WebBrowser.warmUpAsync()
return () => {
// Cleanup: closes browser when component unmounts
void WebBrowser.coolDownAsync()
}
}, [])
}
// Handle any pending authentication sessions
WebBrowser.maybeCompleteAuthSession()
export default function Page() {
useWarmUpBrowser()
// Use the `useSSO()` hook to access the `startSSOFlow()` method
const { startSSOFlow } = useSSO()
const { client } = useClerk()
const router = useRouter()
const lastStrategy = client?.lastAuthenticationStrategy
const onPress = useCallback(async (oauthStrategy: OAuthStrategy) => {
try {
// Start the authentication process by calling `startSSOFlow()`
const { createdSessionId, setActive, signIn, signUp } = await startSSOFlow({
strategy: oauthStrategy,
// For web, defaults to current path
// For native, you must pass a scheme, like AuthSession.makeRedirectUri({ scheme, path })
// For more info, see https://docs.expo.dev/versions/latest/sdk/auth-session/#authsessionmakeredirecturioptions
redirectUrl: AuthSession.makeRedirectUri({
scheme: 'com.clerk.clerk-expo-quickstart',
path: '/sign-in',
}),
})
// If sign in was successful, set the active session
if (createdSessionId) {
setActive!({
session: createdSessionId,
// Check for session tasks and navigate to custom UI to help users resolve them
// See https://clerk.com/docs/guides/development/custom-flows/authentication/session-tasks
navigate: async ({ session }) => {
if (session?.currentTask) {
console.log(session?.currentTask)
return
}
router.push('/')
},
})
} else {
// If there is no `createdSessionId`,
// there are missing requirements, such as MFA
// See https://clerk.com/docs/guides/development/custom-flows/authentication/oauth-connections#handle-missing-requirements
router.push('/sign-in/continue')
}
} catch (err) {
// See https://clerk.com/docs/guides/development/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(err, null, 2))
}
}, [])
const providers = [
{ strategy: 'oauth_google', name: 'Sign in with Google' },
{ strategy: 'oauth_github', name: 'Sign in with GitHub' },
]
return (
<ThemedView style={styles.container}>
<ThemedText type="title" style={styles.title}>
Sign in
</ThemedText>
<ThemedText style={styles.description}>Choose a provider to continue</ThemedText>
{providers.map((provider) => (
<Pressable
key={provider.strategy}
style={({ pressed }) => [styles.button, pressed && styles.buttonPressed]}
onPress={() => onPress(provider.strategy as OAuthStrategy)}
>
<ThemedText style={styles.buttonText}>
{provider.name}
{lastStrategy === provider.strategy ? ' (Last used)' : ''}
</ThemedText>
</Pressable>
))}
</ThemedView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
gap: 12,
},
title: {
marginBottom: 8,
},
description: {
fontSize: 14,
marginBottom: 16,
opacity: 0.8,
},
button: {
backgroundColor: '#0a7ea4',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
marginTop: 8,
},
buttonPressed: {
opacity: 0.7,
},
buttonText: {
color: '#fff',
fontWeight: '600',
},
})The following example demonstrates how to:
- Access the
clientobject from theClerkinstance. - Check the
lastAuthenticationStrategyproperty to identify which OAuth provider was last used. - Display a badge next to the corresponding OAuth button.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js" async crossorigin="anonymous"></script>
</body>
</html>import { Clerk } from '@clerk/clerk-js'
const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(pubKey)
await clerk.load()
if (clerk.user) {
// User is already signed in
document.getElementById('app').innerHTML = `
<div id="user-button"></div>
`
clerk.mountUserButton(document.getElementById('user-button'))
} else {
const lastStrategy = clerk.client?.lastAuthenticationStrategy
const providers = [
{ strategy: 'oauth_google', name: 'Google' },
{ strategy: 'oauth_github', name: 'GitHub' },
]
const buttons = providers
.map(
(provider) => `
<button id="${provider.strategy}">
Sign in with ${provider.name}
${lastStrategy === provider.strategy ? '<span> (Last used)</span>' : ''}
</button>
`,
)
.join('')
document.getElementById('app').innerHTML = `
<h1>Sign in</h1>
${buttons}
`
providers.forEach((provider) => {
document.getElementById(provider.strategy)?.addEventListener('click', async () => {
try {
await clerk.client.signIn.authenticateWithRedirect({
strategy: provider.strategy,
redirectUrl: '/sso-callback',
redirectUrlComplete: '/',
})
} catch (error) {
console.error('Error:', error)
}
})
})
}Examples for this SDK aren't available yet. For now, try switching to a supported SDK, such as Next.js, and converting the code to fit your SDK.
Feedback
Last updated on