Backend for Frontend

How the IAM service exposes protected endpoints for BFF proxies, including authorization checks, token metadata with rotation hints, operational configuration, and the full middleware security chain that gates every BFF call.

The IAM service supports the Backend for Frontend or "BFF" pattern natively. Instead of exposing JWT tokens to the browser, a server-side proxy sits between the client and the IAM service. The proxy holds tokens in httpOnly cookies, rotates them transparently, and relays authorization decisions back to the frontend. The browser never sees a raw token.

The service provides three endpoints dedicated to BFF communication:

  • GET /secret/data answers "is this user authorized?" and returns the user's roles.
  • GET /secret/accesstoken/metadata answers "how long until this token expires?" and computes a proactive rotation hint.
  • GET /operational/config shares the cookie domain and access token TTL so the proxy can set cookies correctly.

Both /secret routes run behind the full middleware chain: access token extraction, refresh token validation, device fingerprinting, checkForActiveMfa, and anomaly-aware JWT verification. The metadata route adds a strict cookie-only guard. The /operational/config endpoint is gated by IP address instead, and the service can also apply HMAC globally.

auth-h3client is the companion Nuxt module that implements the proxy side of the pattern. It consumes the three IAM endpoints above and handles login, signup, logout, OAuth, MFA relay, CSRF, token rotation, and user data caching automatically. it can also be used as h3 / nitro client directly. See the auth-h3client documentation for its full API reference, configuration, and composables.

You can use any http client for this pattern

The pattern

In a traditional SPA, the browser stores access and refresh tokens in localStorage, sessionStorage, or cookies and sends them directly to resource APIs. This exposes tokens to XSS attacks, browser extensions, and third-party scripts. The BFF pattern eliminates this exposure.

Browser sends a request to the BFF

The browser makes a request to the application server. The only credentials the browser carries are httpOnly, Secure, SameSite=Strict cookies that it cannot read or modify via JavaScript.

BFF extracts cookies and calls the IAM service

The BFF reads the session (refresh token), __Secure-a (access token), and canary_id (session binding) cookies from the incoming request. It forwards them to the IAM service as part of a server-to-server call, adding HMAC signature headers when configured.

IAM validates and responds

The IAM service runs the full middleware chain on the forwarded request: access token verification, refresh token verification, device fingerprinting, and anomaly detection. It responds with the authorization decision, user roles, and, for the metadata route, token TTL information.

BFF relays the decision

The BFF interprets the IAM response. If the user is authorized, the BFF proceeds with the application logic. If tokens need rotation, the BFF calls POST /auth/user/refresh-session and forwards the new Set-Cookie headers to the browser. If MFA is required, the BFF relays the challenge to the browser.


IAM service routes

The IAM service exposes two BFF-specific routes under the /secret prefix and one configuration endpoint under /operational. These routes are mounted after the authentication, token rotation, and magic link route groups in the Express middleware chain.

// service.ts (simplified mounting order)
app.use(authenticationRoutes)     // /signup, /login, /auth/OAuth/*
app.use(tokenRotationRoutes)      // /auth/user/refresh-session, /auth/logout
app.use(magicLinks)               // /auth/verify-mfa/*, /auth/reset-password/*
app.use(allowBff)                 // /secret/data, /secret/accesstoken/metadata
app.use(apiProtectedRoutes())     // /api/manage/*
app.use('/operational/config', sendOperationalConfig)

Route table

MethodPathMiddleware chainControllerPurpose
GET/secret/datarequireAccessToken, requireRefreshToken, getFingerPrint, checkForActiveMfa, protectRouteallowBffAccessAuthorization check
GET/secret/accesstoken/metadatarequireAccessToken, requireRefreshToken, getFingerPrint, checkForActiveMfa, protectRoute, acceptCookieOnlygetAccessTokenPayloadToken TTL and rotation hint
GET/operational/configIP restriction (no middleware chain)sendOperationalConfigCookie domain and token TTL

Middleware chain

Every request to /secret/data and /secret/accesstoken/metadata passes through five shared middleware functions before reaching the controller. The metadata route adds a sixth guard. Each middleware either populates request context for downstream handlers or terminates the request with an error.

requireAccessToken

Extracts the JWT access token from the Authorization header. The header must use the Bearer scheme. If the header is missing, empty, or uses a different scheme, the middleware responds with HTTP 401 and { ok: false, error: "Missing Bearer token" }. On success, it stores the raw token string on req.token for the next middleware to verify.

