router-query/repository-context/docs/router/how-to/setup-auth-providers.md
Version 663282b0ebbc.bb1 · MIT. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.
← Return to resource and package checksum
title: How to Set Up Authentication Providers
This guide covers integrating popular authentication services (Auth0, Clerk, Supabase) with TanStack Router.
Quick Start
Choose an authentication provider, install their SDK, wrap your router with their context, and adapt their auth state to work with TanStack Router's context system.
Auth0 Integration
1. Install Auth0
npm install @auth0/auth0-react
2. Set Up Environment Variables
Add to your .env file:
VITE_AUTH0_DOMAIN=your-auth0-domain.auth0.com
VITE_AUTH0_CLIENT_ID=your_auth0_client_id
3. Create Auth0 Wrapper
Create src/auth/auth0.tsx:
import { Auth0Provider, useAuth0 } from '@auth0/auth0-react'
import { createContext, useContext } from 'react'
interface Auth0ContextType {
isAuthenticated: boolean
user: any
login: () => void
logout: () => void
isLoading: boolean
}
const Auth0Context = createContext<Auth0ContextType | undefined>(undefined)
export function Auth0Wrapper({ children }: { children: React.ReactNode }) {
return (
<Auth0Provider
domain={import.meta.env.VITE_AUTH0_DOMAIN}
clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
authorizationParams={{
redirect_uri: window.location.origin,
}}
>
<Auth0ContextProvider>{children}</Auth0ContextProvider>
</Auth0Provider>
)
}
function Auth0ContextProvider({ children }: { children: React.ReactNode }) {
const { isAuthenticated, user, loginWithRedirect, logout, isLoading } =
useAuth0()
const contextValue = {
isAuthenticated,
user,
login: loginWithRedirect,
logout: () =>
logout({ logoutParams: { returnTo: window.location.origin } }),
isLoading,
}
return (
<Auth0Context.Provider value={contextValue}>
{children}
</Auth0Context.Provider>
)
}
export function useAuth0Context() {
const context = useContext(Auth0Context)
if (context === undefined) {
throw new Error('useAuth0Context must be used within Auth0Wrapper')
}
return context
}
4. Update App Configuration
Update src/App.tsx:
import { RouterProvider } from '@tanstack/react-router'
import { Auth0Wrapper, useAuth0Context } from './auth/auth0'
import { router } from './router'
function InnerApp() {
const auth = useAuth0Context()
if (auth.isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
Loading...
</div>
)
}
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<Auth0Wrapper>
<InnerApp />
</Auth0Wrapper>
)
}
export default App
5. Create Protected Routes
Create src/routes/_authenticated.tsx:
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {
// Auth0 handles login redirects, so just trigger login
context.auth.login()
return
}
},
component: () => <Outlet />,
})
Clerk Integration
1. Install Clerk
npm install @clerk/clerk-react
2. Set Up Environment Variables
Add to your .env file:
VITE_CLERK_PUBLISHABLE_KEY=pk_test_your_clerk_key
3. Create Clerk Wrapper
Create src/auth/clerk.tsx:
import { ClerkProvider, useUser, useAuth } from '@clerk/clerk-react'
export function ClerkWrapper({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}>
{children}
</ClerkProvider>
)
}
export function useClerkAuth() {
const { isSignedIn, isLoaded } = useAuth()
const { user } = useUser()
return {
isAuthenticated: isSignedIn,
user: user
? {
id: user.id,
username:
user.username || user.primaryEmailAddress?.emailAddress || '',
email: user.primaryEmailAddress?.emailAddress || '',
}
: null,
isLoading: !isLoaded,
login: () => {
// Clerk handles login through components
window.location.href = '/sign-in'
},
logout: () => {
// Clerk handles logout through components
window.location.href = '/sign-out'
},
}
}
4. Create Clerk Auth Routes
Create src/routes/sign-in.tsx:
import { createFileRoute } from '@tanstack/react-router'
import { SignIn } from '@clerk/clerk-react'
export const Route = createFileRoute('/sign-in')({
component: () => (
<div className="flex items-center justify-center min-h-screen">
<SignIn redirectUrl="/dashboard" signUpUrl="/sign-up" />
</div>
),
})
Create src/routes/sign-up.tsx:
import { createFileRoute } from '@tanstack/react-router'
import { SignUp } from '@clerk/clerk-react'
export const Route = createFileRoute('/sign-up')({
component: () => (
<div className="flex items-center justify-center min-h-screen">
<SignUp redirectUrl="/dashboard" signInUrl="/sign-in" />
</div>
),
})
5. Update App Configuration
Update src/App.tsx:
import { RouterProvider } from '@tanstack/react-router'
import { ClerkWrapper, useClerkAuth } from './auth/clerk'
import { router } from './router'
function InnerApp() {
const auth = useClerkAuth()
if (auth.isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
Loading...
</div>
)
}
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<ClerkWrapper>
<InnerApp />
</ClerkWrapper>
)
}
export default App
6. Create Protected Routes
Create src/routes/_authenticated.tsx:
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/sign-in',
search: {
redirect: location.href,
},
})
}
},
component: () => <Outlet />,
})
Supabase Integration
1. Install Supabase
npm install @supabase/supabase-js
2. Set Up Environment Variables
Add to your .env file:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
3. Create Supabase Client
Create src/auth/supabase.tsx:
import { createClient } from '@supabase/supabase-js'
import { createContext, useContext, useEffect, useState } from 'react'
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
)
interface SupabaseAuthState {
isAuthenticated: boolean
user: any
login: (email: string, password: string) => Promise<void>
logout: () => Promise<void>
isLoading: boolean
}
const SupabaseAuthContext = createContext<SupabaseAuthState | undefined>(
undefined,
)
export function SupabaseAuthProvider({
children,
}: {
children: React.ReactNode
}) {
const [user, setUser] = useState(null)
const [isAuthenticated, setIsAuthenticated] = useState(false)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// Get initial session
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null)
setIsAuthenticated(!!session?.user)
setIsLoading(false)
})
// Listen for auth changes
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user ?? null)
setIsAuthenticated(!!session?.user)
setIsLoading(false)
})
return () => subscription.unsubscribe()
}, [])
const login = async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) throw error
}
const logout = async () => {
const { error } = await supabase.auth.signOut()
if (error) throw error
}
return (
<SupabaseAuthContext.Provider
value={{
isAuthenticated,
user,
login,
logout,
isLoading,
}}
>
{children}
</SupabaseAuthContext.Provider>
)
}
export function useSupabaseAuth() {
const context = useContext(SupabaseAuthContext)
if (context === undefined) {
throw new Error('useSupabaseAuth must be used within SupabaseAuthProvider')
}
return context
}
4. Create Login Component
Create src/routes/login.tsx:
import { createFileRoute, redirect } from '@tanstack/react-router'
import { useState } from 'react'
export const Route = createFileRoute('/login')({
validateSearch: (search) => ({
redirect: (search.redirect as string) || '/dashboard',
}),
beforeLoad: ({ context, search }) => {
if (context.auth.isAuthenticated) {
throw redirect({ to: search.redirect })
}
},
component: LoginComponent,
})
function LoginComponent() {
const { auth } = Route.useRouteContext()
const { redirect } = Route.useSearch()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsLoading(true)
setError('')
try {
await auth.login(email, password)
// Supabase auth will automatically update context
window.location.href = redirect
} catch (err: any) {
setError(err.message || 'Login failed')
} finally {
setIsLoading(false)
}
}
return (
<div className="min-h-screen flex items-center justify-center">
<form
onSubmit={handleSubmit}
className="max-w-md w-full space-y-4 p-6 border rounded-lg"
>
<h1 className="text-2xl font-bold text-center">Sign In</h1>
{error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div>
<label htmlFor="email" className="block text-sm font-medium mb-1">
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium mb-1">
Password
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
>
{isLoading ? 'Signing in...' : 'Sign In'}
</button>
</form>
</div>
)
}
5. Update App Configuration
Update src/App.tsx:
import { RouterProvider } from '@tanstack/react-router'
import { SupabaseAuthProvider, useSupabaseAuth } from './auth/supabase'
import { router } from './router'
function InnerApp() {
const auth = useSupabaseAuth()
if (auth.isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
Loading...
</div>
)
}
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<SupabaseAuthProvider>
<InnerApp />
</SupabaseAuthProvider>
)
}
export default App
Provider Comparison
| Feature | Auth0 | Clerk | Supabase |
|---|---|---|---|
| Setup Complexity | Medium | Low | Medium |
| UI Components | Basic | Excellent | None |
| Customization | High | Medium | High |
| Pricing | Freemium | Freemium | Freemium |
| Social Login | ✅ | ✅ | ✅ |
| Enterprise Features | ✅ | ✅ | ✅ |
| Database Included | ❌ | ❌ | ✅ |
When to Choose Each:
- Auth0: Complex enterprise requirements, extensive customization
- Clerk: Quick setup with beautiful UI components
- Supabase: Full-stack solution with database and real-time features
Common Problems
Provider Context Not Available
Problem: Auth context is undefined in components.
Solution: Ensure the provider wrapper is above RouterProvider:
// ✅ Correct order
<AuthProvider>
<InnerApp>
<RouterProvider />
</InnerApp>
</AuthProvider>
// ❌ Wrong order
<RouterProvider>
<AuthProvider />
</RouterProvider>
Infinite Loading States
Problem: App stuck on loading screen.
Solution: Check if auth provider properly sets isLoading to false:
// Add timeout fallback
useEffect(() => {
const timeout = setTimeout(() => {
if (isLoading) {
setIsLoading(false)
}
}, 5000)
return () => clearTimeout(timeout)
}, [isLoading])
Redirect Loops with Auth0
Problem: Continuous redirects between login and protected routes.
Solution: Handle Auth0's automatic redirects properly:
export const Route = createFileRoute('/_authenticated')({
beforeLoad: ({ context }) => {
if (!context.auth.isAuthenticated && !context.auth.isLoading) {
context.auth.login()
// Don't throw redirect, let Auth0 handle it
return
}
},
component: () => <Outlet />,
})
Common Next Steps
After integrating authentication providers, you might want to:
- How to Set Up Role-Based Access Control - Add permission-based routing
- How to Set Up Basic Authentication - Custom auth implementation
Related Resources
- Auth0 React SDK - Official Auth0 documentation
- Clerk React SDK - Official Clerk documentation
- Supabase Auth - Official Supabase auth guide