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 uses the OAuth connections custom flow guide as a base, but you can apply this logic to any authentication custom flow.
'use client'
import * as React from 'react'
import { OAuthStrategy } from '@clerk/shared/types'
import { useSignIn, useClerk } from '@clerk/nextjs'
export default function Page() {
const { signIn } = useSignIn()
const { client } = useClerk()
const lastStrategy = client?.lastAuthenticationStrategy
const signInWith = (strategy: OAuthStrategy) => {
return signIn.sso({
strategy,
redirectCallbackUrl: '/sign-in/sso-callback',
redirectUrl: '/',
})
}
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 * as React from 'react'
import * as AuthSession from 'expo-auth-session'
import * as WebBrowser from 'expo-web-browser'
import { useClerk, useSSO } from '@clerk/expo'
import { ThemedText } from '@/components/themed-text'
import { ThemedView } from '@/components/themed-view'
import { type Href, useRouter } from 'expo-router'
import { Platform, Pressable, StyleSheet, View } from 'react-native'
import { OAuthStrategy } from '@clerk/types'
// Preloads the browser for Android devices to reduce authentication load time
// See: https://docs.expo.dev/guides/authentication/#improving-user-experience
export const useWarmUpBrowser = () => {
React.useEffect(() => {
if (Platform.OS !== 'android') return
void WebBrowser.warmUpAsync()
return () => {
void WebBrowser.coolDownAsync()
}
}, [])
}
WebBrowser.maybeCompleteAuthSession()
export default function Page() {
useWarmUpBrowser()
const { startSSOFlow } = useSSO()
const router = useRouter()
const [submittingStrategy, setSubmittingStrategy] = React.useState<OAuthStrategy | null>(null)
// Use the useClerk hook to access the clerk object
const { client } = useClerk()
// Use the clerk object to access the last authentication strategy
const lastStrategy = client?.lastAuthenticationStrategy
const onPress = async (oauthStrategy: OAuthStrategy) => {
setSubmittingStrategy(oauthStrategy)
try {
const { createdSessionId, setActive } = await startSSOFlow({
strategy: oauthStrategy,
redirectUrl: AuthSession.makeRedirectUri({
scheme: 'clerkexpoquickstart',
path: '/continue',
}),
})
// If the session was created, set it as the active session
if (createdSessionId) {
setActive!({
session: createdSessionId,
navigate: async ({ session, decorateUrl }) => {
// Handle session tasks
// See https://clerk.com/docs/guides/development/custom-flows/authentication/session-tasks
if (session?.currentTask) {
console.log(session?.currentTask)
return
}
// If no session tasks, navigate the signed-in user to the home page
const url = decorateUrl('/')
if (url.startsWith('http')) {
window.location.href = url
} else {
router.push(url as Href)
}
},
})
} else {
// If the session was not created, navigate to the continue page to collect missing information
router.push('/continue')
}
} catch (err) {
console.error(JSON.stringify(err, null, 2))
} finally {
setSubmittingStrategy(null)
}
}
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.subtitle}>Choose a provider to continue</ThemedText>
<View style={styles.buttonContainer}>
{providers.map((provider) => {
const strategy = provider.strategy as OAuthStrategy
const isThisProviderLoading = submittingStrategy === strategy
const isAnyLoading = submittingStrategy !== null
return (
<Pressable
key={provider.strategy}
style={({ pressed }) => [
styles.button,
isAnyLoading && !isThisProviderLoading && styles.buttonDisabled,
pressed && styles.buttonPressed,
]}
onPress={() => onPress(strategy)}
disabled={isAnyLoading}
>
<ThemedText style={styles.buttonText}>
{isThisProviderLoading
? 'Opening…'
: `Sign in with ${provider.name} ${lastStrategy === provider.strategy ? '(Last used)' : ''}`}
</ThemedText>
</Pressable>
)
})}
</View>
</ThemedView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
paddingVertical: 24,
},
title: {
marginBottom: 4,
},
subtitle: {
fontSize: 14,
marginBottom: 8,
opacity: 0.85,
},
buttonContainer: {
gap: 12,
},
button: {
backgroundColor: '#0a7ea4',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
alignSelf: 'stretch',
},
buttonPressed: {
opacity: 0.7,
},
buttonDisabled: {
opacity: 0.5,
},
buttonText: {
color: '#fff',
fontWeight: '600',
},
linkContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 4,
marginTop: 12,
alignItems: 'center',
},
})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)
}
})
})
}import SwiftUI
import ClerkKit
struct LastAuthenticationStrategyView: View {
@Environment(Clerk.self) private var clerk
var providers: [OAuthProvider] {
clerk.environment?.allSocialProviders ?? []
}
var lastStrategy: String? {
clerk.client?.lastAuthenticationStrategy?.rawValue
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
ForEach(providers) { provider in
Text(provider.name)
.frame(maxWidth: .infinity, alignment: .leading)
.overlay(alignment: .trailing) {
if provider.strategy == lastStrategy {
Text("Last used")
.font(.caption)
.padding(.horizontal, 8)
.padding(.vertical, 2)
.background(Color.gray.opacity(0.2))
.clipShape(Capsule())
}
}
}
}
}
}import com.clerk.api.Clerk
data class ProviderOption(val strategy: String, val name: String)
val providers =
listOf(
ProviderOption("oauth_google", "Google"),
ProviderOption("oauth_github", "GitHub"),
)
val lastStrategy = Clerk.client.lastAuthenticationStrategy
providers.forEach { provider ->
val label =
if (lastStrategy == provider.strategy) {
"Sign in with ${provider.name} (Last used)"
} else {
"Sign in with ${provider.name}"
}
println(label)
}Feedback
Last updated on