requireRefreshToken

Reads the session cookie from the request. If the cookie is absent, the middleware responds with HTTP 401 and { error: "Refresh token missing" }. It does not validate the token's contents or hash; that responsibility belongs to rotation endpoints. Its purpose here is to confirm that the caller holds a session cookie, which proves the request comes from a context where the user has logged in.

getFingerPrint

Extracts device and network metadata from the request headers and attaches a fingerprint to the request context. This includes the raw User-Agent, the resolved IP address, geolocation data, and parsed browser and device details. The fingerprint is consumed by protectRoute for anomaly scoring. See Fingerprinting for the full list of signals collected.

checkForActiveMfa

Hashes the session cookie and checks anomaliesCache() for an unresolved MFA state. If the current session already has a resolvable MFA challenge, the middleware returns 202 { mfa: true }. If the cached anomaly is not resolvable, it returns 401 { error: 'Re-login is required', ... }. Otherwise it calls next() and lets protectRoute continue.

protectRoute

The core security layer. It performs two operations:

  1. JWT verification. Calls verifyAccessToken with the token from req.token. The verification uses a cache-backed lookup: the SHA-256 hash of the token is checked against stored access token records. If the token is revoked, expired, or not found, the middleware returns HTTP 401.
  2. Anomaly detection. Calls strangeThings() with the verified token data and the fingerprint from getFingerPrint. This function runs a series of checks:
    CheckTriggerResult
    Token not foundSHA-256 hash does not match any stored recordHTTP 401, invalid token
    Token revokedToken's valid flag is false or usage_count > 1HTTP 401, invalid token
    Canary mismatchcanary_id cookie does not match the session recordHTTP 401 + MFA challenge
    Idle sessionUser's last_seen exceeds 24 hoursHTTP 401 + MFA challenge
    Session overflowActive sessions exceed maxAllowedSessionsPerUserHTTP 401 + MFA challenge
    Token spam3 or more tokens issued within 10 minutesRevoke all tokens, HTTP 401
    IP range mismatchClient IP falls outside the known range for this sessionHTTP 401 + MFA challenge
    High suspicion scoreSuspicion score reaches 25% of the ban thresholdHTTP 401 + MFA challenge
    Proxy/hosting detectedIP identified as proxy or hosting and user is not allowlistedHTTP 401 + MFA challenge
    Device fingerprint mismatchBrowser, OS, or location differs from the session baselineHTTP 401 + MFA challenge

    When an anomaly triggers an MFA challenge, the IAM service responds with HTTP 202 and sends a magic link email. The BFF is expected to relay this 202 to the browser so the user can verify their identity. Anomalies that trigger within the MFA bypass window are skipped.

On success, protectRoute populates req.user with:

FieldTypeDescription
userIdstringThe sub claim from the JWT
visitor_idstringThe visitor claim from the JWT
accessTokenIdstringThe jti claim (JWT ID)
rolesstring[]The roles claim from the JWT (may be undefined)
payloadJwtPayloadThe full decoded JWT payload

acceptCookieOnly (metadata route only)

The metadata route adds an extra guard after protectRoute. This middleware enforces a strict contract: the request must carry cookies and nothing else. It rejects the request under any of these conditions:

ConditionStatusResponse
session cookie is missing401{ error: "Refresh token missing" }
Request has a body (parsed JSON, content-length > 0, or transfer-encoding: chunked)400{ error: "Request body not allowed" }
Query string is present400{ error: "Query string not allowed" }
Content-Type header is set400{ error: "Content-Type not allowed" }

This prevents injection vectors on an endpoint whose only input should be the cookies attached by the browser. No body, query parameters, or content-type declarations are legitimate for this route.


Authorization check: GET /secret/data

The allowBffAccess controller validates that the upstream middleware successfully populated req.user, then confirms that the user and their visitor record exist in the database. If everything checks out, it returns a JSON payload with the authorization decision.

Response shape

{
  "userId": 42,
  "authorized": true,
  "ipAddress": "203.0.113.10",
  "userAgent": "Mozilla/5.0 ...",
  "date": "2025-01-15T10:30:00.000Z",
  "roles": ["admin", "editor"]
}
userId
number
The authenticated user's database ID, extracted from the JWT sub claim.
authorized
boolean
Always true in a successful response. The BFF proxy uses this flag to decide whether to proceed with the application request.
ipAddress
string
The client IP address as seen by the IAM service. When the BFF forwards X-Forwarded-For, this reflects the original browser IP.
userAgent
string
The User-Agent header value from the request.
date
string
ISO 8601 timestamp of when the response was generated.
roles
string[] | string
The roles embedded in the access token. Returns the array of role strings when roles are present, or "No roles added with this token." when the token carries no roles.

