Skip to content

feat: acquire client-credentials access inline during the invoking command - #28

Merged
kanushka merged 4 commits into
feat/login-broker-refreshfrom
feat/login-client-credentials
Aug 5, 2026
Merged

feat: acquire client-credentials access inline during the invoking command#28
kanushka merged 4 commits into
feat/login-broker-refreshfrom
feat/login-client-credentials

Conversation

@kanushka

@kanushka kanushka commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Implements #22 (part of #17). Fifth PR of the wso2 login stack — 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.
  • Absent/blank secret → auth.credential_unavailable with 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.
  • Both OAuth grants now speak one token-request dialect (shared requestToken helper, Basic auth for confidential clients, form client_id for public ones) instead of half x/oauth2, half hand-rolled.
  • Fake issuer gained client-grant scope modes and secret verification it turned out never to have.

Verification

Full go test ./... -race -count=1 green (acceptance gate 155s); golangci-lint run 0 issues. Highlight: one test drives a single WSO2_NON_INTERACTIVE environment to all three answers — browser login refuses auth.non_interactive, inline login refuses auth.login_not_required, inline module run succeeds with an introspectable token. Secret-disclosure sweeps cover problems, output surfaces, and (one layer down, in internal/rpc) the module environment.

Stack

  1. feat: context schema v2 with identities, v1 compatibility read, and selection #24 schema v2 → feature/login
  2. feat: keychain session store and fake OIDC issuer test bed #25 test bed → feat/login-context-schema-v2
  3. feat: wso2 login runs browser authorization code with PKCE end to end #26 wso2 login PKCE → feat/login-auth-testbed
  4. feat: broker derives module access from the login session by scoped refresh #27 broker scoped refresh → feat/login-browser-pkce
  5. this PRfeat/login-broker-refresh
  6. Live smoke, Asgardeo empirical experiments, and the login walkthrough #23 closes the stack.

https://claude.ai/code/session_01RojiAgW9hi3b9f9G6ZXBVp

…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
@kanushka
kanushka requested a review from hevayo as a code owner August 5, 2026 03:08
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e49ac79-0408-4813-9dd7-aa38d768ca24

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@kanushka
kanushka merged commit 6b7afc1 into feature/login Aug 5, 2026
6 checks passed
@kanushka
kanushka deleted the feat/login-client-credentials branch August 5, 2026 04:53
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.

3 participants