Skip to content

#48 M1: PKCE code_verifier/challenge (RFC 7636) pure pair - #10

Merged
xianxu merged 13 commits into
mainfrom
000048-shim-oauth-add-microsoft-entra-as-the-2nd-real-oauth-provider-behind-the-provider-port-n-2-real-validation
Sep 28, 2026
Merged

xianxu merged 13 commits into
mainfrom
000048-shim-oauth-add-microsoft-entra-as-the-2nd-real-oauth-provider-behind-the-provider-port-n-2-real-validation

Conversation

@xianxu

@xianxu xianxu commented Sep 28, 2026

Copy link
Copy Markdown
Owner
  • #48: move archived issue + plan into per-kind history subfolders (ariadne#181)
  • Merge remote-tracking branch 'origin/main' into 000048-shim-oauth-add-microsoft-entra-as-the-2nd-real-oauth-provider-behind-the-provider-port-n-2-real-validation
  • #48: correct actual_hours 5.96 → 4.0 (active-time-v3 segment-contamination artifact)
  • #48: archive issue + plan to history (done)
  • #48: close — n=2-real OAuth validation done (Google + Microsoft grounded)
  • #48 M2: fix Keychain account-label dup (FIX-THEN-SHIP) + rtCapture hermetic test
  • #48 M2: atlas — Microsoft OAuth grounding certified in the freshness ledger
  • #48 M2: certify real-Microsoft grounding (PASS, repeatable)
  • #48 M2: Microsoft grounding harness + record n=2 findings
  • #48 M1: address boundary-review findings (FIX-THEN-SHIP)
  • #48 M1: tick M1 in issue plan
  • #48 M1: Microsoft/Entra as 2nd OAuth provider behind one OIDCProvider + dialect
  • #48 M1: PKCE code_verifier/challenge (RFC 7636) pure pair

xianxu and others added 13 commits June 8, 2026 16:33
Microsoft is a public client (no secret) → the token exchange uses PKCE.
codeChallenge is pure (tested against the RFC 7636 Appendix-B vector);
codeVerifier (crypto/rand) is the thin IO shell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dialect

Factor the per-provider seam out of the Google-hardcoded core (ARCH-DRY): one
generic OIDCProvider (renamed from GoogleProvider) drives Auth/Refresh/Revoke/
exchange/callback for every provider; the variation is an injected `dialect`
value. Google and Microsoft are sibling provider files over the shared oidc.go
core — not two parallel adapters.

The seam Microsoft forced (the n=2 payoff):
- identity extractor: credentialFromToken no longer hardcodes "google"+email_
  verified; it takes (providerID, extractID). googleIdentity reads email and
  rejects unverified (the guard MOVED here — Google-layer, not shared);
  microsoftIdentity reads preferred_username/upn, no verified claim.
- decodeIDClaims: provider-neutral JWT-payload decode (replaces parseIDToken);
  each provider reads its own claim subset through it (ARCH-PURE).
- PKCE: Microsoft is a public client (no secret) → Auth generates a per-call
  code_verifier, layers the S256 challenge on the auth URL, sends the verifier
  on exchange; exchange/Refresh omit client_secret when empty.