Error responses

StatusBodyCause
200{ authorized: true, ... }User and visitor exist, middleware chain passed
202MFA challenge (from protectRoute)Anomaly detected, magic link sent
401{ authorized: false, reason: "Not authenticated" }req.user was not populated by upstream middleware
404{ authorized: false, reason: "Not found" }User or visitor record does not exist in the database
500Logged internallyDatabase query failure. The controller catches the error and logs it without exposing details
The controller trusts that protectRoute has already verified the JWT, run anomaly detection, and populated req.user. It does not re-validate the token. The database lookups serve as a final consistency check: the user and visitor must still exist at query time.

Observability

Every request to this endpoint produces a structured log entry with the client IP, request ID, user agent, original URL, and canary_id cookie value. Successful authorizations log at info level. Failed attempts log at warn level with the specific reason. Database errors log at error level without exposing the error details in the response.


Token metadata: GET /secret/accesstoken/metadata

The getAccessTokenPayload controller returns the decoded JWT payload along with computed TTL information. The BFF proxy uses this data to decide whether to proactively rotate the access token before it expires.

Response shape

{
  "authorized": true,
  "ipAddress": "203.0.113.10",
  "userAgent": "Mozilla/5.0 ...",
  "date": "2025-01-15T10:30:00.000Z",
  "roles": ["admin"],
  "payload": {
    "sub": "42",
    "visitor": "v_abc123",
    "jti": "tok_xyz789",
    "roles": ["admin"],
    "iat": 1736934600,
    "exp": 1736935500,
    "aud": "example.com",
    "iss": "example.com"
  },
  "msUntilExp": 540000,
  "refreshThreshold": 225000,
  "shouldRotate": false
}
authorized
boolean
Always true in a successful response.
payload
JwtPayload
The full decoded JWT payload as attached by protectRoute. Contains sub (user ID), visitor (visitor ID), jti (token ID), roles, iat, exp, aud, and iss.
msUntilExp
number
Milliseconds remaining until the access token expires. Computed as Math.max(0, expMs - Date.now()). Returns 0 when the token has already expired.
refreshThreshold
number
The threshold in milliseconds below which the BFF should trigger a rotation. Computed as 25% of the configured access token TTL.
shouldRotate
boolean
true when msUntilExp <= refreshThreshold. The BFF uses this flag to trigger proactive rotation by calling POST /auth/user/refresh-session.

TTL computation

The controller computes timing values from the JWT's iat and exp claims combined with the configured access token TTL:

TTL_MS     = config.jwt.access_tokens.expiresInMs ?? 900000   (default: 15 minutes)
THRESHOLD  = TTL_MS * 0.25                                    (25% of TTL)
iatMs      = payload.iat * 1000                               (or Date.now() if missing)
expMs      = payload.exp * 1000                               (or iatMs + TTL_MS if missing)
msUntilExp = max(0, expMs - now)
shouldRotate = msUntilExp <= THRESHOLD

When a 15-minute access token has less than 3 minutes and 45 seconds remaining, shouldRotate becomes true. The BFF can then call POST /auth/user/refresh-session to obtain fresh tokens before the current ones expire.

The 25% threshold is not configurable. It provides a balance between minimizing unnecessary rotations and ensuring the token is refreshed well before expiry. A 15-minute TTL triggers rotation in the last 3:45. A 5-minute TTL triggers rotation in the last 1:15.

Error responses

StatusBodyCause
200{ authorized: true, payload, msUntilExp, ... }Token valid, metadata computed
202MFA challenge (from protectRoute)Anomaly detected, magic link sent
401{ authorized: false, reason: "Not authenticated" }req.user was not populated by upstream middleware
404{ authorized: false, reason: "Not found" }User or visitor record does not exist in the database
500{ authorized: false, reason: "Server error" }Database query failure

How the BFF uses this endpoint

A typical BFF proxy calls this endpoint on every authenticated request to determine whether the access token is still usable or needs rotation:

Call the metadata endpoint

The BFF sends GET /secret/accesstoken/metadata with the access token as a Bearer header and the session and canary_id cookies.

Read the shouldRotate flag

