Skip to content

fix: authenticate against the context's home realm, not the target realm - #32

Merged
NathaelB merged 1 commit into
mainfrom
fix/auth-always-uses-context-realm
Sep 2, 2026
Merged

fix: authenticate against the context's home realm, not the target realm#32
NathaelB merged 1 commit into
mainfrom
fix/auth-always-uses-context-realm

Conversation

@NathaelB

@NathaelB NathaelB commented Sep 2, 2026

Copy link
Copy Markdown
Member

Bug

client and user commands authenticated against the resolved target realm (--realm, falling back to the context's default) instead of the context's home realm — the realm where the context's client_id/client_secret are actually registered.

Repro: a context configured against master (ferris-ctl context add local --url http://localhost:3333 --client-id cli --client-secret *** --realm master), then:

$ ferris-ctl user list --realm mestier
error: api request failed with status 404 Not Found: {"code":"E_NOT_FOUND","status":404,"message":"Resource not found"}

Server logs show the CLI attempting the client_credentials token exchange against /realms/mestier/protocol/openid-connect/token instead of /realms/master/... — the cli client doesn't exist in mestier, so the server 404s. realm.rs already got this right (always authenticates via context.realm, independent of whatever realm a subcommand targets); client.rs and user.rs didn't.

Fix

Added an auth_client(context) helper to client.rs and user.rs, mirroring the one already in realm.rs: it authenticates strictly via context.realm, never the command's resolved target realm. The target realm (resolve_realm) is still used, but only to build the resource path of each API call — authorization for cross-realm operations is left to the server, per #21's "Expected behaviour".

This is the root cause of #21 (--realm refused / failing whenever it differs from the session realm) — the issue's literal repro (no credentials available) is one symptom of the same bug: the stored-credentials cache key and the client_credentials exchange were both keyed off the target realm instead of the home realm.

Issue

Closes #21.

Verification

This repo shares a local FerrisKey instance (http://localhost:3333, context local, realm master) with a mestier realm and a nathaelb user in it — used it to reproduce and verify directly, not just unit tests:

  • Before the fix (main): user list --realm mestier → 404, as in the bug report.
  • After the fix: user list --realm mestier and client list --realm mestier both succeed, returning mestier's users/clients while authenticating against master. user list (no --realm, same-realm case) still works.

Test plan

  • cargo build --workspace
  • cargo test --workspace (62 passed, +2 new regression tests: auth_client_requires_realm_on_context in client.rs and user.rs)
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • Manual reproduction + fix verification against a running FerrisKey server (see above)

Summary by CodeRabbit

  • Bug Fixes
    • Client and user management commands now authenticate through the CLI’s configured home realm, providing more consistent access across target realms.
    • Added a clear error when no home realm is configured, instead of attempting authentication without the required context.
    • Updated client and user operations—including listing, viewing, creating, deleting, and role assignment—to use the corrected authentication flow.
  • Tests
    • Added regression coverage for authentication behavior when a home realm is configured or missing.

client.rs and user.rs authenticated against the resolved target realm
(--realm or context default) instead of the context's configured home
realm, where the OAuth client is actually registered. Any command whose
target realm differed from the client's home realm failed: the token
exchange (or the cached-credentials lookup) was attempted against the
wrong realm.

realm.rs already authenticated correctly (always via context.realm,
independent of the operation's target realm) — client.rs and user.rs
now follow the same auth_client(context) pattern.

Root cause of #21.
@NathaelB NathaelB self-assigned this Sep 2, 2026
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 40373c76-3a0f-4eae-804e-5fbb7c353da5

📥 Commits

Reviewing files that changed from the base of the PR and between 01c681d and f00f73a.

📒 Files selected for processing (2)
  • libs/ferriskey-cli-core/src/client.rs
  • libs/ferriskey-cli-core/src/user.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Client and user commands now authenticate against the selected context's home realm. New MissingAuthRealm errors handle contexts without a configured realm. All affected handlers and regression tests use the new authentication helpers.

Changes

Context-based authentication

Layer / File(s) Summary
Authentication helpers and errors
libs/ferriskey-cli-core/src/client.rs, libs/ferriskey-cli-core/src/user.rs
Both modules add MissingAuthRealm errors. Their auth_client helpers authenticate with context.realm.
Command wiring and regression coverage
libs/ferriskey-cli-core/src/client.rs, libs/ferriskey-cli-core/src/user.rs
Client and user command handlers call auth_client(&context). Tests verify the error returned when the context has no realm.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🔵 Low · up to f00f7

The CLI now authenticates in the context’s home realm while targeting resources in the selected realm, enabling intended cross-realm administration. Merge is reasonable with explicit owner awareness that the server must enforce authorization for these cross-realm client and user operations.

Sequence Diagram(s)

sequenceDiagram
  participant CommandHandler
  participant AuthClient as auth_client
  participant SessionClient as session::authenticated_client
  CommandHandler->>AuthClient: Request authenticated client
  AuthClient->>AuthClient: Read context.realm
  AuthClient->>SessionClient: Authenticate with home realm
  SessionClient-->>CommandHandler: Return FerriskeyClient
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: authentication now uses the context's home realm instead of the target realm.
Linked Issues check ✅ Passed The changes meet issue #21. Client and user commands now authenticate against the context's configured home realm while using the selected target realm for the requested operation. This supports autho…
Out of Scope Changes check ✅ Passed The changes are limited to authentication behavior for client and user commands, related error variants, and regression tests. These changes support the objectives in issue #21 and no unrelated code c…
Full details: Linked Issues check

Explanation

The changes meet issue #21. Client and user commands now authenticate against the context's configured home realm while using the selected target realm for the requested operation. This supports authorized cross-realm requests without local credential-realm rejection.

Full details: Out of Scope Changes check

Explanation

The changes are limited to authentication behavior for client and user commands, related error variants, and regression tests. These changes support the objectives in issue #21 and no unrelated code changes are identified.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/auth-always-uses-context-realm

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.

@NathaelB
NathaelB merged commit 15eaaae into main Sep 2, 2026
4 checks passed
@NathaelB
NathaelB deleted the fix/auth-always-uses-context-realm branch September 2, 2026 22:19
NathaelB added a commit that referenced this pull request Sep 2, 2026
GitHub's branch-update rebase of this PR onto main (post-#32) replayed
this branch's additive commit without semantic conflict: the new
remove-role/roles/set-password functions still called the now-removed
authenticate(context, realm) helper instead of #32's auth_client(context).
NathaelB added a commit that referenced this pull request Sep 2, 2026
* feat(user): add set-password, remove-role and roles commands

Refs #23

* fix(user): correct set-password endpoint (verified against a live server)

set_user_password guessed PUT realms/{realm}/users/{id}/password
(404). The real endpoint is PUT realms/{realm}/users/{id}/reset-password
- found via the 405 Allow header while probing against a running
FerrisKey server.

* fix(user): use auth_client after rebase reintroduced authenticate calls

GitHub's branch-update rebase of this PR onto main (post-#32) replayed
this branch's additive commit without semantic conflict: the new
remove-role/roles/set-password functions still called the now-removed
authenticate(context, realm) helper instead of #32's auth_client(context).
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.

--realm is refused whenever it differs from the session realm

1 participant