Skip to content

The BridgeAuthGuard returns RFC 6750-compliant WWW-Authenticate response headers so your frontend can distinguish between error types and handle them appropriately.

| WWW-Authenticate error | Meaning | Recommended action | |---|---|---| | missing_token | No Authorization header was sent | Redirect to sign-in | | expired_token | Token signature is valid but past expiry | Attempt silent refresh, then redirect | | invalid_token | Token is malformed, tampered, or uses an unknown key | Redirect to sign-in |

This example uses the auth object from @nebulr-group/bridge-svelte (a lazy proxy to the BridgeAuth singleton); @nebulr-group/bridge-react exposes the same capabilities through its useBridgeToken() hook (getAccessToken, login, logout).

// src/lib/api.ts
import { auth } from '@nebulr-group/bridge-svelte';

async function apiFetch(endpoint: string, options: RequestInit = {}) {
  const token = auth.getTokens()?.accessToken;

  const response = await fetch(`http://localhost:3000${endpoint}`, {
    ...options,
    headers: {
      ...options.headers,
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      'Content-Type': 'application/json',
    },
  });

  if (response.status === 401) {
    const wwwAuth = response.headers.get('WWW-Authenticate') ?? '';

    if (wwwAuth.includes('expired_token')) {
      try {
        await auth.refreshTokens();
        const newToken = auth.getTokens()?.accessToken;
        return fetch(`http://localhost:3000${endpoint}`, {
          ...options,
          headers: {
            ...options.headers,
            Authorization: `Bearer ${newToken}`,
            'Content-Type': 'application/json',
          },
        }).then((r) => r.json());
      } catch {
        await auth.login();
        return;
      }
    }

    // missing_token or invalid_token: send the user back to sign in
    await auth.login();
    return;
  }

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return response.json();
}

Returned when token is missing, expired, or invalid. Includes RFC 6750 WWW-Authenticate header:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="expired_token", error_description="The access token has expired"
{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "The access token has expired"
}

Auth type rejection (e.g., API token sent to a @AcceptAuth('jwt') endpoint):

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "auth type not accepted"
}

Returned when authenticated but lacking required role, privilege, or feature flag:

Role check failed:

{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Role 'ADMIN' required"
}

Privilege check failed:

{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Privilege 'USER_READ' required"
}

Feature flag not enabled:

{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Feature flag 'beta-access' is not enabled"
}

Thrown by JwksService when token verification fails. Error codes:

| Code | Meaning | |------|---------| | TOKEN_EXPIRED | JWT has expired | | TOKEN_INVALID | JWT is malformed or invalid; on the API-token path, introspection reported the token inactive (revoked, expired, forged) or not an API token | | JWKS_NO_MATCH | No matching key found in JWKS endpoint | | CLAIM_VALIDATION_FAILED | Token claim validation failed (e.g., wrong issuer or audience) | | APP_MISMATCH | API token was issued for a different app ID | | UNKNOWN_ERROR | Unexpected verification error (including a failed introspection request) |

These are mapped to RFC 6750 WWW-Authenticate errors by the guard:

| Error Code | RFC 6750 Error | Description | |------------|----------------|-------------| | TOKEN_EXPIRED | expired_token | The access token has expired | | TOKEN_INVALID | invalid_token | The access token is invalid | | JWKS_NO_MATCH | invalid_token | The access token signature could not be verified | | CLAIM_VALIDATION_FAILED | invalid_token | The access token claim validation failed | | APP_MISMATCH | invalid_token | The access token was issued for a different application |

Thrown by BridgeHttpService when a downstream HTTP call returns a non-2xx response:

class BridgeHttpError extends Error {
  readonly status: number;  // HTTP status code
  readonly url: string;     // Request URL
}
try {
  await this.bridgeHttp.get('http://service-b/data', token);
} catch (error) {
  if (error instanceof BridgeHttpError) {
    console.log(error.status); // e.g., 404
    console.log(error.url);    // 'http://service-b/data'
  }
}