If shouldRotate is false, the access token is still fresh. The BFF proceeds with the application request using the current token.

Trigger rotation when needed

If shouldRotate is true, the BFF calls POST /auth/user/refresh-session on the IAM service, forwarding the session and canary_id cookies. The IAM service validates the refresh token, generates new tokens, and returns them.

Forward new cookies

After rotation, the BFF sets the new __Secure-a, session, and a-iat cookies on the browser response. The browser transparently picks up the refreshed tokens on the next request.

The BFF can cache the metadata response to avoid calling this endpoint on every single request. A safe cache TTL is msUntilExp - refreshThreshold - 5000 (5-second safety margin for network latency). This ensures the cache entry expires just before the rotation window opens.

Operational configuration: GET /operational/config

The IAM service exposes a non-authenticated endpoint that returns runtime settings the BFF needs for cookie management. This endpoint does not use the middleware chain. Instead, it is gated by IP address: only requests from the configured trusted client IP receive a response.

IP validation

The controller reads the raw socket address via req.socket.remoteAddress, strips the IPv4-mapped IPv6 prefix (::ffff:) if present, and compares the result against config.service.clientIp. If that field is not set, it falls back to config.service.proxy.ipToTrust. Any mismatch returns HTTP 403 with { error: "Forbidden" }.

The comparison uses req.socket.remoteAddress (the physical TCP connection address), not req.ip or X-Forwarded-For. This means the IP restriction cannot be bypassed by spoofing forwarded headers. The BFF must connect directly from the trusted IP.

Response shape

{
  "domain": ".example.com",
  "accessTokenTTL": 900000
}
domain
string
The cookie domain from config.jwt.refresh_tokens.domain. The BFF uses this value as the Domain attribute when setting the refresh token cookie on the browser.
accessTokenTTL
number
The access token lifetime in milliseconds from config.jwt.access_tokens.expiresInMs (defaults to 900000, which is 15 minutes). The BFF uses this as the maxAge for the __Secure-a cookie.

The BFF typically calls this endpoint once at startup and caches the result. Since these values change only when the IAM service configuration changes, a 24-hour cache TTL is reasonable.


Exports for library consumers

The BFF routes and controllers are exported individually from the @riavzon/auth package. This allows library consumers to mount them in their own Express applications, compose them with additional middleware, or use the controllers standalone.

import {
  bffAccessRoute,
  allowBffAccess,
  getAccessTokenPayload,
  sendOperationalConfig
} from '@riavzon/auth'
ExportTypeDescription
bffAccessRouteRouterThe full router with both /secret/data and /secret/accesstoken/metadata routes, including all middleware
allowBffAccessRequestHandlerThe standalone authorization controller. Can be mounted with custom middleware chains
getAccessTokenPayloadRequestHandlerThe standalone metadata controller. Expects req.user to be populated by upstream middleware
sendOperationalConfigRequestHandlerThe operational config controller. Handles its own IP validation internally

The supporting middleware functions are also exported for building custom pipelines:

import {
  requireAccessToken,
  requireRefreshToken,
  protectRoute
} from '@riavzon/auth'

When using the IAM service in standalone mode, all routes are automatically mounted. When using the library, you can mount them yourself with your own middleware composition. For example, you could add rate limiting or logging middleware before the controller without modifying the IAM source.


HMAC integration

When the IAM service has HMAC authentication enabled (service.Hmac), the hmacAuth middleware runs before all route groups, including the BFF routes. Every request to /secret/data, /secret/accesstoken/metadata, and /operational/config must carry valid HMAC headers in addition to the JWT and refresh token cookies.

The BFF proxy must sign each outbound request with the same clientId and sharedSecret configured on the IAM service. See HMAC Authentication for the signature format, clock-skew tolerance, and replay protection details.

If HMAC is enabled on the IAM service but the BFF does not sign its requests, all BFF calls will receive HTTP 401 with a specific HMAC failure reason (missing headers, unknown client, stale timestamp, or signature mismatch).

The BFF pattern relies on cookies that flow between the browser, the BFF proxy, and the IAM service. The IAM service sets refresh token cookies directly on authentication responses (POST /login, POST /signup, POST /auth/user/refresh-session). The BFF proxy is responsible for setting the access token cookie on the browser and forwarding refresh token cookies in both directions.

