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 errorMeaningRecommended action
missing_tokenNo Authorization header was sentRedirect to sign-in
expired_tokenToken signature is valid but past expiryAttempt silent refresh, then redirect
invalid_tokenToken is malformed, tampered, or uses an unknown keyRedirect 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 an API token lacks the scope @RequirePrivilege asks for (API tokens only), or a flag is off for the caller:

API-token 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:

CodeMeaning
TOKEN_EXPIREDJWT has expired
TOKEN_INVALIDJWT is malformed or invalid; on the API-token path, introspection reported the token inactive (revoked, expired, forged) or not an API token
JWKS_NO_MATCHNo matching key found in JWKS endpoint
CLAIM_VALIDATION_FAILEDToken claim validation failed (e.g., wrong issuer or audience)
APP_MISMATCHAPI token was issued for a different app ID
UNKNOWN_ERRORUnexpected verification error (including a failed introspection request)

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

Error CodeRFC 6750 ErrorDescription
TOKEN_EXPIREDexpired_tokenThe access token has expired
TOKEN_INVALIDinvalid_tokenThe access token is invalid
JWKS_NO_MATCHinvalid_tokenThe access token signature could not be verified
CLAIM_VALIDATION_FAILEDinvalid_tokenThe access token claim validation failed
APP_MISMATCHinvalid_tokenThe 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'
  }
}