Skip to content

feat(auth): enforce per-endpoint credential restrictions and make the auth chain order-independent#1718

Open
AmanGIT07 wants to merge 1 commit into
mainfrom
feat/enforce-credential-assertions
Open

feat(auth): enforce per-endpoint credential restrictions and make the auth chain order-independent#1718
AmanGIT07 wants to merge 1 commit into
mainfrom
feat/enforce-credential-assertions

Conversation

@AmanGIT07

Copy link
Copy Markdown
Contributor

Summary

Make per-endpoint credential restrictions actually enforced and the authentication chain order-independent. GetPrincipal previously returned the interceptor-cached principal without consulting the per-call assertion list, so a handler's credential restriction (e.g. AuthToken's) had no effect.

Closes #1698

Changes

  • Add Principal.AuthVia (which credential authenticated the principal); GetPrincipal enforces the caller's assertion list against it in one place.
  • AuthToken: accept PAT explicitly; reject credential types outside its list (e.g. an already-issued access token); return the auth status code (401) on rejection instead of wrapping as 500.
  • Session endpoints (ListSessions/RevokeSession): their existing {session} restriction is now enforced — non-session credentials are rejected. Behavior change: a bearer access-token caller of these endpoints is now rejected (PATs were already denied there).
  • GetByJWT returns classifiable errors so jwt_grant skips tokens that aren't service-user grants (not a JWT, non-UUID key id, or unknown credential) instead of hard-failing; only a genuine credential verification failure is terminal.
  • Remove OpaqueTokenClientAssertion (it shared an authenticator with ClientCredentialsClientAssertion, making provenance ambiguous); opaque-token service-user credentials still validate via the client-credentials path.

Technical Details

  • The assertion list is treated as an order-independent set; resolution iterates the canonical order, so correctness no longer depends on assertion ordering.
  • GetByJWT requires the JWT key id to be a UUID (a service-user credential id) before performing a credential lookup.

Test Plan

  • Build and type checking passes
  • Manual testing completed
  • Unit: GetByJWT error classification; PAT resolves regardless of assertion order; per-endpoint restrictions (session-only and the AuthToken list) accept/reject the right credentials; AuthToken returns 401 on a rejected credential.
  • e2e (test/e2e/regression/authentication_test.go): PAT → AuthToken returns a sub_type=app/pat JWT that authenticates; the minted token cannot be re-exchanged at AuthToken (401). Run with make e2e-test (Docker); not run in this environment.

SQL Safety

N/A — no *_repository.go or goqu changes.

…h chain order-independent

GetPrincipal now records how a principal authenticated (Principal.AuthVia) and enforces the caller's assertion list against it, so AuthToken and the session endpoints actually restrict credential types. GetByJWT returns classifiable errors so the authenticator chain is order-independent, and the redundant opaque assertion is removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jun 30, 2026 9:56am

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1159017a-a553-458a-9886-7c32a827baca

📥 Commits

Reviewing files that changed from the base of the PR and between 5103b7f and 8ae6cad.

📒 Files selected for processing (10)
  • core/authenticate/authenticate.go
  • core/authenticate/authenticators.go
  • core/authenticate/service.go
  • core/authenticate/service_test.go
  • core/serviceuser/errors.go
  • core/serviceuser/service.go
  • core/serviceuser/service_test.go
  • internal/api/v1beta1connect/authenticate.go
  • internal/api/v1beta1connect/authenticate_test.go
  • test/e2e/regression/authentication_test.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for additional credential types during authentication, including PAT-based login flows.
    • Authentication results now carry the credential type used, enabling more precise access handling.
  • Bug Fixes

    • Improved token validation so non-JWT or malformed credentials are handled more consistently.
    • Authentication now respects allowed credential types more strictly and rejects disallowed exchanges.
    • Token exchange now returns clearer authentication failures instead of generic internal errors.

Walkthrough

Adds AuthVia ClientAssertion to Principal, removes OpaqueTokenClientAssertion, tightens serviceuser.GetByJWT to return typed sentinel errors, updates GetPrincipal to annotate and filter principals by AuthVia, adds PATClientAssertion to the AuthToken handler allowlist, and pins behavior with unit and e2e tests.

Changes

PAT AuthToken enforcement and AuthVia tracking

Layer / File(s) Summary
Principal.AuthVia field and serviceuser sentinel errors
core/authenticate/authenticate.go, core/serviceuser/errors.go
Adds AuthVia ClientAssertion to Principal, removes OpaqueTokenClientAssertion constant from the allowlist, and adds ErrTokenNotJWT sentinel to serviceuser.
serviceuser.GetByJWT strict error classification
core/serviceuser/service.go, core/serviceuser/service_test.go
GetByJWT wraps parse failures as ErrTokenNotJWT, enforces KID presence/type/UUID, returns ErrInvalidCred on signature failure; new tests cover all classification branches.
Authenticator map and JWT-grant skip logic
core/authenticate/authenticators.go
Removes OpaqueTokenClientAssertion from the authenticator map; authenticateWithJWTGrant now returns errSkip for non-JWT/missing-key cases and ErrUnauthenticated only for invalid credentials.
GetPrincipal: AuthVia annotation and assertion filter
core/authenticate/service.go
Sets principal.AuthVia after successful authentication and enforces the caller-supplied assertions filter against a context-cached principal's AuthVia.
AuthToken: PATClientAssertion allowlist and error passthrough
internal/api/v1beta1connect/authenticate.go
Adds PATClientAssertion to AuthToken's GetLoggedInPrincipal call and removes the connect.CodeInternal error wrap, returning the raw error instead.
Unit tests
core/authenticate/service_test.go, internal/api/v1beta1connect/authenticate_test.go
Updates existing GetPrincipal expectations to assert AuthVia; adds TestService_GetPrincipal_JWTGrantSkipsNonGrantToken, TestService_GetPrincipal_RestrictsByAuthVia, and TestConnectHandler_AuthToken_RejectsDisallowedCredential.
E2E regression: PAT AuthToken exchange
test/e2e/regression/authentication_test.go
Enables PAT in test server config; new subtest provisions a PAT, exchanges via AuthToken, validates minted JWT sub_type, confirms GetCurrentUser, and asserts re-exchange fails with CodeUnauthenticated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • raystack/frontier#1447: Modifies the same authenticate.Principal struct by adding ResolveSubject(), directly related to the AuthVia addition in this PR.
  • raystack/frontier#1522: Extends PAT principal handling through auth interception, sharing the same PAT/principal context model that AuthVia builds on.

Suggested reviewers

  • whoAbhishekSah
  • rohilsurana
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR enforces handler assertion lists via AuthVia, supports PAT exchange, rejects disallowed credentials, and makes JWT auth chain skipping order-independent as requested.
Out of Scope Changes check ✅ Passed The added serviceuser and test changes are all in support of authentication restriction, token classification, and PAT exchange, with no unrelated scope visible.

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.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 28435903758

Coverage increased (+0.1%) to 43.888%

Details

  • Coverage increased (+0.1%) from the base build.
  • Patch coverage: 4 uncovered changes across 2 files (24 of 28 lines covered, 85.71%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
core/authenticate/authenticators.go 8 5 62.5%
core/serviceuser/service.go 12 11 91.67%
Total (4 files) 28 24 85.71%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 37088
Covered Lines: 16277
Line Coverage: 43.89%
Coverage Strength: 12.39 hits per line

💛 - Coveralls

@AmanGIT07 AmanGIT07 requested a review from rohilsurana June 30, 2026 10:19
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.

Enforce per-endpoint credential restrictions (AuthToken + session endpoints) and make the auth chain order-independent

2 participants