Commit 609c0ad
Filter upstream auth chain via callback hook (#5725)
* Filter upstream auth chain via callback hook
The embedded OAuth authorization server walks every configured upstream
on every authorization, prompting for and storing tokens that a given
authorization may not need. This widens the stored-token surface and
forces needless upstream prompts.
Add an optional UpstreamFilter hook (WithUpstreamFilter), consulted once
in the callback handler after the first upstream resolves, that narrows
the remaining chain to a subset of the configured upstreams:
- The first upstream is always required and is never passed to nor
removable by the filter.
- The kept set is computed once and carried in PendingAuthorization
(ChainUpstreams) so the filter is not re-run per leg.
- nextMissingUpstream and the cross-leg identity check now operate on
the effective chain rather than the raw config.
- A filter error fails the authorization with a server error; it never
silently falls back to walking every upstream.
With no filter configured, behaviour is unchanged: the handler walks all
configured upstreams as before. SingleLeg flows are unaffected and the
upstream-token storage key contracts are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Harden upstream chain resolution per review
Addresses #5725 review comments:
- MEDIUM callback.go (3525733910): distinguish a true first leg
(ResolvedUserID == "") from a subsequent leg; reject a subsequent-leg
pending that lacks a computed chain instead of recomputing the filter
against the wrong leg's request context.
- MEDIUM callback.go (3525733913): validate a chain loaded from storage
(non-empty, leads with the first configured upstream, names only
configured upstreams) so a tampered PendingAuthorization row cannot
shrink the chain and disable the cross-leg identity check.
- MEDIUM callback.go (3525733916): restore discrete expected/got/provider
slog fields on the identity-mismatch check after the verifyChainIdentity
extraction, so log pipelines can still alert on it.
- MEDIUM redis_test.go (3525733919): add a backward-compatibility test
that loads a legacy pending JSON lacking chain_upstreams and asserts an
empty ChainUpstreams.
Also expands the UpstreamFilter doc comment (first-leg-context guarantee
across rolling upgrades, WithPlatformUser in ctx) and updates existing
chain tests to carry ChainUpstreams on subsequent legs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Expose first-upstream identity to chain filter
A useful upstream filter needs to key its narrowing decision on who
authenticated — the subject and the authorization-relevant claims from
the first upstream — not just on request context. Widen the hook to
receive that identity explicitly.
- FilterUpstreams now takes the canonical platform user ID and the
resolved auth.PrincipalInfo (claim-mapped Subject, Name, Email, and the
Claims map used by authz policies) alongside the configured upstreams.
The principal is passed as an explicit parameter, not via context: the
callback deliberately avoids placing a bearer-less Identity in ctx.
- Capture the upstream claims that previously had nowhere to live: add
Claims to upstream.Identity, populated from the validated ID token
(OIDC) and the userinfo response (OAuth2). identityFromToken and
synthetic OAuth2 resolve no structured claim set and leave it nil.
- The callback builds the principal from the first leg's resolved
identity and threads it through continueChainOrComplete -> resolveChain
-> computeChain to the filter.
Tests: an OIDC provider integration test (real provider + mock IdP)
asserting ID-token claims are captured into the resolved Identity, and a
callback integration test asserting the first upstream's subject and
claims reach FilterUpstreams.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add callback/chain integration tests for auth server
The embedded auth server integration suite stopped at the first-leg
redirect and never exercised the /oauth/callback or multi-upstream chain
traversal. Add coverage that drives the callback end to end against the
real embedded server and mock upstream IDPs:
- Single upstream: authorize -> callback -> the chain completes at the
sole upstream and issues the authorization code to the client.
- Multi-upstream: the first callback continues the chain to the second
upstream, and the second completes it and issues the code.
Adds a WithUpstreams helper option, an OAuth2 upstream builder, and a
Callback client method to support driving the flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Tighten chain validation and clarify filter docs
Addresses #5725 review comments:
- MEDIUM handler.go (3528199173): validateChain now enforces an in-order,
duplicate-free subsequence of the configured upstreams (config-cursor
walk), so a tampered pending row cannot shrink/reorder the chain to skip
legs (e.g. ["provider-1"] or ["provider-1","provider-1"]) and slip past
the single-element identity-check no-op. Adds a TestValidateChain table
covering wrong-first / unconfigured / out-of-order / duplicate.
- MEDIUM handler.go (body): NewHandler rejects duplicate upstream names —
the name invariant is now load-bearing (chain keyed by name; tokens and
upstreamByName key by name). Adds a constructor test.
- LOW handler.go (3528199199): document the Claims contract — values are
untyped (assert defensively, never panic), nil is possible on a transient
OIDC extraction failure (treat as fail-closed), and aud is the upstream's
client_id, not this AS.
- LOW callback.go (3528199204): reword verifyChainIdentity to match scope —
it reconciles only the first leg; intermediate legs are intentionally not
identity-checked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Drop redundant platformUserID param from UpstreamFilter
The canonical user is already carried as principal.PlatformUserID, so the
separate platformUserID argument to FilterUpstreams was redundant. Remove it
and have consumers read principal.PlatformUserID; update the doc, the stub
filter, and the tests accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>1 parent d27d85a commit 609c0ad
17 files changed
Lines changed: 1173 additions & 42 deletions
File tree
- pkg/authserver
- server/handlers
- storage
- upstream
- test/integration/authserver
- helpers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
188 | 206 | | |
189 | 207 | | |
190 | 208 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
183 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
184 | 197 | | |
185 | 198 | | |
186 | 199 | | |
| |||
409 | 422 | | |
410 | 423 | | |
411 | 424 | | |
412 | | - | |
413 | | - | |
414 | | - | |
| 425 | + | |
415 | 426 | | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
416 | 434 | | |
417 | 435 | | |
418 | 436 | | |
| |||
433 | 451 | | |
434 | 452 | | |
435 | 453 | | |
436 | | - | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
437 | 468 | | |
438 | 469 | | |
439 | 470 | | |
| |||
442 | 473 | | |
443 | 474 | | |
444 | 475 | | |
445 | | - | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
450 | | - | |
451 | | - | |
452 | | - | |
453 | | - | |
454 | | - | |
455 | | - | |
456 | | - | |
457 | | - | |
458 | | - | |
459 | | - | |
460 | | - | |
461 | | - | |
462 | | - | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
467 | 482 | | |
468 | 483 | | |
469 | 484 | | |
| |||
495 | 510 | | |
496 | 511 | | |
497 | 512 | | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
498 | 516 | | |
499 | 517 | | |
500 | 518 | | |
| |||
533 | 551 | | |
534 | 552 | | |
535 | 553 | | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
0 commit comments