Uh oh!
There was an error while loading. Please reload this page.
remote/oidc: discriminated credential record + session-aware resolve (T4) - #199
Closed
platypii wants to merge 3 commits into
Closed
remote/oidc: discriminated credential record + session-aware resolve (T4)#199platypii wants to merge 3 commits into
platypii wants to merge 3 commits into
Conversation
Two dependency-free local primitives for the browser login flow:
- pkce.js: createPkcePair() -> { verifier, challenge }, S256 over stdlib
crypto. The client's downstream PKCE leg (LLP 0046 D3).
- loopback.js: startLoopbackReceiver({ state, timeoutMs }) binds a
single-shot 127.0.0.1:0 HTTP listener serving /callback, returns its
redirectUri up front, and resolves { code } on a state-matched
callback (rejecting on mismatch, error=, or timeout). RFC 8252
ephemeral redirect (LLP 0046 D2).
Unit tests cover the SHA-256 challenge derivation, fresh randomness,
and the loopback success / state-mismatch / error / timeout paths.… T5) Completes milestone-1 local primitives, composing chunk 1's PKCE + loopback: - identity_client.js: exchangeCode / refreshSession over an injectable fetch against <origin>/v1/identity/token; a 401 invalid_grant surfaces a typed InvalidGrantError. Response field is access_jwt, expires_at is ISO. No external JWKS on the client. - open_browser.js: platform opener (open / xdg-open / cmd start), detached; returns whether an opener was found (LLP 0046 D8). - oidc_login.js: loginWithBrowser() orchestrates PKCE + a random state, starts the loopback receiver, builds the /login/start URL, opens the browser (or prints the URL), awaits the code, and exchanges it for the session. No persistence; the caller stores it (LLP 0046 D2/D3). - types.d.ts: OidcSession / RefreshedAccess shared interfaces. Unit tests cover the token request bodies + response mapping, the invalid_grant typing, each platform opener, and the full PKCE->loopback->exchange orchestration including --no-browser and loopback cleanup on failure.
…(T4)
Extends the 0600 credential store to carry OIDC sessions alongside the
existing static tokens (LLP 0046 D4), and adds the attach-path resolver
(LLP 0046 D5):
- Records gain a kind discriminator. readCredentials normalizes each
record; a legacy token-only record (no kind) reads as static, so
existing files keep working without a rewrite. writeToken now stamps
kind: 'static'.
- writeSession(stateDir, target, { refreshToken, accessJwt, expiresAt,
org }) writes a kind: 'oidc' record through the same atomic 0600 path.
- resolveAccessJwt({ target, env, stateDir, identityBase, now, fetchImpl
}): env override wins; a static record returns its token; an oidc
record returns a fresh access JWT, refreshing + persisting when the
cached one is within a 60s skew of expiry, and propagating a refresh
failure (typed invalid_grant). resolveToken stays for the stdio proxy.
- removeToken drops either kind (whole-record delete, unchanged).
- types.d.ts: RemoteStaticRecord / RemoteOidcRecord / the union.
Unit tests cover the round-trip, legacy normalization, env override,
fresh vs stale refresh + persistence, and failure propagation. Full
suite green.platypiiforce-pushed
the
chunk2-identity-oidc-login
branch
from
June 29, 2026 18:36
d0ea83e to
a60d2d6Compareplatypii
commented
Jun 29, 2026
ContributorAuthor
Review (independent agent review)Verdict: approve-with-nits. Correctness is sound for realistic inputs: read-implicit migration (legacy Findings
Addressed in this PR
The mixed error channel is intentional (D5) and the proxy-freshness item is an out-of-scope follow-up. 21 tests green, typecheck clean. |
platypii added a commit
that referenced
this pull request
Jun 29, 2026
… record Review follow-up (PR #199): an incomplete oidc shape (refreshToken but no accessJwt) on a hand-edited record no longer returns null and drop a working static `token`; it falls through to the static branch. Added boundary tests: the static fallthrough, a malformed-oidc drop, resolveToken on an oidc record, and a skew-window-boundary refresh.
platypii added a commit
that referenced
this pull request
Jun 29, 2026
…(T4) (#203) * remote/oidc: discriminated credential record + session-aware resolve (T4) Extends the 0600 credential store to carry OIDC sessions alongside the existing static tokens (LLP 0046 D4), and adds the attach-path resolver (LLP 0046 D5): - Records gain a kind discriminator. readCredentials normalizes each record; a legacy token-only record (no kind) reads as static, so existing files keep working without a rewrite. writeToken now stamps kind: 'static'. - writeSession(stateDir, target, { refreshToken, accessJwt, expiresAt, org }) writes a kind: 'oidc' record through the same atomic 0600 path. - resolveAccessJwt({ target, env, stateDir, identityBase, now, fetchImpl }): env override wins; a static record returns its token; an oidc record returns a fresh access JWT, refreshing + persisting when the cached one is within a 60s skew of expiry, and propagating a refresh failure (typed invalid_grant). resolveToken stays for the stdio proxy. - removeToken drops either kind (whole-record delete, unchanged). - types.d.ts: RemoteStaticRecord / RemoteOidcRecord / the union. Unit tests cover the round-trip, legacy normalization, env override, fresh vs stale refresh + persistence, and failure propagation. Full suite green. * remote/oidc: normalizeRecord keeps a usable static token on a corrupt record Review follow-up (PR #199): an incomplete oidc shape (refreshToken but no accessJwt) on a hand-edited record no longer returns null and drop a working static `token`; it falls through to the static branch. Added boundary tests: the static fallthrough, a malformed-oidc drop, resolveToken on an oidc record, and a skew-window-boundary refresh.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chunk 3 of the OIDC client login (LLP 0046-0048). Stacked on #198 (base
chunk2-identity-oidc-login); review/merge after #197, #198.What
kinddiscriminator (LLP 0046 D4).readCredentialsnormalizes; a legacytoken-only record reads asstatic(read-implicit migration).writeTokennow stampskind: 'static'.writeSession(...)writes akind: 'oidc'record through the same atomic 0600 path.resolveAccessJwt({...})(LLP 0046 D5): env override wins; static returns its token; oidc returns a fresh JWT, refreshing + persisting within a 60s skew of expiry and propagating aninvalid_grantfailure.resolveTokenstays for the stdio proxy.removeTokendrops either kind.types.d.tsgains the record interfaces.Tests
remote-credentials-oidc.test.js(10 tests) + updated static tests; fullnpm testgreen (1515 pass); typecheck clean.🤖 Generated with Claude Code