Documentation Index
Fetch the complete documentation index at: https://docs.ouim.me/llms.txt
Use this file to discover all available pages before exploring further.
This guide demonstrates various patterns for protecting routes and implementing access control in your application using logto-authkit.
Basic Route Protection
The simplest way to protect a route is using the middleware option in the useAuth hook:
import { useAuth } from '@ouim/logto-authkit'
export default function Dashboard() {
const { user, isLoadingUser } = useAuth({
middleware: 'auth',
redirectTo: '/signin',
})
if (isLoadingUser) {
return <div>Loading...</div>
}
return (
<div>
<h1>Protected Dashboard</h1>
<p>Welcome, {user?.name}!</p>
</div>
)
}
Advanced Protection Patterns
Guest-Only Routes
Custom Navigation
Conditional Protection
Redirect authenticated users away from sign-in pages:import { useAuth } from '@ouim/logto-authkit'
export default function Login() {
const { user, isLoadingUser } = useAuth({
middleware: 'guest',
redirectIfAuthenticated: '/dashboard',
})
if (isLoadingUser) {
return <div>Loading...</div>
}
return (
<div>
<h1>Sign In</h1>
<p>Please sign in to continue</p>
</div>
)
}
Use custom navigation options for more control:import { useAuth } from '@ouim/logto-authkit'
export default function Settings() {
const { user, isLoadingUser } = useAuth({
middleware: 'auth',
redirectTo: '/signin',
navigationOptions: {
replace: true, // Use replaceState instead of pushState
force: false, // Only navigate if not already on target page
},
})
if (isLoadingUser) {
return <div>Loading...</div>
}
return (
<div>
<h1>Settings</h1>
<p>User ID: {user?.id}</p>
</div>
)
}
Implement complex access control logic:import { useAuth } from '@ouim/logto-authkit'
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
export default function AdminPanel() {
const { user, isLoadingUser } = useAuth({
middleware: 'auth',
redirectTo: '/signin',
})
const navigate = useNavigate()
useEffect(() => {
// Custom role-based access control
if (!isLoadingUser && user) {
const isAdmin = user.roles?.includes('admin')
if (!isAdmin) {
navigate('/unauthorized')
}
}
}, [user, isLoadingUser, navigate])
if (isLoadingUser) {
return <div>Loading...</div>
}
return (
<div>
<h1>Admin Panel</h1>
<p>Welcome, Admin {user?.name}</p>
</div>
)
}
Route Guard Component
Create a reusable route guard component for cleaner code:
Create Route Guard Component
components/RouteGuard.tsx
import { useAuth } from '@ouim/logto-authkit'
import { ReactNode } from 'react'
interface RouteGuardProps {
children: ReactNode
fallback?: ReactNode
requireAuth?: boolean
redirectTo?: string
}
export function RouteGuard({
children,
fallback = <div>Loading...</div>,
requireAuth = true,
redirectTo = '/signin',
}: RouteGuardProps) {
const { user, isLoadingUser } = useAuth({
middleware: requireAuth ? 'auth' : undefined,
redirectTo: requireAuth ? redirectTo : undefined,
})
if (isLoadingUser) {
return <>{fallback}</>
}
if (requireAuth && !user) {
return null // Will redirect
}
return <>{children}</>
}
Use Route Guard
import { RouteGuard } from '../components/RouteGuard'
import { useAuth } from '@ouim/logto-authkit'
export default function Profile() {
const { user } = useAuth()
return (
<RouteGuard>
<div>
<h1>User Profile</h1>
<p>Name: {user?.name}</p>
<p>Email: {user?.email}</p>
</div>
</RouteGuard>
)
}
React Router Integration
Protect routes at the router level:
import { AuthProvider, useAuth } from '@ouim/logto-authkit'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { ReactNode } from 'react'
// Protected route wrapper
function ProtectedRoute({ children }: { children: ReactNode }) {
const { user, isLoadingUser } = useAuth()
if (isLoadingUser) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
)
}
if (!user) {
return <Navigate to="/signin" replace />
}
return <>{children}</>
}
function App() {
return (
<AuthProvider
config={{
endpoint: 'https://your-tenant.logto.app',
appId: 'your-app-id',
}}
>
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/signin" element={<SignIn />} />
<Route path="/callback" element={<Callback />} />
{/* Protected routes */}
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="/profile"
element={
<ProtectedRoute>
<Profile />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
</AuthProvider>
)
}
Next.js Route Protection
For Next.js applications, use client-side protection:
'use client'
import { useAuth } from '@ouim/logto-authkit'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
const { user, isLoadingUser } = useAuth()
const router = useRouter()
useEffect(() => {
if (!isLoadingUser && !user) {
router.push('/signin')
}
}, [user, isLoadingUser, router])
if (isLoadingUser) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
)
}
if (!user) {
return null
}
return <>{children}</>
}
import { useAuth } from '@ouim/logto-authkit'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
export default function Dashboard() {
const { user, isLoadingUser } = useAuth()
const router = useRouter()
useEffect(() => {
if (!isLoadingUser && !user) {
router.push('/signin')
}
}, [user, isLoadingUser, router])
if (isLoadingUser) {
return <div>Loading...</div>
}
if (!user) {
return null
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {user.name}!</p>
</div>
)
}
Role-Based Access Control
Implement role-based access control (RBAC):
import { useAuth } from '@ouim/logto-authkit'
import { useMemo } from 'react'
type Role = 'admin' | 'user' | 'moderator'
export function useRBAC() {
const { user } = useAuth()
const hasRole = useMemo(() => {
return (role: Role) => {
return user?.roles?.includes(role) ?? false
}
}, [user])
const hasAnyRole = useMemo(() => {
return (roles: Role[]) => {
return roles.some(role => user?.roles?.includes(role))
}
}, [user])
const hasAllRoles = useMemo(() => {
return (roles: Role[]) => {
return roles.every(role => user?.roles?.includes(role))
}
}, [user])
return {
hasRole,
hasAnyRole,
hasAllRoles,
userRoles: user?.roles || [],
}
}
Use the RBAC hook in your components:
import { useAuth } from '@ouim/logto-authkit'
import { useRBAC } from '../hooks/useRBAC'
import { useNavigate } from 'react-router-dom'
import { useEffect } from 'react'
export default function AdminDashboard() {
const { user, isLoadingUser } = useAuth({
middleware: 'auth',
redirectTo: '/signin',
})
const { hasRole } = useRBAC()
const navigate = useNavigate()
useEffect(() => {
if (!isLoadingUser && user && !hasRole('admin')) {
navigate('/unauthorized')
}
}, [user, isLoadingUser, hasRole, navigate])
if (isLoadingUser) {
return <div>Loading...</div>
}
if (!hasRole('admin')) {
return null
}
return (
<div>
<h1>Admin Dashboard</h1>
<p>Admin-only content</p>
</div>
)
}
Loading States
Handle loading states elegantly:
components/ProtectedPage.tsx
import { useAuth } from '@ouim/logto-authkit'
import { ReactNode } from 'react'
interface ProtectedPageProps {
children: ReactNode
loadingComponent?: ReactNode
}
export function ProtectedPage({
children,
loadingComponent,
}: ProtectedPageProps) {
const { user, isLoadingUser } = useAuth({
middleware: 'auth',
redirectTo: '/signin',
})
if (isLoadingUser) {
return (
<>
{loadingComponent || (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-16 w-16 border-b-2 border-blue-600"></div>
</div>
)}
</>
)
}
if (!user) {
return null // Will redirect
}
return <>{children}</>
}
Best Practices
Always check loading state
Before checking if a user exists, always verify isLoadingUser is false to avoid race conditions:const { user, isLoadingUser } = useAuth()
if (isLoadingUser) {
return <LoadingSpinner />
}
if (!user) {
// Safe to redirect or show login
}
Use middleware option for automatic redirects
The middleware option in useAuth provides automatic redirects:useAuth({
middleware: 'auth', // Require authentication
redirectTo: '/signin', // Where to redirect if not authenticated
})
Implement route guards at the router level
For cleaner code and better DX, implement protection at the router level rather than in each component.
Provide clear feedback during loading
Always show loading indicators while authentication state is being determined:if (isLoadingUser) {
return <Skeleton />
}
Next Steps
React SPA Example
See a complete React application
Next.js Example
Explore Next.js integration
useAuth Hook
Learn more about the useAuth hook
Server auth
Implement server-side protection