@sftech/nestjs-auth-backend
v1.5.0
Published
A standalone, reusable NestJS library for OAuth 2.0 / OIDC authentication with Authorization Code Flow.
Downloads
464
Readme
@sftech/nestjs-auth-backend
A standalone, reusable NestJS library for OAuth 2.0 / OIDC authentication with Authorization Code Flow.
This library provides a complete authentication microservice that can be used as a central auth service for your microservices architecture.
Features
- OAuth 2.0 / OIDC Authorization Code Flow with PKCE support
- Database-backed session management using TypeORM
- Session-based authentication with HTTP-only cookies
- Token management (Access Token, Refresh Token, ID Token)
- Single Sign-On (SSO) with provider logout support
- Dynamic post-login redirect URL with OWASP open-redirect protection (v1.4.0+)
- Native app deep-link relay for Capacitor / mobile OAuth flows (v1.5.0+)
- Clean Architecture (domain, application, infrastructure, presentation)
- REST API endpoints for authentication flows
Installation
npm install @sftech/nestjs-auth-backendPeer Dependencies
npm install @sftech/nestjs-core @sftech/nestjs-dbUsage
1. Configure Module
import { Module } from '@nestjs/common';
import { NestjsAuthBackendModule } from '@sftech/nestjs-auth-backend';
@Module({
imports: [
NestjsAuthBackendModule.register({
// Module configuration options
}),
],
})
export class AppModule {}2. Configuration
The module uses configuration values from @sftech/nestjs-core configuration system. Add the following to your app.config.json:
{
"OAUTH_PROVIDER_URL": "https://your-oauth-provider.com/realms/your-realm",
"OAUTH_AUTHORIZATION_ENDPOINT": "https://your-oauth-provider.com/realms/your-realm/protocol/openid-connect/auth",
"OAUTH_TOKEN_ENDPOINT": "https://your-oauth-provider.com/realms/your-realm/protocol/openid-connect/token",
"OAUTH_USERINFO_ENDPOINT": "https://your-oauth-provider.com/realms/your-realm/protocol/openid-connect/userinfo",
"OAUTH_END_SESSION_ENDPOINT": "https://your-oauth-provider.com/realms/your-realm/protocol/openid-connect/logout",
"OAUTH_CLIENT_ID": "your-client-id",
"OAUTH_CLIENT_SECRET": "your-client-secret",
"OAUTH_REDIRECT_URL": "http://localhost:3000/api/auth/callback",
"OAUTH_FRONTEND_CALLBACK_URL": "http://localhost:4200/auth/callback",
"OAUTH_SCOPE": "openid profile email roles",
"OAUTH_SESSION_MODE": "single",
"OAUTH_SESSION_MAX_AGE": "3600000",
"OAUTH_ALLOWED_REDIRECT_ORIGINS": "https://localhost,http://localhost:4200,https://myapp.example.com",
"OAUTH_NATIVE_APP_SCHEME": "com.example.myapp"
}Configuration Parameters
| Parameter | Required | Description | Example |
|-----------|----------|-------------|---------|
| OAUTH_PROVIDER_URL | Yes | Base URL of OAuth provider | https://keycloak.example.com/realms/myrealm |
| OAUTH_AUTHORIZATION_ENDPOINT | Yes | OAuth authorization endpoint | https://keycloak.example.com/realms/myrealm/protocol/openid-connect/auth |
| OAUTH_TOKEN_ENDPOINT | Yes | OAuth token endpoint | https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token |
| OAUTH_USERINFO_ENDPOINT | Yes | OAuth userinfo endpoint | https://keycloak.example.com/realms/myrealm/protocol/openid-connect/userinfo |
| OAUTH_END_SESSION_ENDPOINT | No | OAuth logout endpoint (for SSO logout) | https://keycloak.example.com/realms/myrealm/protocol/openid-connect/logout |
| OAUTH_CLIENT_ID | Yes | OAuth client ID | my-app |
| OAUTH_CLIENT_SECRET | Yes | OAuth client secret | your-secret-here |
| OAUTH_REDIRECT_URL | Yes | OAuth callback URL (this service's own endpoint) | http://localhost:3000/api/auth/callback |
| OAUTH_FRONTEND_CALLBACK_URL | No | Default frontend URL to redirect to after login | http://localhost:4200/auth/callback |
| OAUTH_SCOPE | Yes | OAuth scopes to request | openid profile email roles |
| OAUTH_SESSION_MODE | No | Session mode: single or multi | single (default) |
| OAUTH_SESSION_MAX_AGE | No | Session max age in milliseconds | 3600000 (1 hour, default) |
| OAUTH_ALLOWED_REDIRECT_ORIGINS | No | Comma-separated list of origins allowed as post-login redirect targets (v1.4.0+) | https://localhost,http://localhost:4200 |
| OAUTH_NATIVE_APP_SCHEME | No | Custom URL scheme for native app deep-link relay (v1.5.0+) | com.sftech.ebuilder |
Notes:
- Session cookie name is fixed to
oauth_session(not configurable). - When
OAUTH_ALLOWED_REDIRECT_ORIGINSis not set, the library derives a single allowed origin fromOAUTH_FRONTEND_CALLBACK_URLautomatically. - When
OAUTH_NATIVE_APP_SCHEMEis not set,GET /auth/native-relayreturns 404.
3. Keycloak Example
For Keycloak, the endpoints follow this pattern:
{
"OAUTH_PROVIDER_URL": "https://your-keycloak.com/realms/your-realm",
"OAUTH_AUTHORIZATION_ENDPOINT": "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/auth",
"OAUTH_TOKEN_ENDPOINT": "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/token",
"OAUTH_USERINFO_ENDPOINT": "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/userinfo",
"OAUTH_END_SESSION_ENDPOINT": "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/logout"
}REST API Endpoints
The module provides the following REST endpoints under /api/auth:
GET /auth/login
Initiates OAuth login flow by redirecting to the OAuth provider.
Query Parameters:
| Parameter | Required | Description |
|-----------|----------|-------------|
| redirect_url | No | Post-login redirect URL (see validation rules below) |
Successful response: 302 Redirect to OAuth provider authorization endpoint.
Error response: 400 Bad Request when redirect_url is provided but fails validation.
redirect_url Validation Rules (v1.4.0+)
When redirect_url is supplied the library enforces the following rules in order. Any rule failure returns HTTP 400 with a distinct error message — the request is never silently redirected to a fallback.
| Rule | Accepted | Rejected |
|------|----------|---------|
| Empty / whitespace-only | Treated as absent; falls back to OAUTH_FRONTEND_CALLBACK_URL | — |
| Relative path starting with / (not //) | redirect_url=/auth/callback | redirect_url=//evil.com/auth/callback |
| Absolute URL — must be parseable | https://localhost/auth/callback | not-a-url |
| Absolute URL — origin must be in the allowlist | Origin is in OAUTH_ALLOWED_REDIRECT_ORIGINS | https://evil.com/auth/callback |
| Absolute URL — path must be exactly /auth/callback | https://localhost/auth/callback | https://localhost/dashboard |
| Absolute URL — must contain no query parameters | https://localhost/auth/callback | https://localhost/auth/callback?foo=bar |
400 error messages:
| Condition | Message |
|-----------|---------|
| Not a valid URL format | Invalid redirect URL: not a valid URL format |
| Origin not in allowlist | Invalid redirect URL: origin not in allowlist |
| Path is not /auth/callback | Invalid redirect URL: path must be /auth/callback |
| Query parameters present | Invalid redirect URL: must not contain query parameters |
Examples:
# Valid — accepted, user is redirected here after login
GET /auth/login?redirect_url=https%3A%2F%2Flocalhost%2Fauth%2Fcallback
# Valid — Capacitor mobile (https://localhost)
GET /auth/login?redirect_url=https%3A%2F%2Flocalhost%2Fauth%2Fcallback
# Valid — Angular dev server
GET /auth/login?redirect_url=http%3A%2F%2Flocalhost%3A4200%2Fauth%2Fcallback
# Valid — production SPA
GET /auth/login?redirect_url=https%3A%2F%2Fmyapp.example.com%2Fauth%2Fcallback
# Valid — native app relay (relative path, always accepted)
GET /auth/login?redirect_url=%2Fauth%2Fnative-relay
# Invalid — origin not in allowlist → 400
GET /auth/login?redirect_url=https%3A%2F%2Fevil.com%2Fauth%2Fcallback
# Invalid — wrong path → 400
GET /auth/login?redirect_url=https%3A%2F%2Flocalhost%2Fdashboard
# No redirect_url — falls back to OAUTH_FRONTEND_CALLBACK_URL (backward compatible)
GET /auth/loginGET /auth/callback
Handles OAuth callback after user authentication. Exchanges authorization code for tokens and creates a session.
Query Parameters:
code(required): Authorization code from OAuth providerstate(required): State parameter for CSRF validation
Response: 302 Redirect
- On success: redirects to the
redirect_urlthat was stored in the PKCE challenge (orOAUTH_FRONTEND_CALLBACK_URLif none was provided during login). - On error: redirects to the same target URL with
?error=<code>appended (see Dynamic Error Redirect below).
GET /auth/native-relay (v1.5.0+)
Relay endpoint for native mobile apps (Capacitor, React Native, etc.) completing an OAuth login flow. The native app's in-app browser is redirected here after GET /auth/callback sets the session cookie, and this endpoint performs a final redirect to the app's custom URL scheme so the native runtime can capture the session.
Query Parameters: None.
Cookie: Reads the oauth_session HTTP-only cookie set by GET /auth/callback.
Responses:
| Condition | Status | Location |
|-----------|--------|----------|
| OAUTH_NATIVE_APP_SCHEME configured, oauth_session cookie present | 302 | {scheme}://auth/callback?session={sessionId} |
| OAUTH_NATIVE_APP_SCHEME configured, no oauth_session cookie | 302 | {scheme}://auth/callback?error=no_session |
| OAUTH_NATIVE_APP_SCHEME not configured | 404 | — |
Examples:
# Session cookie present — native app receives the session ID
GET /auth/native-relay
Cookie: oauth_session=abc123
→ 302 com.sftech.ebuilder://auth/callback?session=abc123
# No session cookie — native app receives an error signal
GET /auth/native-relay
→ 302 com.sftech.ebuilder://auth/callback?error=no_session
# OAUTH_NATIVE_APP_SCHEME not configured
GET /auth/native-relay
→ 404 Not FoundNotes:
- The endpoint does not validate whether
sessionIdcorresponds to a live session. Session validity is the responsibility of the native app after it receives the deep link. - The
sessionIdvalue is URL-encoded before being appended to the redirect URL. - Use
redirect_url=/auth/native-relaywhen callingGET /auth/loginfrom a native app (relative paths are always accepted byvalidateRedirectUrl).
GET /auth/status
Returns current authentication status. Does not require authentication.
Response:
{
"authenticated": true,
"userId": "user-id",
"email": "[email protected]",
"name": "John Doe",
"roles": ["admin", "user"],
"expiresAt": "2024-12-31T23:59:59.000Z",
"timeRemaining": 3600000
}GET /auth/user
Returns current user information from session. Requires authentication.
Response:
{
"sub": "user-id",
"email": "[email protected]",
"name": "John Doe",
"roles": ["admin", "user"],
"expiresAt": "2024-12-31T23:59:59.000Z",
"timeRemaining": 3600000
}POST /auth/refresh
Refreshes access token using refresh token. Extends session lifetime. Requires authentication.
Response:
{
"message": "Tokens refreshed successfully",
"expiresAt": "2024-12-31T23:59:59.000Z",
"timeRemaining": 3600000
}POST /auth/logout
Ends user session by deleting from database and clearing cookie. If OAUTH_END_SESSION_ENDPOINT is configured, redirects to OAuth provider logout for SSO logout.
Response (without provider logout):
{
"message": "Logout successful"
}Response (with provider logout):
302 Redirect to OAuth provider logout endpoint
GET /auth/logout/callback
Handles OAuth provider logout callback. After the OAuth provider completes logout, they redirect back to this endpoint, which then redirects to the frontend.
Response: 302 Redirect to OAUTH_FRONTEND_CALLBACK_URL
Authentication Flow
Login Flow (Web)
- Frontend calls
GET /auth/login(optionally withredirect_urlquery parameter) - Backend validates
redirect_urlif provided (returns 400 on failure — does NOT fall back silently) - Backend generates PKCE challenge and stores the validated
redirect_urlalongside it - Backend redirects to OAuth provider
- User authenticates at OAuth provider
- OAuth provider redirects back to
GET /auth/callback?code=xxx&state=xxx - Backend exchanges code for tokens (with PKCE verifier)
- Backend retrieves the
redirect_urlstored in the PKCE challenge - Backend creates session in database
- Backend sets
oauth_sessioncookie (HTTP-only, SameSite=Lax) - Backend redirects browser to the stored
redirect_url(orOAUTH_FRONTEND_CALLBACK_URLif none was stored) - Frontend stores cookie and is now authenticated
Login Flow (Native App — v1.5.0+)
Use this flow when a Capacitor, React Native, or other native app opens an in-app browser to perform OAuth login and needs the session returned via a custom URL scheme deep link.
- Native app opens an in-app browser pointed at
GET /auth/login?redirect_url=%2Fauth%2Fnative-relay - Backend validates the relative path (always accepted) and stores it in the PKCE challenge
- Backend redirects to OAuth provider
- User authenticates at OAuth provider
- OAuth provider redirects to
GET /auth/callback?code=xxx&state=xxx - Backend exchanges code, creates session, sets
oauth_sessioncookie, and redirects to/auth/native-relay GET /auth/native-relayreads theoauth_sessioncookie and redirects to{OAUTH_NATIVE_APP_SCHEME}://auth/callback?session={sessionId}- Native app intercepts the deep link, extracts
sessionfrom query parameters, and stores it for subsequent API calls
Configuration required:
{
"OAUTH_NATIVE_APP_SCHEME": "com.sftech.ebuilder"
}Capacitor example (TypeScript):
import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';
// 1. Open in-app browser to start OAuth flow
await Browser.open({
url: 'https://api.example.com/api/auth/login?redirect_url=%2Fauth%2Fnative-relay',
});
// 2. Listen for the deep link returned by /auth/native-relay
App.addListener('appUrlOpen', async (event) => {
// event.url === 'com.sftech.ebuilder://auth/callback?session=abc123'
const url = new URL(event.url);
const sessionId = url.searchParams.get('session');
const error = url.searchParams.get('error');
await Browser.close();
if (error) {
console.error('Login failed:', error);
return;
}
if (sessionId) {
// Store sessionId and use it as the oauth_session cookie value
// in subsequent API requests (e.g. via Capacitor HTTP plugin or Axios)
await storage.set('sessionId', sessionId);
router.push('/home');
}
});Logout Flow
- Frontend calls
POST /auth/logout - Backend deletes session from database
- Backend clears
oauth_sessioncookie - If
OAUTH_END_SESSION_ENDPOINTconfigured:- Backend redirects to OAuth provider logout with
id_token_hint - OAuth provider clears SSO session
- OAuth provider redirects back to
GET /auth/logout/callback(backend) - Backend redirects to
OAUTH_FRONTEND_CALLBACK_URL(frontend)
- Backend redirects to OAuth provider logout with
- Otherwise: Returns JSON success response
Session Validation
Other microservices can validate sessions by calling GET /auth/status with the session cookie.
See @sftech/nestjs-auth for client library to validate sessions from microservices.
Dynamic Error Redirect (v1.4.0+)
When OAuth callback processing fails (e.g. invalid authorization code, expired state, provider error), the backend must redirect the browser to an error page rather than returning a JSON error body because the response is a browser redirect flow.
Behavior:
- If the login was initiated with a
redirect_url, that URL is stored in the PKCE challenge alongside the code verifier. - On callback error, the stored
redirect_urlis retrieved from the thrown exception and used as the redirect target. - The error code is appended as a
?error=<code>query parameter. - If no
redirect_urlwas stored (login was initiated without one), the fallback isOAUTH_FRONTEND_CALLBACK_URL.
Error codes:
| Code | Condition |
|------|-----------|
| auth_failed | Invalid credentials, expired session, unauthorized |
| invalid_request | Missing required parameters (code, state) |
| auth_error | Unexpected internal error |
Example redirect on error:
# Login initiated with redirect_url=https://localhost/auth/callback
# Callback fails with invalid code
→ 302 https://localhost/auth/callback?error=auth_failed
# Login initiated without redirect_url
# Callback fails
→ 302 {OAUTH_FRONTEND_CALLBACK_URL}?error=auth_failedFrontend handling example:
// Angular component at /auth/callback
import { ActivatedRoute } from '@angular/router';
export class AuthCallbackComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
const error = this.route.snapshot.queryParamMap.get('error');
if (error) {
// Handle error — e.g. show message or redirect to login page
console.error('Authentication failed:', error);
}
// Otherwise: authenticated, proceed to application
}
}Open-Redirect Protection (v1.4.0+)
The library enforces OWASP CWE-601 open-redirect protection on all redirect_url values before they are stored. Validation happens at the presentation layer in AuthController.validateRedirectUrl().
Allowlist configuration:
OAUTH_ALLOWED_REDIRECT_ORIGINS accepts a comma-separated list of origins. Each entry can be either a bare origin (https://example.com) or a full URL (the origin is extracted automatically).
{
"OAUTH_ALLOWED_REDIRECT_ORIGINS": "https://localhost,http://localhost:4200,https://myapp.example.com"
}Fallback when OAUTH_ALLOWED_REDIRECT_ORIGINS is not set:
The library automatically derives one allowed origin from OAUTH_FRONTEND_CALLBACK_URL. This preserves backward compatibility for deployments with a single frontend origin.
{
"OAUTH_FRONTEND_CALLBACK_URL": "http://localhost:4200/auth/callback"
// Derived allowed origin: http://localhost:4200
}Multi-origin deployment example (Capacitor + dev + prod):
{
"OAUTH_FRONTEND_CALLBACK_URL": "https://myapp.example.com/auth/callback",
"OAUTH_ALLOWED_REDIRECT_ORIGINS": "https://localhost,http://localhost:4200,https://myapp.example.com"
}This configuration permits all three frontends to pass a redirect_url:
https://localhost/auth/callback— Capacitor mobile (Ionic/Capacitor useshttps://localhost)http://localhost:4200/auth/callback— Angular development serverhttps://myapp.example.com/auth/callback— production SPA
Attack surface covered:
| Attack | Protection |
|--------|-----------|
| Redirect to unrelated domain (https://evil.com) | Origin must be in allowlist |
| Subdomain hijack (https://localhost.evil.com) | Exact origin match, not substring |
| Path traversal (https://localhost/admin) | Path must be exactly /auth/callback |
| Parameter injection (https://localhost/auth/callback?token=x) | No query parameters allowed |
| Protocol-relative redirect (//evil.com/auth/callback) | Must not start with // |
Session Management
Sessions are stored in the database using TypeORM. The session entity includes:
sessionId: Unique session identifier (stored in cookie)userId: User ID from OAuth provideraccessToken: OAuth access tokenrefreshToken: OAuth refresh token (optional)idToken: OIDC ID token (used for provider logout)expiresAt: Session expiration timestampuserInfo: Cached user information (email, name, roles)
Session Modes
- Single mode (
OAUTH_SESSION_MODE=single): Only one active session per user. New login invalidates previous session. - Multi mode (
OAUTH_SESSION_MODE=multi): Multiple concurrent sessions allowed per user.
Architecture
This library follows Clean Architecture principles:
lib/
├── domain/ # Core business models and interfaces
│ ├── models/ # Session, UserInfo, PKCEChallenge domain models
│ ├── repositories/ # Repository interfaces (ports)
│ └── oauth-options.interface.ts
├── application/ # Use cases and business logic
│ └── usecases/ # Login, Callback, Logout, Refresh, etc.
├── infrastructure/ # External concerns
│ ├── entities/ # TypeORM database entities
│ ├── repositories/ # Repository implementations
│ └── mappers/ # Domain ↔ Entity mapping
└── presentation/ # REST API layer
├── controllers/ # AuthController
├── dtos/ # Request/Response DTOs
└── guards/ # SessionGuardBuilding
nx build nestjs-auth-backendPublishing
# Build first
nx build nestjs-auth-backend
# Navigate to dist folder
cd dist/libs/nestjs-auth-backend
# Publish to npm
npm publish --access publicBase Classes & Interfaces
IAuthBundle
Interface for the main authentication bundle containing all auth use cases.
Provides:
initiateLogin- Start OAuth login flow (accepts optionalredirectUrl)handleCallback- Process OAuth callback (returns optionalredirectUrlin output)getAuthStatus- Get current auth statusgetUserInfo- Get current user inforefreshToken- Refresh tokenslogout- End sessioncreateAnonymousSession- Create anonymous sessionrecoverAnonymousSession- Recover anonymous session by recovery code
Usage: This is typically used internally by the module. Access it if you need custom auth flows.
IApiKeyBundle
Interface for API key management bundle.
Provides:
createApiKeyUsecase- Create new API keyvalidateApiKeyUsecase- Validate an API keyrevokeApiKeyUsecase- Revoke an API keylistApiKeysUsecase- List all API keys for a user
Usage example:
import { IApiKeyBundle } from '@sftech/nestjs-auth-backend';
@Controller('api-keys')
@UseGuards(JwtAuthGuard)
export class ApiKeyController {
constructor(
@Inject('IApiKeyBundle')
private readonly apiKeyBundle: IApiKeyBundle,
) {}
@Post()
async create(@Body() dto: CreateApiKeyDto, @CurrentUser() user: UserInfo) {
return this.apiKeyBundle.createApiKeyUsecase.execute({
userId: user.sub,
name: dto.name,
scopes: dto.scopes,
});
}
@Get()
async list(@CurrentUser() user: UserInfo) {
return this.apiKeyBundle.listApiKeysUsecase.execute({ userId: user.sub });
}
@Delete(':id')
async revoke(@Param('id') id: string) {
return this.apiKeyBundle.revokeApiKeyUsecase.execute({ keyId: id });
}
}ISessionRepository
Repository interface for session management.
Methods:
create(session: Session): Promise<Session>- Create new sessionfindById(sessionId: string): Promise<Session | null>- Find session by IDfindByUserId(userId: string): Promise<Session[]>- Find all sessions for userupdate(session: Session): Promise<Session>- Update sessiondelete(sessionId: string): Promise<void>- Delete sessiondeleteAllForUser(userId: string): Promise<void>- Delete all sessions for user
Usage: Implement this interface if you need custom session storage (e.g., Redis).
IUserRepository
Repository interface for user management.
Extends: IBaseDbRepository<User>
Additional methods:
findByEmail(email: string): Promise<User | null>findByOAuthSub(oauthSub: string): Promise<User | null>
IOAuthOptions
Configuration interface for OAuth settings. Populated automatically from app.config.json by OAuthOptionsMapper.
Properties:
interface IOAuthOptions {
providerUrl: string;
authorizationEndpoint: string;
tokenEndpoint: string;
userinfoEndpoint: string;
endSessionEndpoint?: string;
clientId: string;
clientSecret: string;
redirectUri: string; // OAuth callback URL (this service's own endpoint)
frontendCallbackUrl?: string; // Default post-login redirect target
scope: string;
sessionMode: 'single' | 'multi';
sessionMaxAge: number;
anonymousSessionMaxAge?: number;
allowedRedirectOrigins?: string[]; // Parsed from OAUTH_ALLOWED_REDIRECT_ORIGINS (v1.4.0+)
nativeAppScheme?: string; // Custom URL scheme for native app deep linking (v1.5.0+)
}allowedRedirectOrigins notes:
- Populated by splitting
OAUTH_ALLOWED_REDIRECT_ORIGINSon commas and trimming whitespace. - Each entry is normalized to a bare origin at validation time (e.g.
https://example.com/some/pathbecomeshttps://example.com). - When
undefinedor empty, the origin offrontendCallbackUrlis used as the single allowed origin.
nativeAppScheme notes (v1.5.0+):
- Populated from
OAUTH_NATIVE_APP_SCHEMEinapp.config.json. - When
undefined,GET /auth/native-relayreturns 404. - The value is used verbatim to build the redirect URL:
{nativeAppScheme}://auth/callback?session={sessionId}.
IApiKeyOptions
Configuration interface for API key settings.
Properties:
interface IApiKeyOptions {
hashAlgorithm: string; // e.g., 'sha256'
keyPrefix: string; // e.g., 'sk_'
keyLength: number; // e.g., 32
}PKCEChallenge
Domain model for PKCE (Proof Key for Code Exchange, RFC 7636) challenges.
Constructor parameters:
codeVerifier: string- Random string, 43–128 characterscodeChallenge: string- SHA-256 of codeVerifier, base64url-encodedstate: string- CSRF protection token, minimum 32 characterscreatedAt: Date- Creation timestamp (used for 5-minute TTL)redirectUrl?: string- Optional post-login redirect URL stored alongside the challenge (v1.4.0+)
Methods:
isValid(): boolean- Returnstrueif the challenge is less than 5 minutes oldisExpired(): boolean- Returnstrueif the challenge is 5 minutes old or older
Note: PKCE challenges are stored in an in-memory cache (InitiateLoginUseCase.pkceCache) with a 5-minute TTL. When the challenge expires, any stored redirectUrl is also lost; the error redirect falls back to frontendCallbackUrl in that case.
Extending the Module
Custom Session Storage
To use Redis or another storage backend:
import { ISessionRepository } from '@sftech/nestjs-auth-backend';
import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';
@Injectable()
export class RedisSessionRepository implements ISessionRepository {
constructor(private readonly redis: Redis) {}
async create(session: Session): Promise<Session> {
await this.redis.setex(
`session:${session.sessionId}`,
session.maxAge / 1000,
JSON.stringify(session),
);
return session;
}
async findById(sessionId: string): Promise<Session | null> {
const data = await this.redis.get(`session:${sessionId}`);
return data ? JSON.parse(data) : null;
}
// ... implement other methods
}
// Register in module
@Module({
providers: [
{
provide: 'ISessionRepository',
useClass: RedisSessionRepository,
},
],
})
export class CustomAuthModule {}Changelog
See CHANGELOG.md
License
MIT
