NIST 800-63-4 FAL Classifier

NIST SP 800-63-4 Federation Assurance Levels — FAL1 / FAL2 / FAL3. While AAL describes the rigor of authentication (what the user did to prove they are who they say they are), FAL describes the rigor of the FEDERATION assertion that carried that authentication from the IdP to the RP.

FAL bands per NIST 800-63C-4:

FAL1: Bearer assertion delivered through the front channel (typical OIDC ID token over the browser redirect). Signed by the IdP; verified by the RP. No audience binding beyond the standard aud claim.

FAL2: Bearer assertion delivered through the back channel OR front-channel assertion that is encrypted to the RP. Replay-protection nonce required. Typical OIDC Authorization Code Flow with mTLS or DPoP-bound token.

FAL3: Holder-of-Key assertion. RP verifies the subject cryptographically holds a key bound to the assertion (mTLS client-cert pinned to the subject, DPoP-bound + audience-restricted, OR SAML HoK SubjectConfirmation). Defeats stolen-bearer-token replay.

Operators classify the FAL of an incoming federation assertion via fromAssertion(opts) — pass the assertion's properties (channel, encrypted, hokBinding, etc.) and get back the band. Compose with b.middleware.requireFal({ minimum: "FAL2" }) for the gate.

b.auth.fal.isValidBand(band) #

stable0.8.87

Predicate returning true when band is one of the documented FAL band strings ("FAL1" / "FAL2" / "FAL3").

b.auth.fal.isValidBand("FAL2");  // → true
b.auth.fal.isValidBand("FALX");  // → false

b.auth.fal.meets(actualBand, requiredBand) #

stable0.8.87

Predicate returning true when actualBand satisfies the requiredBand floor (FAL3 ≥ FAL2 ≥ FAL1). Invalid band strings on either argument return false — operators using meets directly for authorization decisions never get a "true" verdict out of a malformed input pair.

b.auth.fal.meets("FAL3", "FAL2");    // → true
b.auth.fal.meets("FAL1", "FAL2");    // → false
b.auth.fal.meets("FAL1", "FALX");    // → false (invalid required band)
b.auth.fal.meets("bad", "bad");      // → false (both invalid)

b.auth.fal.fromAssertion(opts) #

stable0.8.87
{
  channel:                  "front" | "back",   // REQUIRED
  encrypted:                boolean,             // assertion encrypted to RP
  replayProtected:          boolean,             // nonce / jti / iat binding present
  backChannelAuthenticated: boolean,             // back-channel transport-auth'd (mTLS / signed) — required for FAL2 over plain back-channel
  hokBinding:               "mtls" | "dpop" | "saml-hok" | null,
                                                 // proof-of-possession binding present
  bearerOnly:               boolean,             // alias for hokBinding === null
}

Classify an incoming federation assertion's FAL band per NIST 800-63C-4. Returns one of "FAL1" / "FAL2" / "FAL3". Throws auth/bad-fal-opts on missing required fields.

- HoK binding (mTLS client-cert pinned, DPoP-bound, SAML HoK) → FAL3 - Back-channel delivery OR encrypted-to-RP front-channel + replay-protection nonce → FAL2 - Anything else → FAL1

The classifier is conservative: missing replay-protection on a back-channel assertion downgrades to FAL1 because §5.2 requires nonce / jti binding before back-channel can claim FAL2.

var fal = b.auth.fal.fromAssertion({
  channel:         "back",
  encrypted:       false,
  replayProtected: true,
  hokBinding:      null,
});
// → "FAL2"

var fal3 = b.auth.fal.fromAssertion({
  channel:         "back",
  hokBinding:      "mtls",
  replayProtected: true,
});
// → "FAL3"

b.auth.fal.requireFal(minimumBand) #

stable0.8.87

Build a guard that throws auth/fal-insufficient when the supplied band is below the minimum. The middleware form (b.middleware.requireFal) wraps this guard at the request layer.

var fal3Only = b.auth.fal.requireFal("FAL3");
fal3Only(req.session.federationFal);
// throws auth/fal-insufficient if not FAL3

Last updated 2026-08-08T16:39:49.652Z by seeder.