The authkitMiddleware() function accepts configuration options to control authentication behavior, route protection, and debugging. This guide covers all available options.
Configuration overview
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
export default authkitMiddleware ({
redirectUri: string , // Custom redirect URI
middlewareAuth: { // Enforce auth at middleware level
enabled: boolean ,
unauthenticatedPaths: string [],
} ,
debug: boolean , // Enable debug logging
signUpPaths: string [] , // Paths that trigger sign-up flow
eagerAuth: boolean , // Enable synchronous token access
}) ;
Options reference
redirectUri
Type: string
Default: undefined (uses NEXT_PUBLIC_WORKOS_REDIRECT_URI)
Override the OAuth callback URI dynamically. Useful for preview deployments or multi-environment setups.
export default authkitMiddleware ({
redirectUri: 'https://preview-abc123.example.com/callback' ,
}) ;
Use case: Vercel preview deployments
const redirectUri = process . env . VERCEL_URL
? `https:// ${ process . env . VERCEL_URL } /callback`
: process . env . NEXT_PUBLIC_WORKOS_REDIRECT_URI ;
export default authkitMiddleware ({
redirectUri ,
}) ;
The custom redirect URI must be configured in your WorkOS dashboard under “Redirects”.
middlewareAuth
Type: object
Default: undefined (auth is opt-in via withAuth)
Enforce authentication at the middleware level instead of requiring withAuth calls in each component.
Enable middleware-level authentication enforcement.
middlewareAuth.unauthenticatedPaths
Example: Secure by default
export default authkitMiddleware ({
middlewareAuth: {
enabled: true ,
unauthenticatedPaths: [ '/' , '/about' , '/pricing' ],
} ,
}) ;
export const config = {
matcher: [ '/' , '/about' , '/pricing' , '/dashboard/:path*' , '/admin/:path*' ],
};
In this setup:
/, /about, /pricing are public
/dashboard/* and /admin/* require authentication
Unauthenticated users are redirected to WorkOS AuthKit
Path patterns
Exact paths
Wildcard patterns
Mixed patterns
unauthenticatedPaths : [ '/' , '/about' , '/contact' ]
Matches only exact paths. unauthenticatedPaths : [ '/blog/:path*' , '/docs/:slug*' ]
/blog/:path* matches /blog, /blog/post-1, /blog/category/tech, etc.
/docs/:slug* matches /docs, /docs/guide, /docs/api/reference, etc.
unauthenticatedPaths : [
'/' ,
'/about' ,
'/blog/:path*' ,
'/api/public/:path*' ,
]
Combine exact paths and wildcards.
When middlewareAuth is enabled, you don’t need to call withAuth({ ensureSignedIn: true }) in your pages. Authentication is handled by middleware.
debug
Type: boolean
Default: false
Enable debug logging for authentication flow, session management, and token refresh.
export default authkitMiddleware ({
debug: true ,
}) ;
Debug output examples
[AuthKit] Session found for user: user_01H1234567890
[AuthKit] Access token expires in 845 seconds
[AuthKit] Refreshing session with refresh token
[AuthKit] Session refresh successful
[AuthKit] Redirecting unauthenticated user to AuthKit
Enable debug mode in development to troubleshoot authentication issues. Disable in production to avoid logging sensitive information.
signUpPaths
Type: string[]
Default: []
Specify paths that should trigger the sign-up screen hint in AuthKit instead of the sign-in screen.
export default authkitMiddleware ({
signUpPaths: [ '/signup' , '/register' , '/join' ] ,
}) ;
When unauthenticated users visit these paths:
They’re redirected to AuthKit with screen_hint=sign-up
The AuthKit UI displays the sign-up form by default
Users can still switch to sign-in if they have an account
Example: Dedicated sign-up page
// middleware.ts
export default authkitMiddleware ({
middlewareAuth: {
enabled: true ,
unauthenticatedPaths: [ '/' ],
} ,
signUpPaths: [ '/signup' , '/get-started' ] ,
}) ;
export const config = {
matcher: [ '/' , '/signup' , '/get-started' , '/dashboard/:path*' ],
};
Paths /signup and /get-started require auth but show the sign-up screen.
eagerAuth
Type: boolean
Default: false
Enable synchronous access token availability for third-party services that need immediate token access.
export default authkitMiddleware ({
eagerAuth: true ,
}) ;
When enabled:
Access token is stored in a 30-second cookie on page load
Client components can access tokens synchronously via getAccessToken()
Token cookie is immediately consumed and deleted
See the Eager auth guide for detailed usage and security considerations.
Eager auth makes tokens briefly accessible via JavaScript. Only enable if you need synchronous token access for third-party services.
Complete examples
Basic setup
Minimal configuration for most applications:
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
export default authkitMiddleware () ;
export const config = {
matcher: [ '/dashboard/:path*' , '/admin/:path*' ],
};
Authentication is opt-in via withAuth({ ensureSignedIn: true }) in pages.
Secure by default
Protect all routes except specific public pages:
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
export default authkitMiddleware ({
middlewareAuth: {
enabled: true ,
unauthenticatedPaths: [
'/' ,
'/about' ,
'/pricing' ,
'/blog/:path*' ,
'/docs/:path*' ,
],
} ,
}) ;
export const config = {
matcher: [
'/' ,
'/about' ,
'/pricing' ,
'/blog/:path*' ,
'/docs/:path*' ,
'/dashboard/:path*' ,
'/app/:path*' ,
],
};
Development with debugging
Enable debug logging for troubleshooting:
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
const isDevelopment = process . env . NODE_ENV === 'development' ;
export default authkitMiddleware ({
debug: isDevelopment ,
middlewareAuth: {
enabled: true ,
unauthenticatedPaths: [ '/' , '/test' ],
} ,
}) ;
export const config = {
matcher: [ '/' , '/test' , '/app/:path*' ],
};
Multi-environment with preview deployments
Handle preview deployments with dynamic redirect URIs:
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
const getRedirectUri = () => {
// Vercel preview deployment
if ( process . env . VERCEL_URL ) {
return `https:// ${ process . env . VERCEL_URL } /callback` ;
}
// Netlify preview deployment
if ( process . env . DEPLOY_PRIME_URL ) {
return ` ${ process . env . DEPLOY_PRIME_URL } /callback` ;
}
// Default from environment variable
return process . env . NEXT_PUBLIC_WORKOS_REDIRECT_URI ;
};
export default authkitMiddleware ({
redirectUri: getRedirectUri () ,
debug: process . env . NODE_ENV === 'development' ,
}) ;
export const config = {
matcher: [ '/dashboard/:path*' ],
};
Sign-up focused paths
Differentiate sign-in vs sign-up flows:
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs' ;
export default authkitMiddleware ({
middlewareAuth: {
enabled: true ,
unauthenticatedPaths: [ '/' , '/signin' ],
} ,
signUpPaths: [ '/signup' , '/join' , '/register' , '/get-started' ] ,
}) ;
export const config = {
matcher: [
'/' ,
'/signin' ,
'/signup' ,
'/join' ,
'/register' ,
'/get-started' ,
'/dashboard/:path*' ,
],
};
/signin → Shows sign-in screen
/signup, /join, /register, /get-started → Show sign-up screen
/ → Public (no redirect)
Matcher configuration
The matcher export controls which routes run through middleware. It’s separate from middleware options but works together.
Best practices
Be specific with matchers
Avoid catch-all matchers that intercept static assets: // ❌ Bad: Intercepts everything
export const config = {
matcher: [ '/:path*' ],
};
// ✅ Good: Specific paths only
export const config = {
matcher: [ '/dashboard/:path*' , '/api/:path*' ],
};
If you need broad matching, exclude Next.js internals: export const config = {
matcher: [ '/((?!_next/static|_next/image|favicon.ico).*)' ],
};
Match authenticated routes only
For performance, only run middleware on routes that need authentication: export const config = {
matcher: [ '/dashboard/:path*' , '/admin/:path*' , '/api/private/:path*' ],
};
Environment variables Required and optional environment variables
Middleware concepts How middleware works
Composable middleware Advanced middleware patterns
Protecting routes Route protection strategies