Skip to content

A frontend Bridge SDK tracks a single reactive authState that walks a signed-in user through a multi-step sign-in flow (credentials, MFA, workspace selection, fully authenticated). An Express backend doesn’t have a sign-in flow to walk through; it evaluates one request at a time, independently, with no memory of the previous one. The useful equivalent here is the set of outcomes bridge.auth() / bridge.protect(...) can land a request in.

OutcomeHTTP resultWhat’s on req afterward
No credential offered401 with WWW-Authenticate: ... error="missing_token"Nothing; handler never runs
Malformed Authorization header401, error="invalid_token"Nothing; handler never runs
Expired user JWT401, error="expired_token"Nothing; handler never runs
Invalid signature / unknown key / bad issuer or audience401, error="invalid_token"Nothing; handler never runs
API token issued for a different app401, error="invalid_token"Nothing; handler never runs
Wrong credential type for this endpoint (acceptAuth)401, error="invalid_request"Nothing; handler never runs
Valid user JWT, but missing required role / route-rule privilege / featureFlag403 Forbiddenreq.bridgeUser is set even though the handler never runs
Valid API token, but missing required privilege403 Forbiddenreq.bridgeApiToken is set even though the handler never runs
Valid user JWT onlyhandler runsreq.bridgeUser, req.bridgeTenant, req.bridgeAccessToken set; req.bridgeApiToken unset
Valid API token onlyhandler runsreq.bridgeApiToken set; req.bridgeUser/req.bridgeTenant/req.bridgeAccessToken unset
Both a valid user JWT and a valid API tokenhandler runsAll four are set; both contexts coexist on req
Route marked public (bridge.public() or an ANONYMOUS route rule)handler runs, no verification attemptedNothing set; req.bridgeUser/req.bridgeApiToken are undefined

The two credential paths are evaluated independently: when both an x-api-key and an Authorization: Bearer header are present and valid, both contexts coexist on the request rather than one taking precedence (some Bridge frontends always send both). See API tokens for the full dual-credential story, and Configuration for the exact JSON error bodies.

Checking which state you’re in, inside a handler

Section titled “Checking which state you’re in, inside a handler”

For a dual-auth endpoint (the default), branch on which context is present:

router.get('/users', bridge.protect({ privilege: 'USER_READ' }), (req, res) => {
  if (req.bridgeApiToken) {
    // Authenticated via API token (machine caller)
    return res.json({ users: [], tenantId: req.bridgeApiToken.tenantId });
  }

  // Authenticated via user JWT (a signed-in person)
  const user = req.bridgeUser!;
  return res.json({ users: [], tenantId: user.tenantId });
});

A frontend’s authState has intermediate states ('credentials-validated', 'mfa-required', 'tenant-selection') because signing in is a multi-step, stateful process that the SDK walks a browser through. Verifying a request has no equivalent multi-step process: a token either verifies against Bridge’s JWKS (or introspection endpoint) right now, in full, or it doesn’t. There’s nothing partial to represent. The MFA challenge, workspace selection (a workspace is called a tenant in the API, hence the state name above), and credential exchange that produced the token already happened upstream, in whichever frontend or CLI flow signed the user in. By the time a request reaches your Express app, that’s all settled into a single bearer token you either accept or reject.