feat: acquire client-credentials access inline during the invoking command - #28
Conversation
…mmand A context whose identity is client-credentials needs no login: the secret is already on the machine. The shell reads it from the variable the identity names into process memory, spends it on one grant scoped to the module's own request, and applies the browser source's issued-token verification unchanged. Nothing is written to the state root or the OS secure store. A refusal to narrow and a rejected credential are told apart, because they send the user to different places. A rejected credential is never reported as a login to run: wso2 login refuses this kind of identity outright. The broker's credential read is generalized to name what the variable holds, so the guidance a user receives reads as an instruction for both kinds. Claude-Session: https://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp
The CI rows of the acceptance chain: a client-credentials context with the secret exported hands the reference module a token the issuer vouches for, carrying only the permission it asked for; a job that forgot the variable is told which one to set; and under WSO2_NON_INTERACTIVE the login refuses as not required while the command it would have preceded still succeeds. Claude-Session: https://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp
The client-credentials grant went out through golang.org/x/oauth2 while the refresh grant posted its own form, so the package read a token endpoint's answer two ways and turned the same invalid_scope into two different refusals. Both now share one request helper: the caller supplies the grant's own members, and how a client identifies itself, how an answer is read, and what counts as a refusal to narrow are each decided once. Doing so lets the inline grant honor the broker clock, so its expiry is the shell's own arithmetic and a test can pin it. A deployment that answers with something the shell cannot classify is now told apart from one that did not answer at all, instead of both claiming the issuer was unreachable. The fake issuer validates its scope modes rather than treating a typo as the permissive default, and decodes HTTP Basic credentials as RFC 6749 requires. Claude-Session: https://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp
The non-interactive check asserted only that an inline identity refuses a login as not required, which it does with or without the variable set — so nothing proved the non-interactive guard was still live beside it. The test now runs an interactive and an inline deployment in the same environment: the first is refused as non-interactive, the second as needing no login, and the command the job actually runs still succeeds. Claude-Session: https://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Implements #22 (part of #17). Fifth PR of the
wso2 loginstack — stacked on #27; only the last four commits are new here.What this does
clientCredentialsSource: a command against a client-credentials context acquires access inline — the shell reads the client secret from the environment variable the identity names into process memory only, runs the grant with the module's requested scopes, and applies the same issued-token scope/audience verification as the browser source. Nothing touches the filesystem, keychain, or module environment; no login step exists for CI.auth.credential_unavailablewith user guidance naming the variable and a module-safe problem naming nothing; a rejected secret (401/invalid_client) maps to the same code rather than masquerading as an unreachable issuer; an issuer that will not narrow →auth.narrowing_unavailable.requestTokenhelper, Basic auth for confidential clients, formclient_idfor public ones) instead of half x/oauth2, half hand-rolled.Verification
Full
go test ./... -race -count=1green (acceptance gate 155s);golangci-lint run0 issues. Highlight: one test drives a singleWSO2_NON_INTERACTIVEenvironment to all three answers — browser login refusesauth.non_interactive, inline login refusesauth.login_not_required, inline module run succeeds with an introspectable token. Secret-disclosure sweeps cover problems, output surfaces, and (one layer down, ininternal/rpc) the module environment.Stack
feature/loginfeat/login-context-schema-v2wso2 loginPKCE →feat/login-auth-testbedfeat/login-browser-pkcefeat/login-broker-refreshhttps://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp