@maroonedsoftware/authentication
v0.1.0
Published
Authentication utilities for ServerKit.
Maintainers
Readme
@maroonedsoftware/authentication
Authentication utilities for ServerKit. Provides a scheme-based handler dispatch model for resolving Authorization request headers into typed AuthenticationContext objects, with full dependency injection support via injectkit.
Installation
pnpm add @maroonedsoftware/authenticationFeatures
- Scheme-based dispatch — register a handler per
Authorizationscheme (Bearer,Basic, or any custom scheme) and the right one is called automatically AuthenticationContext— a typed context object carrying session metadata, satisfied MFA factors, and arbitrary credential claims- Safe defaults —
invalidAuthenticationContextis a well-typed sentinel for unauthenticated state that can be safely checked without null handling - DI-friendly — all classes are decorated with
@Injectable()and designed to be registered in an injectkit container
Usage
1. Implement a handler for your scheme
import { Injectable } from 'injectkit';
import { AuthenticationHandler, AuthenticationContext } from '@maroonedsoftware/authentication';
import { DateTime } from 'luxon';
@Injectable()
class JwtAuthenticationHandler implements AuthenticationHandler {
async authenticate(scheme: string, token: string): Promise<AuthenticationContext> {
const payload = verifyJwt(token); // your JWT verification logic
return {
authenticationId: payload.jti,
issuedAt: DateTime.fromSeconds(payload.iat),
lastAccessedAt: DateTime.now(),
expiresAt: DateTime.fromSeconds(payload.exp),
factors: [
{
method: 'password',
type: 'password',
lastAuthenticated: DateTime.fromSeconds(payload.iat),
kind: 'knowledge',
},
],
claims: payload,
};
}
}2. Register the handler map in your DI container
import { InjectKitRegistry } from 'injectkit';
import {
AuthenticationHandlerMap,
AuthenticationSchemeHandler,
} from '@maroonedsoftware/authentication';
const registry = new InjectKitRegistry();
registry
.register(AuthenticationHandlerMap)
.useMap(AuthenticationHandlerMap)
.set('Bearer', JwtAuthenticationHandler);
registry.register(JwtAuthenticationHandler).useClass(JwtAuthenticationHandler).asSingleton();
registry.register(AuthenticationSchemeHandler).useClass(AuthenticationSchemeHandler).asSingleton();3. Resolve the authentication context
AuthenticationSchemeHandler.handle() parses the raw Authorization header, looks up the registered handler for the scheme, and returns the resolved context. It returns invalidAuthenticationContext when no header is present, the header is malformed, or no handler is registered for the scheme.
const schemeHandler = container.get(AuthenticationSchemeHandler);
// With a valid Bearer token
const ctx = await schemeHandler.handle('Bearer eyJhbGci...');
console.log(ctx.authenticationId); // 'abc-123'
console.log(ctx.claims); // { sub: 'user-1', ... }
// With no header
const ctx = await schemeHandler.handle(undefined);
console.log(ctx === invalidAuthenticationContext); // true4. With the ServerKit Koa middleware
When using @maroonedsoftware/koa, the authenticationMiddleware handles all of this automatically and attaches the resolved context to ctx.authenticationContext on every request:
import { authenticationMiddleware } from '@maroonedsoftware/koa';
app.use(authenticationMiddleware());
// In a route handler:
router.get('/me', async (ctx) => {
const { authenticationContext } = ctx;
if (authenticationContext === invalidAuthenticationContext) {
throw httpError(401);
}
ctx.body = { userId: authenticationContext.claims.sub };
});API Reference
AuthenticationContext
The resolved context produced by a successful authentication check.
| Property | Type | Description |
| ------------------ | ------------------------- | -------------------------------------------------------- |
| authenticationId | string | Unique identifier for this authentication session |
| issuedAt | DateTime | When the session was originally issued |
| lastAccessedAt | DateTime | When the session was last accessed |
| expiresAt | DateTime | When the session expires |
| factors | AuthenticationFactor[] | MFA factors satisfied in this session |
| claims | Record<string, unknown> | Arbitrary key/value claims extracted from the credential |
AuthenticationFactor
Describes a single satisfied authentication factor.
| Property | Type | Description |
| ------------------- | -------------------------- | ----------------------------------------------------------------- |
| method | string | Specific method used (e.g. "password", "totp", "webauthn") |
| type | string | Broader type grouping (e.g. "otp", "passkey") |
| lastAuthenticated | DateTime | When this factor was last successfully authenticated |
| kind | AuthenticationFactorKind | MFA category: "knowledge", "possession", or "biometric" |
AuthenticationFactorKind
type AuthenticationFactorKind = 'knowledge' | 'possession' | 'biometric';| Value | Meaning | Examples |
| ------------ | -------------------- | ------------------------------- |
| knowledge | Something you know | Password, PIN |
| possession | Something you have | TOTP app, hardware security key |
| biometric | Something you are | Fingerprint, face ID |
invalidAuthenticationContext
A sentinel AuthenticationContext value representing an unauthenticated or failed state. All DateTime fields are invalid Luxon instances. Use this as a safe default before authentication resolves, or as a guard value:
import { invalidAuthenticationContext } from '@maroonedsoftware/authentication';
if (ctx.authenticationContext === invalidAuthenticationContext) {
throw httpError(401);
}AuthenticationSchemeHandler
Parses the Authorization header and dispatches to the registered handler for the scheme.
| Method | Returns | Description |
| ------------------------------ | -------------------------------- | -------------------------------------------------------------------------------------- |
| handle(authorizationHeader?) | Promise<AuthenticationContext> | Resolves the header to a context, or returns invalidAuthenticationContext on failure |
Returns invalidAuthenticationContext when:
- No
Authorizationheader is present - The header is malformed (missing scheme or value)
- No handler is registered for the scheme
AuthenticationHandlerMap
An injectable Map<AuthorizationScheme, AuthenticationHandler>. Register one entry per scheme you want to support.
AuthenticationHandler
Interface for scheme-specific credential validators.
interface AuthenticationHandler {
authenticate(scheme: string, value: string): Promise<AuthenticationContext>;
}AuthorizationScheme
type AuthorizationScheme = 'bearer' | 'basic' | string;Accepts any string so custom schemes can be registered alongside the built-in hints.
License
MIT
