Skip to content

feat(core): accept recent OIDC authentication context in the account API - #9771

Open
wangsijie wants to merge 3 commits into
wangsijie-share-identity-verified-assertionfrom
wangsijie-log-14124-accept-recent-oidc-authentication-context-in-account-api-and
Open

wangsijie wants to merge 3 commits into
wangsijie-share-identity-verified-assertionfrom
wangsijie-log-14124-accept-recent-oidc-authentication-context-in-account-api-and

Conversation

@wangsijie

@wangsijie wangsijie commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Stacked: this branch sits on #9774 → #9773. Review and merge those first; this diff is what remains of the original PR after the split.

Summary

One vertical slice for M6: the Account API derives identityVerified from the access token's authentication context, keeps the legacy logto-verification-id path working, and answers a missing or stale proof with an RFC 9470 challenge.

  • koaOidcAuth (packages/core/src/middleware/koa-auth/): identityVerified is now oidcVerified || legacyVerified. The OIDC branch requires a first-party caller, an acr that satisfies urn:logto:acr:1fa, and an auth_time no older than 600 seconds, and fails closed on a missing, malformed, stale, future, third-party, or CIMD client value. Token issuance time (iat) is never used, and the legacy record's ownership, expiry, verification type, and template checks are untouched.
  • authentication-context.ts holds the policy in one place: the required ACR, the maximum authentication age, the strict parse of the token's acr / auth_time, and the challenge builder that escapes quoted-strings.
  • Sensitive routes (packages/core/src/routes/account/): the shared assertIdentityVerified helper emits the challenge for a first-party caller only, since a step-up cannot help a third-party application. The failure keeps the 401 status and the verification_record.permission_denied body, check order is unchanged so third-party error codes stay as they are, and the initial-password exception is unchanged.
  • OpenAPI: the affected 401 responses describe the new requirement, which holds for both the token and the legacy record. The WWW-Authenticate header itself is not described in the supplement yet — its documentation lands with the release that makes the challenge unconditional, so the served document never promises a header an installation does not send.

Resolves LOG-14124.

What moved out of this PR

The original PR also carried a released-behavior fix and an extraction. They are separate now, so each can be reviewed and shipped on its own:

PR Contents
#9773 The first-party requirement on the four reads that need recent authentication, with the changeset.
#9774 The shared assertIdentityVerified assertion.

Testing

Unit tests, integration tests.

  • Unit tests for the policy (fresh 1fa / mfa, stale, future, missing, malformed, and non-Logto acr, third-party and CIMD clients, legacy record path), for the challenge header format and escaping, and for challenge emission from a first-party caller only.
  • Unit tests for the route changes.
  • Integration tests cover the new token semantics: oidc-authentication-context.test.ts for the fresh-context and challenge paths, while the legacy-path negative cases sign in through the mock social connector, which records amr=["fed"] and no acr. Left to CI.

Checklist

  • unit tests
  • integration tests
  • necessary TSDoc comments

Copilot AI balanced review requested due to automatic review settings October 16, 2026 04:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added size/xl and removed size/xl labels Oct 16, 2026
Copilot AI balanced review requested due to automatic review settings October 16, 2026 06:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added size/xl and removed size/xl labels Oct 16, 2026
Copilot AI balanced review requested due to automatic review settings October 16, 2026 07:59
@wangsijie
wangsijie force-pushed the wangsijie-log-14124-accept-recent-oidc-authentication-context-in-account-api-and branch from c50bd98 to 2270221 Compare October 16, 2026 07:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

`koaOidcAuth` derives `identityVerified` from the access token's authentication
context as well as the legacy `logto-verification-id` header: a first-party token
whose `acr` satisfies `urn:logto:acr:1fa` and whose `auth_time` is at most 600
seconds old authorizes sensitive Account API operations without a separate
verification record. The branch fails closed for missing, malformed, stale,
future, or third-party values, never reads `iat`, and is guarded by
`isDevFeaturesEnabled`; the legacy branch is unchanged.

A sensitive route that lacks recent authentication now answers with the RFC 9470
challenge next to the existing 401 body, built by one shared helper and emitted
only for first-party callers, which are the only ones a step-up can help.

The account reads that need recent authentication (backup codes, sessions,
grants, and trusted devices) additionally require a first-party application, so a
third-party token from a fresh sign-in can no longer read them.

Amp-Thread-ID: https://ampcode.com/threads/T-01a14383-fded-744a-b90a-56f48926c865
…tication context

The shared profile helper imported the step-up helper, which pulls
`@silverhand/slonik` (a dev dependency, so it is bundled) into every test entry
that imports it: the console and experience suites then failed to load with a
dynamic `require` error. The helper now signs in through the mock social
connector with its own connector setup, and removes the identity it linked when
the user had none, so it leaves the account as it was found.

Also sign in before binding MFA factors, whose verification a sign-in then
requires; enable trusted devices in `enableAllAccountCenterFields`; request
`profile` for the third-party read case, which the SDK always asks for; and read
the challenge response without the client throwing on the 401.

Amp-Thread-ID: https://ampcode.com/threads/T-01a142a9-f6bd-706b-a70d-f77ec3058cc0
The positive OIDC branch and the RFC 9470 challenge only exist with the feature
enabled, so those two cases use `devFeatureTest`. The legacy verification record
case still runs in both modes.

Amp-Thread-ID: https://ampcode.com/threads/T-01a142a9-f6bd-706b-a70d-f77ec3058cc0
@wangsijie
wangsijie force-pushed the wangsijie-log-14124-accept-recent-oidc-authentication-context-in-account-api-and branch from 2270221 to e4a5a66 Compare October 16, 2026 08:57
@wangsijie
wangsijie changed the base branch from wangsijie-swagger-dev-feature-pruning to wangsijie-share-identity-verified-assertion October 16, 2026 08:57
@github-actions github-actions Bot added size/xl and removed size/xl labels Oct 16, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants