HTTP Server framework for Node.js with built-in authentication, validation, caching, logging, and plugin architecture
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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;
}