Overview
The CallbackPage component handles the authentication callback after users are redirected back from Logto. It processes the authentication code, exchanges it for tokens, and manages the post-authentication flow including popup and redirect scenarios.
Installation
npm install @ouim/logto-authkit
Basic Usage
Create a callback route in your application:
import { CallbackPage } from '@ouim/logto-authkit'
export default function Callback () {
return < CallbackPage />
}
Props
Additional CSS classes to apply to the container element. < CallbackPage className = "bg-gray-50" />
Custom component to display while processing authentication. < CallbackPage
loadingComponent = {
< div className = "flex items-center gap-2" >
< Spinner />
< p > Please wait... </ p >
</ div >
}
/>
Custom component to display after successful authentication. < CallbackPage
successComponent = {
< div className = "text-center" >
< CheckCircle className = "text-green-500" />
< p > Success! Taking you to your dashboard... </ p >
</ div >
}
/>
Callback function executed after successful authentication, before redirect/close. < CallbackPage
onSuccess = { () => {
console . log ( 'Authentication successful!' )
// Track analytics, etc.
} }
/>
Callback function executed if authentication fails. < CallbackPage
onError = { error => {
console . error ( 'Auth failed:' , error )
// Show error message, redirect to error page, etc.
} }
/>
How It Works
The CallbackPage component:
Receives the auth code from Logto redirect URL
Exchanges code for tokens using useHandleSignInCallback() from @logto/react
Detects the flow type (popup vs. redirect)
Handles completion :
Popup flow : Sends message to parent window and closes
Redirect flow : Redirects to home page (/)
Flow Detection
The component automatically detects whether it’s handling a popup or redirect flow:
const isPopup = ( window . opener && window . opener !== window ) || sessionStorage . getItem ( 'simple_logto_popup_flow' ) === 'true'
Checks if the window has an opener (parent window)
Falls back to sessionStorage flag for cross-origin scenarios
For popup-based authentication:
Component detects it’s in a popup
Processes authentication
Sends SIGNIN_SUCCESS message to parent window
Closes the popup
// Sends message to parent
window . opener . postMessage ({ type: 'SIGNIN_SUCCESS' }, window . location . origin )
// Closes popup after small delay
setTimeout (() => {
window . close ()
}, 100 )
Fallback Mechanism
If window.opener is unavailable (some browsers clear it), falls back to localStorage:
localStorage . setItem ( 'simple_logto_signin_complete' , Date . now (). toString ())
The parent window listens for both postMessage and localStorage events to handle popup completion.
Redirect Flow
For full-page redirect authentication:
Component detects it’s NOT in a popup
Processes authentication
Redirects to home page (/)
if ( ! isPopup ) {
window . location . href = '/'
}
Examples
Basic Setup (Next.js App Router)
import { CallbackPage } from '@ouim/logto-authkit'
export default function Callback () {
return < CallbackPage />
}
With Custom Loading State
import { CallbackPage } from '@ouim/logto-authkit'
import { Loader2 } from 'lucide-react'
export default function Callback () {
return (
< CallbackPage
loadingComponent = {
< div className = "flex flex-col items-center gap-4" >
< Loader2 className = "h-8 w-8 animate-spin text-blue-500" />
< p className = "text-lg font-medium" > Signing you in... </ p >
< p className = "text-sm text-gray-500" > Please wait a moment </ p >
</ div >
}
/>
)
}
With Success Tracking
import { CallbackPage } from '@ouim/logto-authkit'
import { useRouter } from 'next/navigation'
export default function Callback () {
const router = useRouter ()
return (
< CallbackPage
onSuccess = { () => {
// Track successful authentication
if ( typeof window !== 'undefined' && window . analytics ) {
window . analytics . track ( 'User Signed In' , {
method: 'logto' ,
timestamp: new Date (). toISOString (),
})
}
} }
onError = { error => {
console . error ( 'Authentication error:' , error )
// Redirect to error page
router . push ( '/auth/error' )
} }
/>
)
}
Custom Styling
import { CallbackPage } from '@ouim/logto-authkit'
export default function Callback () {
return (
< div className = "min-h-screen bg-gradient-to-br from-blue-500 to-purple-600" >
< CallbackPage
className = "flex items-center justify-center min-h-screen"
loadingComponent = {
< div className = "bg-white rounded-lg shadow-xl p-8" >
< div className = "flex items-center gap-3" >
< div className = "animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500" />
< p className = "text-gray-700" > Authenticating... </ p >
</ div >
</ div >
}
/>
</ div >
)
}
With Custom Redirect
import { CallbackPage } from '@ouim/logto-authkit'
export default function Callback () {
return (
< CallbackPage
onSuccess = { () => {
// Check if this is popup flow
const isPopup = ( window . opener && window . opener !== window ) || sessionStorage . getItem ( 'simple_logto_popup_flow' ) === 'true'
if ( ! isPopup ) {
// Custom redirect for non-popup flow
window . location . href = '/dashboard'
}
} }
/>
)
}
Default UI
If no custom components are provided, the callback page displays:
Loading State
< div className = "flex items-center gap-2" >
< Spinner /> { /* Animated spinner */ }
< div className = "text-lg text-slate-500" > Signing you in... </ div >
</ div >
Success State
< div className = "flex items-center gap-2" >
< Spinner />
< div className = "text-lg text-slate-500" > Authentication complete! Redirecting... </ div >
</ div >
Session Storage Flag
For popup flows, your sign-in page should set a flag:
'use client'
import { useEffect } from 'react'
import { useAuth } from '@ouim/logto-authkit'
import { useSearchParams } from 'next/navigation'
export default function SignIn () {
const { signIn } = useAuth ()
const searchParams = useSearchParams ()
const isPopup = searchParams . get ( 'popup' ) === 'true'
useEffect (() => {
if ( isPopup ) {
sessionStorage . setItem ( 'simple_logto_popup_flow' , 'true' )
}
signIn ( '/callback' )
}, [ signIn , isPopup ])
return < div > Redirecting to sign in... </ div >
}
Error Handling
The component handles errors during authentication:
try {
// Process authentication
} catch ( error ) {
console . error ( 'Authentication callback error:' , error )
if ( onError ) {
onError ( error as Error )
}
}
Common errors:
Invalid authorization code
Token exchange failure
Network errors
CORS issues
Best Practices
Always use a dedicated route like /callback or /auth/callback for handling authentication callbacks.
Configure Logto redirect URI
Make sure your Logto application’s redirect URI matches your callback route exactly.
The component automatically handles both popup and redirect flows - no additional configuration needed.
Use the onError callback to track authentication failures in your monitoring system.
Customize user experience
Provide custom loading and success components that match your application’s design.
Configuration in Logto
In your Logto application settings, add your callback URL:
https://yourdomain.com/callback
For local development:
http://localhost:3000/callback
Troubleshooting
Popup doesn’t close : Ensure your sign-in page sets the simple_logto_popup_flow sessionStorage flag when ?popup=true is in the URL.
Infinite redirect loop : Check that your Logto redirect URI exactly matches your callback route.
CORS errors : Ensure your Logto application’s allowed origins include your application’s domain.
The component automatically cleans up the simple_logto_popup_flow flag after successful authentication.
AuthProvider Configure authentication provider
useAuth Hook Access sign-in functionality
UserCenter User menu with sign-in button
Route Protection Protect authenticated routes