- offline_access scope (vs Google's access_type=offline) via the auth-URL dialect.
- no token-revoke endpoint: microsoftRevoke returns ErrRevokeUnsupported (honest
  about the boundary — MS only has global Graph revokeSignInSessions).

The Fake is dialect-aware (newFake(conf, dialect)): it mints MS-shaped id_tokens
and tags credentials "microsoft", so the SAME oauth-credential-lifecycle S
contract certifies against both Google and Microsoft dialects (hermetic n=2).

Tests: PKCE codeChallenge against the RFC 7636 vector; microsoft_test.go grounds
the MS wire (PKCE verifier + no client_secret + rotation + preferred_username/upn)
against httptest; TestContract_FakeMicrosoft + TestFakeMicrosoft_NoVerifiedGuard.
go test ./lib/provider/oauth/ green; Google behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both Important findings + the cheap minors from the M1 boundary review:
- atlas drift: atlas/nous/oauth-health.md (*GoogleProvider)→(*OIDCProvider)
  CheckHealth — drift the rename created now (fix it in the milestone that
  caused it, not the planned M2 atlas pass).
- removed a stray tracked compiled binary oauth-conformance-provision at the
  repo root (+ .gitignore entry) — accidental build artifact.
- gofmt: oidc.go (dialect-struct comment alignment) + token.go/token_test.go.
- lessons.md: rename sweeps must include atlas/docs; skim git status for
  build artifacts before a broad add -A.

Review-Verdict: FIX-THEN-SHIP
Review-Window: 209de69..HEAD

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- conformance provisioner: -provider google|microsoft (public-client/PKCE);
  reads MICROSOFT_CLIENT_ID/TENANT_ID; stores under the MS Keychain service.
- Contract_RealMicrosoft (conformance-tagged): rtCapture decorator captures
  every rotation (incl. CheckHealth's internal one) and persists the final live
  token back to Keychain — repeatable grounding despite MS single-use rotation.
  Asserts the always-rotate invariant Google's contract can't. SKIPs without an
  Entra app + Keychain token (like Google's).
- SKILL.md: the full Entra app recipe (public client, http://localhost, allow
  public-client flows, openid/profile/offline_access) + the MS provision/certify
  loop + the rotation/revoke boundary.
- target oauth-credential-lifecycle.md: invariants 4/5 updated to the realized
  seam; a ## Revisions entry recording every n=2 cross-provider finding.

Records the cross-provider evidence to ariadne#71 (the abstraction held at
n=2-real: one OIDCProvider + in-package dialect, no shared cross-service
framework).

Hermetic dual-dialect contract green; Contract_RealMicrosoft SKIPs cleanly;
Contract_RealGoogle still PASSES.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contract_RealMicrosoft PASSED against real Microsoft (single-tenant public-client
Entra app, PKCE) and passed again on immediate re-run — the rtCapture rotated-token
write-back makes grounding repeatable despite MS single-use rotation. The fake
matches real Microsoft on Refresh + CheckHealth. n=2-real is grounded, not just
designed. Recorded in the oauth-credential-lifecycle target Revisions + issue Log.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ledger

shim-conformance-grounding.md: Microsoft/Entra row moves planned→PASS (2026-06-11,
repeatable via rotated-token write-back), + a boundary note on the two real
cross-provider differences (ErrRevokeUnsupported by mechanism; single-use rotation
persistence).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rmetic test

M2 boundary review Important finding: the MS provisioner stored the seed token
under the real account (preferred_username) while the cert wrote the rotated
token back under a hardcoded "conformance@grounding" — and security
add-generic-password -U keys on (service, account), so this created a SECOND
item under one service (dead seed + live token). keychainSecret reads by service
only, so which item wins is order-undefined; a bare find-generic-password -s
SERVICE empirically returned the DEAD item (the twice-PASS was most-recently-
modified luck).

Fix: one source of truth for the account label — oauth.ConformanceAccountMicrosoft
— used by BOTH the provisioner's MS store and the cert write-back, so -U updates
a single item in place. Deleted the stale item; re-certified vs real Microsoft →
PASS with a single item. (Google needs no analogue: reusable token, no write-back.)

Also added TestRTCapture_CapturesLastRotation: hermetic (fake + always-rotate, no
network) proof the decorator captures the rotation buried inside CheckHealth's
internal Refresh — the repeatability linchpin, previously only exercised by the
real run.

Review-Verdict: FIX-THEN-SHIP
Review-Window: 3007cc9..HEAD

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded)

status → done, actual_hours 5.96 (focused; the 14.47h issue-close re-measure
bridges the 06-08→06-11 idle gap — see Log). Both milestones + boundary reviews
complete; real-Microsoft grounding certified and repeatable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation artifact)

Issue-close measured 14.47h; ~90% traces to one 2.7-day-spanning segment that
absorbed cross-session nous+brain activity and pinned it on #48 (not idle-
bridging — the 15-min cap works; it's a segment-width artifact). Corrected to a
labeled judgment of 4.0h (cleanly-anchored #48 work ~1.4h + the genuine 06-11
resume-session dev buried in the fat segment). Upstream fix filed as ariadne#92.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-microsoft-entra-as-the-2nd-real-oauth-provider-behind-the-provider-port-n-2-real-validation
…adne#181)

The close archived into the flat workshop/history/ layout; main has since
migrated to history/issues + history/plans, so place them to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xianxu
xianxu merged commit 222c8fc into main Sep 28, 2026
1 check failed
@xianxu
xianxu deleted the 000048-shim-oauth-add-microsoft-entra-as-the-2nd-real-oauth-provider-behind-the-provider-port-n-2-real-validation branch September 28, 2026 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant