Pluggable authentication system supporting multiple schemes and strategies with built-in session management in @hapi/hapi.
The authentication interface provides methods to register schemes, strategies, and manage authentication.
interface AuthAPI {
/** Register authentication scheme */
scheme(name: string, scheme: AuthScheme): void;
/** Register authentication strategy */
strategy(name: string, scheme: string, options?: object): void;
/** Set default authentication */
default(options: AuthOptions): void;
/** Test authentication strategy */
test(name: string, request: Request): Promise<AuthTestResult>;
/** Verify request authentication */
verify(request: Request): Promise<AuthVerifyResult>;
/** Lookup authentication strategy */
lookup(name: string): AuthStrategy | null;
}
interface AuthScheme {
/** Authentication function */
authenticate(request: Request, h: ResponseToolkit): AuthResult;
/** Optional payload authentication */
payload?(request: Request, h: ResponseToolkit): AuthResult;
/** Optional response decoration */
response?(request: Request, h: ResponseToolkit): void;
/** Optional verification function */
verify?(auth: AuthCredentials): boolean;
/** Optional strategy options */
options?: object;
}
type AuthResult = AuthSuccess | AuthError | symbol;
interface AuthSuccess {
/** Authentication credentials */
credentials: AuthCredentials;
/** Authentication artifacts */
artifacts?: object;
}
interface AuthError {
/** Error object */
error: Error;
/** Authentication credentials if partial success */
credentials?: AuthCredentials;
/** Authentication artifacts */
artifacts?: object;
}Register authentication schemes that define how authentication works.
/**
* Register an authentication scheme
* @param name - Scheme name
* @param scheme - Scheme implementation
*/
scheme(name: string, scheme: AuthScheme): void;Usage Examples:
// Basic token authentication scheme
server.auth.scheme('token', (server, options) => {
return {
authenticate: (request, h) => {
const token = request.headers.authorization;
if (!token) {
return h.unauthenticated(new Error('Missing token'));
}
// Validate token
const user = validateToken(token.replace('Bearer ', ''));
if (!user) {
return h.unauthenticated(new Error('Invalid token'));
}
return h.authenticated({
credentials: { user: user.id, scope: user.permissions },
artifacts: { token }
});
}
};
});
// Cookie session authentication scheme
server.auth.scheme('session', (server, options) => {
return {
authenticate: (request, h) => {
const session = request.state.session;
if (!session) {
return h.unauthenticated(new Error('No session'));
}
// Validate session
if (session.expires < Date.now()) {
return h.unauthenticated(new Error('Session expired'));
}
return h.authenticated({
credentials: {
user: session.userId,
scope: session.scope
},
artifacts: { sessionId: session.id }
});
}
};
});Register authentication strategies that use schemes with specific configuration.
/**
* Register an authentication strategy
* @param name - Strategy name
* @param scheme - Scheme name to use
* @param options - Strategy-specific options
*/
strategy(name: string, scheme: string, options?: object): void;Usage Examples:
// Register strategies using the schemes
server.auth.strategy('jwt-token', 'token', {
validate: async (credentials) => {
const user = await db.users.findById(credentials.user);
return { isValid: !!user, credentials: { ...credentials, profile: user } };
}
});
server.auth.strategy('user-session', 'session', {
cookie: 'sid',
ttl: 24 * 60 * 60 * 1000, // 1 day
validateFunc: async (session) => {
const user = await db.sessions.validate(session.id);
return { valid: !!user, credentials: user };
}
});Set default authentication requirements for all routes.
/**
* Set default authentication for all routes
* @param options - Default authentication options
*/
default(options: AuthOptions): void;
interface AuthOptions {
/** Strategy name to use */
strategy?: string;
/** Authentication mode */
mode?: 'required' | 'optional' | 'try';
/** Required scope */
scope?: string | string[];
/** Required entity type */
entity?: 'user' | 'app' | 'any';
/** Access function for additional validation */
access?: (request: Request) => boolean;
}Usage Examples:
// Set default authentication for all routes
server.auth.default({
strategy: 'jwt-token',
mode: 'required'
});
// Routes can override default
server.route({
method: 'GET',
path: '/public',
options: {
auth: false // Disable authentication for this route
},
handler: () => ({ message: 'Public endpoint' })
});
server.route({
method: 'GET',
path: '/admin',
options: {
auth: {
strategy: 'jwt-token',
scope: ['admin']
}
},
handler: () => ({ message: 'Admin endpoint' })
});Test authentication strategies programmatically.
/**
* Test authentication strategy
* @param name - Strategy name
* @param request - Request object
* @returns Authentication test result
*/
test(name: string, request: Request): Promise<AuthTestResult>;
interface AuthTestResult {
/** Whether authentication succeeded */
isValid: boolean;
/** Authentication credentials if valid */
credentials?: AuthCredentials;
/** Authentication artifacts */
artifacts?: object;
/** Error if authentication failed */
error?: Error;
}Usage Examples:
server.route({
method: 'POST',
path: '/test-auth',
handler: async (request, h) => {
const result = await server.auth.test('jwt-token', request);
return {
valid: result.isValid,
user: result.credentials?.user,
error: result.error?.message
};
}
});Verify request authentication credentials programmatically.
/**
* Verify request authentication
* @param request - Request object
* @returns Authentication verification result
*/
verify(request: Request): Promise<AuthVerifyResult>;
interface AuthVerifyResult {
/** Whether authentication is valid */
isValid: boolean;
/** Authentication credentials if valid */
credentials?: AuthCredentials;
/** Authentication artifacts */
artifacts?: object;
/** Authentication error if invalid */
error?: Error;
/** Strategy name used */
strategy?: string;
}Usage Examples:
server.ext('onPreHandler', async (request, h) => {
if (request.path.startsWith('/api/')) {
const result = await server.auth.verify(request);
if (!result.isValid) {
return h.response({ error: 'Authentication required' }).code(401);
}
// Authentication is valid, continue
return h.continue;
}
return h.continue;
});
// In a route handler
server.route({
method: 'POST',
path: '/secure-action',
handler: async (request, h) => {
const verification = await server.auth.verify(request);
if (verification.isValid && verification.credentials.scope.includes('admin')) {
return { message: 'Admin action completed' };
}
return h.response({ error: 'Insufficient permissions' }).code(403);
}
});Configure authentication at the route level.
interface RouteAuthOptions {
/** Authentication strategy name */
strategy?: string;
/** Authentication strategies (multiple) */
strategies?: string[];
/** Authentication mode */
mode?: 'required' | 'optional' | 'try';
/** Payload authentication */
payload?: boolean | string;
/** Required scope */
scope?: string | string[];
/** Required entity type */
entity?: 'user' | 'app' | 'any';
/** Access requirements */
access?: AccessOptions | AccessOptions[];
}
interface AccessOptions {
/** Required scope */
scope?: string | string[];
/** Required entity */
entity?: 'user' | 'app' | 'any';
/** Custom access function */
validate?: (request: Request) => boolean;
}Usage Examples:
// Required authentication
server.route({
method: 'GET',
path: '/protected',
options: {
auth: {
strategy: 'jwt-token',
mode: 'required'
}
},
handler: (request, h) => {
return {
message: `Hello ${request.auth.credentials.user}`,
scope: request.auth.credentials.scope
};
}
});
// Optional authentication
server.route({
method: 'GET',
path: '/optional-auth',
options: {
auth: {
strategy: 'jwt-token',
mode: 'optional'
}
},
handler: (request, h) => {
if (request.auth.isAuthenticated) {
return { message: `Hello ${request.auth.credentials.user}` };
}
return { message: 'Hello anonymous user' };
}
});
// Multiple strategies
server.route({
method: 'GET',
path: '/multi-auth',
options: {
auth: {
strategies: ['jwt-token', 'user-session'],
mode: 'required'
}
},
handler: (request, h) => {
return {
strategy: request.auth.strategy,
user: request.auth.credentials.user
};
}
});
// Scope-based access control
server.route({
method: 'DELETE',
path: '/admin/users/{id}',
options: {
auth: {
strategy: 'jwt-token',
scope: ['admin', 'user:delete']
}
},
handler: (request, h) => {
return { message: 'User deleted' };
}
});Helper methods available in the response toolkit for authentication.
/**
* Set authentication success
* @param data - Authentication data
* @returns Authentication result
*/
authenticated(data: AuthCredentials): object;
/**
* Set authentication failure
* @param error - Authentication error
* @param data - Optional partial credentials
* @returns Authentication result
*/
unauthenticated(error: Error, data?: AuthCredentials): object;Usage Examples:
// Used within authentication schemes
const customScheme = (server, options) => {
return {
authenticate: async (request, h) => {
try {
const token = extractToken(request);
const user = await validateToken(token);
return h.authenticated({
credentials: {
user: user.id,
scope: user.permissions,
profile: user
},
artifacts: { token, validatedAt: Date.now() }
});
} catch (error) {
return h.unauthenticated(error);
}
}
};
};interface AuthCredentials {
/** User identifier */
user?: string;
/** User scope/permissions */
scope?: string[];
/** Authentication strategy used */
strategy?: string;
/** Additional credential data */
[key: string]: any;
}
interface AuthStrategy {
/** Strategy name */
name: string;
/** Scheme name */
scheme: string;
/** Strategy options */
options: object;
/** Strategy test method */
test: (request: Request) => Promise<AuthTestResult>;
}
interface AuthVerifyResult {
/** Whether verification succeeded */
isValid: boolean;
/** Updated credentials */
credentials?: AuthCredentials;
/** Error if verification failed */
error?: Error;
}