App-asserted step-up route: Bifrost asserts a native strong proof, Heimdall mints step_up_at + step_up_method #15

Merged
heimdall merged 4 commits from heimdall/app-asserted-step-up into main 2026-11-13 20:19:55 +00:00
Member

POST /v1/apps/:appSlug/step-up (scoping note /opt/commons/handoff/strong-proof-scoping-20261113.md, S1).

  • Needs the app secret AND the member's live bearer for the same app/session; returns a copy of the claims with new jti, step_up_at=now, step_up_method. Original exp and sid kept, no refresh token, no session row. Refresh carries neither claim.
  • Profile field stepUpAssertion.methods (bifrost only: bifrost-passkey, bifrost-email-code); other apps 403. Nonce single-use via oauth_state_uses (no migration); rate limits 10/600s per account, 120/60s per app; audit events step_up_asserted / step_up_assertion_refused without secret, bearer or nonce.
  • Provider step-up now sets step_up_method=provider:. Unlink unchanged (records method).
  • Tests: 885 pass / 17 skip (was 867/17), live-PG nonce test ran; 14 mutations all red. Docs and state/evidence.jsonl updated.
  • Limits: Heimdall cannot verify the factor, it trusts the calling app; rate limiter is in-process. Not deployed; no root-list needs (no schema, env or grant change).

🤖 Generated with Claude Code

POST /v1/apps/:appSlug/step-up (scoping note /opt/commons/handoff/strong-proof-scoping-20261113.md, S1). - Needs the app secret AND the member's live bearer for the same app/session; returns a copy of the claims with new jti, step_up_at=now, step_up_method. Original exp and sid kept, no refresh token, no session row. Refresh carries neither claim. - Profile field stepUpAssertion.methods (bifrost only: bifrost-passkey, bifrost-email-code); other apps 403. Nonce single-use via oauth_state_uses (no migration); rate limits 10/600s per account, 120/60s per app; audit events step_up_asserted / step_up_assertion_refused without secret, bearer or nonce. - Provider step-up now sets step_up_method=provider:<name>. Unlink unchanged (records method). - Tests: 885 pass / 17 skip (was 867/17), live-PG nonce test ran; 14 mutations all red. Docs and state/evidence.jsonl updated. - Limits: Heimdall cannot verify the factor, it trusts the calling app; rate limiter is in-process. Not deployed; no root-list needs (no schema, env or grant change). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
App secret plus the member's live bearer for that app, a per-profile grant and method allowlist (bifrost only), a nonce spent once per session through the existing oauth_state_uses claim (no schema change), an in-process fixed-window rate per account and per app, and an audit row per assertion or post-secret refusal. Provider step-up sign-ins now label their proof provider:<name>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Heimdall!15
No description provided.