CookiehttpOnlySecureSameSiteTTLSet byPurpose
__Secure-aYesYesStrictAccess token TTLBFF proxyCarries the JWT access token
sessionYesYesStrictRefresh token TTLIAM serviceCarries the refresh token
canary_idYesYesLax90 daysIAM serviceSession binding for anomaly detection
a-iatYesYesStrictAccess token TTLBFF proxyStores the access token's iat (issued-at) timestamp

The IAM service validates the session and canary_id cookies on every BFF request. The access token arrives as a Bearer header, not from a cookie, because the BFF extracts it from __Secure-a and places it in the Authorization header before calling the IAM.

The __Secure-a prefix enforces that the cookie is Secure (HTTPS only). The a-iat cookie lets the BFF compare the current token's issued-at time against a cached value to detect when the access token has changed (for example, after another tab triggers a rotation).

What the BFF proxy expects from the IAM service

The BFF proxy needs specific response patterns from the IAM service to manage the session lifecycle. This table summarizes every IAM endpoint the BFF calls and what it expects in return.

IAM endpointMethodWhen the BFF calls itExpected successExpected errors
/secret/dataGETOn every authenticated request to check authorization200 with { authorized, userId, roles, ... }202 MFA, 401 unauthorized, 404 not found
/secret/accesstoken/metadataGETOn every authenticated request to check token freshness200 with { shouldRotate, msUntilExp, ... }202 MFA, 401 unauthorized, 404 not found
/operational/configGETOnce at startup200 with { domain, accessTokenTTL }403 IP not trusted
/loginPOSTWhen the user logs in201 with access token in body + Set-Cookie for refresh token401 invalid credentials, 429 rate limited
/signupPOSTWhen the user signs up201 with access token in body + Set-Cookie for refresh token400 validation error, 429 rate limited
/auth/user/refresh-sessionPOSTWhen tokens need rotation201 with new access token + Set-Cookie for new refresh token202 MFA, 401 invalid refresh token, 429 rate limited
/auth/logoutPOSTWhen the user logs out200 with Set-Cookie clearing the refresh token401 unauthorized
When the IAM service returns 202 on any endpoint, it means anomaly detection triggered an MFA challenge and a magic link email has been sent. The BFF must relay this response to the browser so the frontend can prompt the user to check their email. See MFA for the verification flow.

Configuration reference (IAM side)

The following IAM service configuration fields directly affect BFF behavior. These are set in the IAM service's configuration file, not in the BFF proxy.

Trusted proxy

service.clientIp
string
The IP address of the BFF server. Used by GET /operational/config to restrict access. Must match the BFF's outbound IP as seen by req.socket.remoteAddress on the IAM service.
service.proxy.ipToTrust
string
Fallback IP when service.clientIp is not set. Also used for proxy trust in X-Forwarded-For processing.
service.proxy.trust
boolean
When true, the IAM service trusts X-Forwarded-For and X-Real-IP headers. This must be enabled when the BFF forwards the original client IP so that rate limiting and anomaly detection target the real browser, not the proxy.

HMAC verification

service.Hmac.sharedSecret
string
The shared secret for HMAC-SHA256 verification. Must be identical to the BFF proxy's signing secret. See HMAC Authentication.
service.Hmac.clientId
string
The expected client identifier in the X-Client-Id header. Must match the BFF proxy's configured client ID.
service.Hmac.maxClockSkew
number
Maximum allowed clock difference in milliseconds between the BFF and the IAM service. Requests with timestamps outside this window are rejected. Default is 5 minutes.

Token settings

jwt.access_tokens.expiresInMs
number
Access token lifetime in milliseconds. Controls how long until shouldRotate becomes true on the metadata endpoint. Also returned by GET /operational/config as accessTokenTTL. Default is 15 minutes.
jwt.refresh_tokens.domain
string
The cookie domain for refresh token cookies. Returned by GET /operational/config as domain. The BFF uses this when setting cookies on the browser.
jwt.refresh_tokens.maxAllowedSessionsPerUser
number
Maximum concurrent sessions per user. When this limit is exceeded, the protectRoute anomaly detection triggers an MFA challenge on BFF routes.
jwt.refresh_tokens.byPassAnomaliesFor
number
Time in milliseconds after a successful MFA verification during which anomaly checks are skipped. See MFA bypass window.

Using auth-h3client as the BFF proxy

