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.
Quick Start
Use verifyNextAuth to authenticate Next.js App Router API routes and middleware. It handles both cookies and Authorization headers.
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function GET(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
if (!result.success) {
return NextResponse.json(
{ error: result.error },
{ status: 401 }
);
}
const { userId, payload } = result.auth;
return NextResponse.json({
userId,
email: payload.email,
});
}
Function Signature
function verifyNextAuth(
request: NextRequest,
options: VerifyAuthOptions
): Promise<
| { success: true; auth: AuthContext }
| { success: false; error: string; auth?: AuthContext }
>
Parameters
The Next.js request object from your API route or middleware
options
VerifyAuthOptions
required
Configuration options for authenticationShow VerifyAuthOptions properties
Your Logto server URL (e.g., https://your-tenant.logto.app)
API resource identifier registered in Logto
cookieName
string
default:"logto_authtoken"
Custom cookie name if changed in frontend
Required scope that must be present in the token
Enable guest mode for unauthenticated users
Return Value
Whether authentication was successful
User authentication context (always present when success: true, optional with guest mode)Show AuthContext properties
Logto user ID from the sub claim
Whether the user is authenticated
Full JWT payload with all claims
Whether user is in guest mode (when allowGuest: true)
Generated UUID for guest users
Error message when success: false
Usage Examples
API Route (App Router)
// app/api/user/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function GET(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
if (!result.success) {
return NextResponse.json(
{ error: result.error },
{ status: 401 }
);
}
// Fetch user data from database
const user = await db.user.findUnique({
where: { id: result.auth.userId },
});
return NextResponse.json({ user });
}
POST Route with Data
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function POST(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
if (!result.success) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
const body = await request.json();
const post = await db.post.create({
data: {
...body,
authorId: result.auth.userId,
},
});
return NextResponse.json({ post });
}
Route with Required Scope
// app/api/admin/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function DELETE(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
requiredScope: 'admin:users:delete',
});
if (!result.success) {
return NextResponse.json(
{ error: 'Insufficient permissions' },
{ status: 403 }
);
}
// Delete user logic
return NextResponse.json({ success: true });
}
Next.js Middleware
Protect multiple routes with Next.js middleware:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function middleware(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
if (!result.success) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
// Add user ID to response headers for downstream use
const response = NextResponse.next();
response.headers.set('x-user-id', result.auth.userId!);
return response;
}
export const config = {
matcher: '/api/protected/:path*',
};
Guest Mode
Allow both authenticated and guest users:
// app/api/cart/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth } from '@ouim/logto-authkit/server';
export async function GET(request: NextRequest) {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
allowGuest: true,
});
// result.auth is always present with allowGuest
const cart = result.auth!.isAuthenticated
? await getCartByUserId(result.auth.userId!)
: await getCartByGuestId(result.auth.guestId!);
return NextResponse.json({ cart });
}
Reusable Auth Helper
Create a helper function for consistent authentication:
// lib/auth.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyNextAuth, AuthContext } from '@ouim/logto-authkit/server';
type AuthHandler = (
request: NextRequest,
auth: AuthContext
) => Promise<NextResponse> | NextResponse;
export function withAuth(handler: AuthHandler) {
return async (request: NextRequest) => {
const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
if (!result.success) {
return NextResponse.json(
{ error: result.error },
{ status: 401 }
);
}
return handler(request, result.auth);
};
}
// Usage in routes
// app/api/profile/route.ts
import { withAuth } from '@/lib/auth';
export const GET = withAuth(async (request, auth) => {
const user = await db.user.findUnique({
where: { id: auth.userId },
});
return NextResponse.json({ user });
});
Server Actions (Experimental)
For Next.js Server Actions, extract the token from cookies:
'use server'
import { cookies } from 'next/headers';
import { verifyAuth } from '@ouim/logto-authkit/server';
export async function updateProfile(formData: FormData) {
const cookieStore = cookies();
const token = cookieStore.get('logto_authtoken')?.value;
if (!token) {
throw new Error('Unauthorized');
}
const auth = await verifyAuth(token, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
// Update user profile
await db.user.update({
where: { id: auth.userId },
data: {
name: formData.get('name'),
},
});
}
For Server Actions, use the generic verifyAuth function instead of verifyNextAuth. See Generic Usage for details.
Error Handling
With allowGuest: false (default)
const result = await verifyNextAuth(request, { ... });
if (!result.success) {
// result.error contains the error message
// result.auth is undefined
return NextResponse.json(
{ error: result.error },
{ status: 401 }
);
}
// result.auth is guaranteed to exist and be authenticated
const userId = result.auth.userId;
With allowGuest: true
const result = await verifyNextAuth(request, {
allowGuest: true,
// ... other options
});
if (result.success) {
// Authenticated user
const userId = result.auth.userId;
} else {
// Guest user (result.auth contains guest context)
if (result.auth?.isGuest) {
const guestId = result.auth.guestId;
}
}
Common Error Messages
No token found in cookies or Authorization header
Token verification failed: Token has expired
The JWT token’s expiration time has passed.Solution: Refresh the token on the frontend or prompt user to re-authenticate.
Token wasn’t issued by your Logto server.Solution: Verify logtoUrl matches your Logto tenant URL.
Token’s audience claim doesn’t match your API resource.Solution: Verify audience matches the API resource registered in Logto.
Token doesn’t include the required scope.Solution: Ensure the scope is requested during frontend authentication.
Best Practices
Use Environment Variables
Store Logto configuration in environment variables:const result = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL!,
audience: process.env.LOGTO_API_RESOURCE!,
});
Create Helper Functions
Wrap verifyNextAuth in helper functions for consistent error handling:export const withAuth = (handler: AuthHandler) => { ... };
Return Appropriate Status Codes
401 Unauthorized: Authentication failed or missing
403 Forbidden: Authenticated but insufficient permissions
Validate Auth Context
Always check isAuthenticated when using guest mode:if (!result.auth?.isAuthenticated) {
return NextResponse.json({ error: 'Login required' }, { status: 401 });
}
Express Middleware
Middleware for Express.js applications
Generic Usage
Flexible verifyAuth for any environment