@autolabz/service-auth-nextjs
v1.0.3
Published
Next.js App Router authentication utilities for AutoLab services
Readme
@autolabz/service-auth-nextjs
Next.js App Router authentication utilities for AutoLab services.
Overview
This package provides authentication utilities specifically designed for Next.js App Router applications, built on the framework-agnostic @autolabz/service-auth-core. It offers:
- Request Verification: Verify incoming requests using JWT or OAuth userinfo fallback
- Scope Validation: Check for required scopes before processing requests
- AuthBridge Creation: Generate AuthBridge objects for downstream SDK calls
- Type-Safe: Full TypeScript support with discriminated union return types
Installation
npm install @autolabz/service-auth-nextjsQuick Start
Basic Request Verification
import { verifyRequest, type AuthConfig } from '@autolabz/service-auth-nextjs';
const authConfig: AuthConfig = {
jwtAlg: 'HS256',
jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
authBaseUrl: process.env.AUTH_BASE_URL!,
oauthUserinfoPath: '/oauth/userinfo',
oauthUserinfoTimeoutMs: 2000,
};
// In your API route (app/api/hello/route.ts)
export async function GET(request: Request) {
const result = await verifyRequest(request, authConfig);
if (!result.success) {
return Response.json({ error: result.error }, { status: 401 });
}
return Response.json({
message: `Hello, ${result.auth.email || result.auth.userId}!`,
});
}Request Verification with Scope Check
import { verifyRequestWithScopes } from '@autolabz/service-auth-nextjs';
export async function GET(request: Request) {
// Require both 'data' and 'points' scopes
const result = await verifyRequestWithScopes(request, authConfig, ['data', 'points']);
if (!result.success) {
const status = result.error === 'Insufficient scopes' ? 403 : 401;
return Response.json({ error: result.error }, { status });
}
return Response.json({ userId: result.auth.userId });
}Using with Downstream Services
import { verifyRequest, makeAuthBridge } from '@autolabz/service-auth-nextjs';
import { createDataClient } from '@autolabz/data-sdk';
import { createPointsClient } from '@autolabz/points-sdk';
import { createLLMClient } from '@autolabz/llmapi-sdk';
export async function GET(request: Request) {
// 1. Verify the request
const authResult = await verifyRequest(request, authConfig);
if (!authResult.success) {
return Response.json({ error: authResult.error }, { status: 401 });
}
// 2. Create AuthBridge for downstream calls
const auth = makeAuthBridge(request, {
onUnauthorized: () => console.error('Downstream service returned 401'),
});
// 3. Use with SDK clients
const dataClient = createDataClient({
baseURL: process.env.DATA_BASE_URL!,
auth,
});
const pointsClient = createPointsClient({
baseURL: process.env.POINTS_BASE_URL!,
auth,
});
// 4. Make authenticated requests
const [userData, balance] = await Promise.all([
dataClient.get(`/v1/data/user-profile`),
pointsClient.getMyBalance(),
]);
return Response.json({ userData, balance });
}API Reference
verifyRequest(request: Request, config: AuthConfig): Promise<AuthResult>
Verify an incoming Next.js request using JWT verification (SIMPLE mode) with OAuth userinfo fallback.
Parameters:
request: Next.js Request object (Web API Request)config: Authentication configuration
Returns: AuthResult
- On success:
{ success: true, auth: AuthPayload } - On failure:
{ success: false, error: string }
verifyRequestWithScopes(request: Request, config: AuthConfig, requiredScopes: string[]): Promise<AuthResult>
Verify a request and check for required scopes.
Parameters:
request: Next.js Request objectconfig: Authentication configurationrequiredScopes: Array of required scope strings
Returns: AuthResult
- On success:
{ success: true, auth: AuthPayload } - On failure:
{ success: false, error: string }(error can be 'Insufficient scopes')
makeAuthBridge(request: Request, options?: AuthBridgeOptions): AuthBridgeLike
Create an AuthBridge from a Next.js Request for use with AutoLab SDK clients.
Parameters:
request: Next.js Request objectoptions: Optional configurationonUnauthorized?: () => void: Callback when downstream service returns 401
Returns: AuthBridgeLike object compatible with SDK clients
Configuration
AuthConfig
interface AuthConfig {
// JWT verification (SIMPLE mode)
jwtAlg: 'HS256' | 'RS256';
jwtAccessSecret?: string; // Required for HS256
jwksUrl?: string; // Required for RS256
authIssuer?: string; // Optional issuer validation
// OAuth userinfo fallback
authBaseUrl: string; // OAuth service base URL
oauthUserinfoPath?: string; // Default: 'oauth/userinfo'
oauthUserinfoTimeoutMs?: number; // Default: 2000
oauthExpectedAudience?: string; // Optional audience validation
}Type Definitions
AuthResult
Discriminated union type for authentication results:
type AuthResult =
| { success: true; auth: AuthPayload }
| { success: false; error: string };AuthPayload
interface AuthPayload {
userId: string;
sub?: string;
email?: string;
iat?: number;
exp?: number;
iss?: string;
aud?: string | string[];
azp?: string;
scope?: string;
tokenType?: string;
}Complete Example: API Route
// app/api/data/[key]/route.ts
import { verifyRequestWithScopes, makeAuthBridge } from '@autolabz/service-auth-nextjs';
import { createDataClient } from '@autolabz/data-sdk';
const authConfig = {
jwtAlg: 'HS256' as const,
jwtAccessSecret: process.env.JWT_ACCESS_SECRET!,
authBaseUrl: process.env.AUTH_BASE_URL!,
oauthUserinfoPath: '/oauth/userinfo',
oauthUserinfoTimeoutMs: 2000,
};
export async function GET(
request: Request,
{ params }: { params: { key: string } }
) {
// Verify with required scope
const result = await verifyRequestWithScopes(request, authConfig, ['data']);
if (!result.success) {
const status = result.error === 'Insufficient scopes' ? 403 : 401;
return Response.json({ error: result.error }, { status });
}
// Create auth bridge for downstream call
const auth = makeAuthBridge(request);
const dataClient = createDataClient({
baseURL: process.env.DATA_BASE_URL!,
auth,
});
try {
const data = await dataClient.get(`/v1/data/${encodeURIComponent(params.key)}`);
return Response.json(data);
} catch (error) {
return Response.json(
{ error: 'Failed to fetch data' },
{ status: 500 }
);
}
}
export async function PUT(
request: Request,
{ params }: { params: { key: string } }
) {
const result = await verifyRequestWithScopes(request, authConfig, ['data']);
if (!result.success) {
const status = result.error === 'Insufficient scopes' ? 403 : 401;
return Response.json({ error: result.error }, { status });
}
const auth = makeAuthBridge(request);
const dataClient = createDataClient({
baseURL: process.env.DATA_BASE_URL!,
auth,
});
const body = await request.json();
try {
await dataClient.put(`/v1/data/${encodeURIComponent(params.key)}`, body);
return Response.json({ success: true });
} catch (error) {
return Response.json(
{ error: 'Failed to update data' },
{ status: 500 }
);
}
}Environment Variables
# JWT verification (SIMPLE mode)
JWT_ALG=HS256
JWT_ACCESS_SECRET=your-secret
# OAuth userinfo fallback
AUTH_BASE_URL=http://auth-service/api
OAUTH_USERINFO_PATH=/oauth/userinfo
OAUTH_USERINFO_TIMEOUT_MS=2000
# Optional
AUTH_ISSUER=https://auth.example.com
OAUTH_EXPECTED_AUDIENCE=autolab-api
# Downstream services
DATA_BASE_URL=http://data-service
POINTS_BASE_URL=http://points-service
LLMAPI_BASE_URL=http://llmapi-serviceMiddleware Pattern
For route-wide authentication, you can create a middleware wrapper:
// lib/auth-middleware.ts
import { verifyRequest, type AuthConfig, type AuthPayload } from '@autolabz/service-auth-nextjs';
export function withAuth(
handler: (request: Request, auth: AuthPayload, ...args: any[]) => Promise<Response>
) {
return async (request: Request, ...args: any[]) => {
const authConfig: AuthConfig = {
jwtAlg: 'HS256',
jwtAccessSecret: process.env.JWT_ACCESS_SECRET!,
authBaseUrl: process.env.AUTH_BASE_URL!,
};
const result = await verifyRequest(request, authConfig);
if (!result.success) {
return Response.json({ error: result.error }, { status: 401 });
}
return handler(request, result.auth, ...args);
};
}
// Usage in route
export const GET = withAuth(async (request, auth) => {
return Response.json({ userId: auth.userId });
});Best Practices
- Configuration: Store
AuthConfigin a central location and reuse across routes - Error Handling: Always check
result.successbefore accessingresult.auth - Scope Validation: Use
verifyRequestWithScopeswhen endpoints require specific permissions - AuthBridge: Create AuthBridge only after successful verification
- Environment Variables: Never hardcode secrets; use environment variables
Troubleshooting
401 Unauthorized
- Verify
AUTH_BASE_URLandOAUTH_USERINFO_PATHare correct - Ensure request includes
Authorization: Bearer <token>header - Check token validity and expiration
403 Insufficient Scopes
- Verify the token includes all required scopes
- Check scope configuration in OAuth application settings
Request Timeout
- Increase
oauthUserinfoTimeoutMsvalue - Check auth service performance and network latency
License
MIT