auth-h3client is the companion Nuxt module that implements the BFF proxy side of the pattern for H3-based frameworks. When installed, it consumes every IAM endpoint listed above and provides:

  • Automatic token rotation using GET /secret/accesstoken/metadata and POST /auth/user/refresh-session.
  • Cookie management using domain and TTL values from GET /operational/config.
  • HMAC signing on every outbound request to the IAM service.
  • mTLS support with optional client certificate authentication between the BFF and the IAM service.
  • OAuth 2.0 and OpenID Connect client with pre-configured providers (Google, GitHub, X/Twitter, LinkedIn) and support for custom providers. Includes OIDC auto-discovery, PKCE, JWKS-based ID token verification, at_hash validation, per-provider scopes, custom email extraction callbacks, and configurable redirect URLs.
  • Magic link handling for password reset, email change, and MFA verification flows with configurable redirect paths and a bounce page system.
  • Client composables for browser-side auth state.
  • Global middleware for IP validation, bot detection, CSRF cookie generation, and visitor fingerprint validation via signed canary cookies.
  • Server-side route handler that wraps token rotation, authorization, and MFA relay into a single function.
  • Input sanitization with configurable HTML sanitizer iterations, filename sanitization, and image file type/size validation.
  • Structured logging via Pino with automatic secret redaction, configurable log levels, and per-request ID tracking.

The module auto-imports both client-side and server-side utilities, registers global middleware, and mounts authentication routes and OAuth routes with built-in CSRF enforcement.

For installation, configuration, composables API, and handler reference, see the auth-h3client documentation.


Rate limiting on BFF routes

The IAM service enforces rate limits on the middleware that protects BFF routes. These limits target the access token and refresh token validation layers, not the BFF controllers directly.

LimiterPointsWindowBlock durationScope
Access token brute force21 second30 minutesPer IP
Access token slow310 minutes1 hourPer IP
Refresh token brute force21 second30 minutesPer IP
Refresh token slow412 hours12 hoursPer IP
Blacklist (JTI revocation)2024 hours72 hoursPer token ID

When a rate limit is hit, the IAM service responds with HTTP 429 and a Retry-After header. The BFF proxy should forward this header and status code to the browser.


Security layers

Requests to BFF routes pass through the full IAM security stack. This is the complete order of operations for a request reaching GET /secret/data or GET /secret/accesstoken/metadata:

IP validation

The global validateIp middleware verifies that req.ip is a valid IP address. Malformed or missing IPs receive HTTP 403.

Security headers

The helmet middleware sets Content-Security-Policy, X-Frame-Options: DENY, Cross-Origin-Embedder-Policy, Referrer-Policy: origin, and cache directives (no-cache, private).

HMAC authentication (optional)

When service.Hmac is configured, hmacAuth validates the four HMAC headers. This runs before body parsing and cookie parsing. See HMAC Authentication.

HTTP logging

The pino-http middleware generates a request ID (from X-Request-ID or a UUID), sets the X-Request-Id response header, and logs the request with redacted authorization headers and session cookie values.

Access token extraction

requireAccessToken extracts the Bearer token from the Authorization header.

Refresh token check

requireRefreshToken verifies the session cookie is present.

Fingerprinting

getFingerPrint collects device, browser, geolocation, and network metadata from the request headers and resolved IP address.

Active MFA short-circuit

checkForActiveMfa stops requests that already have an unresolved MFA state cached for the session before protectRoute runs again.

JWT verification and anomaly detection

protectRoute verifies the JWT and runs the full anomaly detection suite. This is where MFA challenges originate.

acceptCookieOnly enforces a strict no-body, no-query, no-content-type contract on the metadata endpoint.

Controller

The allowBffAccess or getAccessTokenPayload controller runs with req.user fully populated.


Summary

SystemIntegration point
Access tokensThe metadata endpoint returns shouldRotate based on access token TTL and the 25% threshold. The BFF stores access tokens as httpOnly cookies and rotates them proactively
Refresh tokensBoth BFF routes require a valid session cookie. The BFF forwards it to POST /auth/user/refresh-session for rotation
Anomaly detectionBoth /secret/data and /secret/accesstoken/metadata run through protectRoute, which calls strangeThings() for the full anomaly detection suite
FingerprintingThe getFingerPrint middleware runs on both BFF routes and builds the server-side fingerprint from the request User-Agent, resolved IP, and geolocation data
HMAC authenticationWhen configured, HMAC validation runs before all BFF routes, authenticating the proxy as a trusted service
MFAAnomaly detection on BFF routes can return HTTP 202, triggering an MFA challenge that the BFF relays to the browser
auth-h3clientThe companion Nuxt module that implements the full BFF proxy, consuming every IAM endpoint documented above
Logo