Skip to content

feat(ogar-auth): issuer-qualified external identities and a verified-login boundary (PR-00) - #366

Merged
AdaWorldAPI merged 1 commit into
mainfrom
feat/ogar-oidc-principal-contract
Nov 4, 2026
Merged

AdaWorldAPI merged 1 commit into
mainfrom
feat/ogar-oidc-principal-contract

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

PR-00 adds the identity contract that an external OIDC provider such as ZITADEL plugs into. It adds no OIDC client.

  • identity
    • Issuer is the byte-exact OIDC iss. The value is not normalised. It is accepted over https, and over http for localhost only.
    • ExternalSubject holds the OIDC sub.
    • ExternalIdentity is the pair (issuer, subject).
  • user
    • LocalUserStore keeps external identities in a unique index.
    • try_insert and bind_external reject each of the following with a StoreConflict:
      • a duplicate user id;
      • a duplicate subject;
      • a binding already held by another user;
      • a duplicate identity on the same user.
    • insert keeps its signature and panics (#[track_caller]) on conflicting input.
    • UserStore::resolve_external defaults to None, so a store that does not implement it fails closed.
  • federation
    • TrustedIssuers is the registry of trusted issuers. It maps an issuer to its provider label and refuses a second, conflicting registration.
    • AssertionVerifier is the interface the OIDC adapter in PR-01 implements.
    • establish() returns a VerifiedLogin. It runs: verify the assertion → check the issuer is trusted → resolve (iss, sub) → AuthContext::federated, with the provider label taken from the registry.
    • VerifiedLogin has private fields and no public constructor (pinned by compile_fail doctests). A hand-built AuthContext::federated yields an AuthenticatedUser but never a VerifiedLogin.

Falsifiers

# claim test
F1 the same sub under two issuers names two different users federation_boundary::same_subject_different_issuer_*
F2 a duplicate binding is refused, so resolution is never ambiguous user::tests::*duplicate*, bind_external_rejects_*
F3 email never links an account federation_boundary::email_is_never_a_link
F4 a manual AuthContext::federated is not proof of a login hand_built_context_gives_a_user_but_no_login, compile_fail doctests
F5 switching provider does not change the authorization decision for an explicitly bound actor ogar-rbac tests::federated::provider_switch_keeps_decision_for_explicitly_bound_actor (+ two companion tests)

Disable runs (each turned red, then the tree was restored)

  • D1: lookup by subject only → F1 tests red.
  • D2: holder check removed from bind_external → the duplicate-external test red.
  • D3: email fallback in establish → F3 red.
  • D4: VerifiedLogin fields made pub → compile_fail doctest red.
  • D5: provider added to the actor-context subject → two_bindings_resolve_to_one_canonical_user and the F5 rbac test red.

Gates

  • cargo test -p ogar-auth -p ogar-rbac: green (26 + 13 + 25 tests, 2 doctests).
  • cargo clippy -p ogar-auth -p ogar-rbac --all-targets -- -D warnings: clean.
  • cargo fmt --check: clean.

Compatibility

  • Additive. No change to class ids, ActorContext, kiosk, local auth or UserStore semantics.
  • LocalUserStore::insert now panics on conflicting input (a duplicate id or subject). Before, the second insert silently overwrote the first. Callers that need to handle a conflict use try_insert.
  • A federated login resolves only through an explicit bind_external. A provider-scoped identity never links by email or by subject alone.
  • Full rules, migration and trust boundary: docs/AUTH-EXTERNAL-IDENTITY.md.

Dependency SHAs

  • Base: OGAR 5b82ae0fc26e4442e1a6814ad48ea4444fac6356.
  • lance-graph-contract: as resolved by the workspace at that base, f859d484…, on branch main, unpinned. Recorded in docs/UPSTREAM-DEPS.md.

Not in scope

  • The OIDC adapter (PR-01: provider-agnostic, implements AssertionVerifier).
  • ZITADEL deployment.
  • report-rs login routes.
  • Role policies.

Generated by Claude Code

…login boundary (PR-00)

- identity: Issuer (byte-exact OIDC iss, no normalisation), ExternalSubject,
  ExternalIdentity keyed by (issuer, subject).
- user: LocalUserStore keeps external identities in a unique index;
  try_insert / bind_external refuse duplicate user ids, subjects, bindings
  held by another user and duplicate identities; insert keeps its signature
  and panics on conflicting input; UserStore::resolve_external defaults to
  None (fail closed). AuthContext::federated and AuthenticatedUser are
  documented as descriptive, not proof.
- federation: TrustedIssuers registry, AssertionVerifier seam for the
  provider-agnostic OIDC adapter (PR-01), establish() -> VerifiedLogin with
  private fields and no public constructor; resolution is (iss, sub) only,
  never email; the provider label comes from the registry.
- Tests F1-F5 (ogar-auth integration tests + ogar-rbac federated module),
  compile_fail doctests for VerifiedLogin. Disable runs: subject-only lookup,
  dropped holder check, email fallback, public VerifiedLogin fields, provider
  in the identity envelope — each turns its test red.
- docs/AUTH-EXTERNAL-IDENTITY.md (rules, compatibility, migration, trust
  boundary), docs/UPSTREAM-DEPS.md row with dependency SHAs.

No class id, ActorContext, kiosk or local-auth behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMQJzQD5KvNm8BtbsWG
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 253a81dd-cc92-4caa-b8b8-2126751f8e3c
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review November 4, 2026 22:28
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T22:29:52.998852Z e3ed938 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@AdaWorldAPI
AdaWorldAPI merged commit 7bb3ad7 into main Nov 4, 2026
4 checks passed
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.

2 participants