Authentication

StreetJS provides a complete, production-grade authentication system out of the box. It covers every layer of a modern auth stack — JWT access tokens, server-side sessions, API keys, OAuth2/OIDC with PKCE, WebAuthn passkeys, refresh tokens, and role-based access control (RBAC). No external auth service required.

Overview

Feature Class / Helper Guide
JWT access tokens JwtService below
Server-side sessions SessionManager below
API key authentication ApiKeyService, apiKeyMiddleware below
OAuth2 + PKCE OAuthManager OAuth2 Guide
WebAuthn / Passkeys WebAuthnService WebAuthn Guide
Refresh tokens RefreshTokenService below
RBAC RbacService, @Roles(), @Permissions() RBAC Guide

JWT

JwtService provides HMAC-SHA256 or RS256 signed tokens with expiry, subject, and arbitrary claims.

1
2
3
4
5
6
7
8
9
10
import { JwtService } from 'streetjs';

const jwt = new JwtService({ secret: process.env.JWT_SECRET! });

// Sign a token (expires in 15 min by default)
const token = jwt.sign({ sub: user.id, roles: user.roles }, { expiresIn: '15m' });

// Verify and decode
const payload = jwt.verify(token);
// → { sub: '...', roles: [...], iat: ..., exp: ... }

Use authMiddleware to automatically validate Bearer tokens on every request:

1
2
3
4
import { authMiddleware } from 'streetjs';

app.use(authMiddleware(jwt));
// ctx.user is now populated for authenticated requests

Sessions

For cookie-based server sessions, use SessionManager. It encrypts session data with AES-256-GCM using a key derived from SESSION_KEY.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { SessionManager } from 'streetjs';

const sessions = new SessionManager({ secret: process.env.SESSION_KEY! });

// In a login route:
await sessions.set(ctx, { userId: user.id, roles: user.roles });

// In a protected route:
const session = await sessions.get(ctx);
if (!session) throw new UnauthorizedException();

// Logout:
await sessions.destroy(ctx);

Sessions are stored in a signed, encrypted cookie. The session data never leaves the browser unencrypted.


API Keys

ApiKeyService generates and verifies long-lived API keys suitable for machine-to-machine authentication. Keys are hashed in the database (SHA-256 + prefix storage) and never stored in plaintext.

Setup

Run the migration first:

1
2
3
4
import { API_KEYS_MIGRATION_SQL, ApiKeyService } from 'streetjs';

await pool.query(API_KEYS_MIGRATION_SQL);
const apiKeys = new ApiKeyService(pool);

Generate a key

1
2
3
4
5
6
7
8
9
const { key, record } = await apiKeys.generate({
  ownerId: 'user-123',
  name: 'Production API Key',
  expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),
});

// `key` is the plaintext key — show to user ONCE and never store it.
// `record` contains the database row (id, prefix, ownerId, createdAt).
console.log('Your API key:', key); // sk_live_xxxxxxxx...

Verify a key (middleware)

Use the built-in apiKeyMiddleware:

1
2
3
4
import { apiKeyMiddleware } from 'streetjs';

app.use(apiKeyMiddleware(apiKeys));
// Sets ctx.user if the X-API-Key header contains a valid key

Revoke a key

1
await apiKeys.revoke(record.id);

Refresh Tokens

RefreshTokenService implements single-use refresh token rotation with replay detection. Stolen tokens are automatically invalidated via the TokenReplayError mechanism.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import {
  RefreshTokenService,
  REFRESH_TOKENS_MIGRATION_SQL,
  TokenReplayError,
} from 'streetjs';

await pool.query(REFRESH_TOKENS_MIGRATION_SQL);
const refreshSvc = new RefreshTokenService(pool, { ttlMs: 7 * 24 * 60 * 60 * 1000 });

// On login — issue both tokens:
const accessToken = jwt.sign({ sub: user.id });
const refreshToken = await refreshSvc.issue(user.id);

// On token refresh endpoint:
try {
  const newRefresh = await refreshSvc.rotate(incomingRefreshToken);
  const newAccess = jwt.sign({ sub: userId });
  ctx.json({ accessToken: newAccess, refreshToken: newRefresh });
} catch (err) {
  if (err instanceof TokenReplayError) {
    // Token was already used — revoke all tokens for this user
    await refreshSvc.revokeAll(userId);
    throw new UnauthorizedException('Token reuse detected');
  }
  throw err;
}

RBAC

See the dedicated RBAC Guide for full documentation on @Roles(), @Permissions(), RoleHierarchy, and rbacGuard.


OAuth2

See the dedicated OAuth2 Guide for full documentation on OAuthManager, PKCE flow, and provider configuration.


WebAuthn / Passkeys

See the dedicated WebAuthn Guide for full documentation on passkey registration and authentication.


Security Checklist

  • Rotate JWT_SECRET and SESSION_KEY via your secrets manager (see Cloud Secret Providers).
  • Set expiresIn to ≤15 minutes for access tokens; use refresh tokens for long sessions.
  • Enable securityHeaders middleware to set HSTS, CSP, and other security headers.
  • Use requireRoles guards on all admin routes.
  • Store API keys hashed — never log or return the plaintext key after generation.

Table of contents