Thoryn

Policy rules · Policy rules

MFA required for privileged actions

ALLOW only if MFA was completed in the current session AND the OIDC `acr` value indicates a sufficient assurance level. Use it as a step-up gate before any high-risk action.

Tested against:policyEngine: 1.0.0

policy-engine recipe — shared category architecture: how this pattern composes with Hub, Broker, and the rest of the catalog

Use case

A privileged action — admin user creation, key rotation, money transfer — must require fresh MFA, not just a valid session. The OIDC acr (Authentication Context Class Reference) carries the assurance level; combine it with a fresh-MFA flag from your session table.

Rule

{
  "all": [
    { "fact": "mfa_completed", "operator": "equal", "value": true },
    { "fact": "acr", "operator": "in", "value": ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:gold", "loa3", "loa4"] }
  ]
}

Facts shape

data class StepUpFacts(
  val mfa_completed: Boolean,           // from your session table — true if MFA in last N min
  val acr: String,                      // from the OIDC ID token
)

Composition

Wrap this rule around any privileged action:

fun handlePrivilegedAction(ctx: RequestContext): Response {
  val decision = policyEngine.evaluate(stepUpRule, ctx.facts())
  if (decision != Decision.ALLOW) return Response.stepUpRequired(decision.reason)
  // … proceed with the privileged action
}

Trace — DENY (MFA stale)

{
  "decision": "DENY",
  "reason": "mfa_completed expected true, got false (stale: 47 minutes since last MFA)",
  "trace": [
    { "all": [
        { "fact": "mfa_completed", "operator": "equal", "value": true, "actual": false, "result": "fail" }
      ],
      "result": "fail"
    }
  ]
}

When to use

  • High-risk actions inside an authenticated session
  • Compliance regimes requiring step-up for specific operations (PSD2, regulated-finance equivalents)

When not to use

  • All requests across a surface — that's session-level, not per-action
  • Pure account login — handle MFA at the IdP layer, not Policy Engine

See also