Skip to content

fix(auth): require Bearer/Basic scheme and route exclusively#1677

Open
AmanGIT07 wants to merge 1 commit into
mainfrom
fix-auth-scheme-strict-parsing
Open

fix(auth): require Bearer/Basic scheme and route exclusively#1677
AmanGIT07 wants to merge 1 commit into
mainfrom
fix-auth-scheme-strict-parsing

Conversation

@AmanGIT07
Copy link
Copy Markdown
Contributor

Summary

The Authorization header parser in pkg/server/connect_interceptors/session.go accepted credentials with no scheme prefix, populated both the token and secret metadata slots from a single header, and matched Bearer/Basic case-sensitively. This PR tightens the parser so that the scheme is required, matched case-insensitively, and routes credentials to exactly one slot.

Changes

  • Add extractAuthCredentials(header, scheme) — returns the credentials portion of an Authorization header, matching the scheme case-insensitively via strings.EqualFold (RFC 7235).
  • Add applyAuthorizationHeader(md, header, patConf) — routes Bearer credentials to UserTokenGatewayKey (PAT or Frontier-issued JWT) and Basic credentials to UserSecretGatewayKey. The two paths are mutually exclusive.
  • Replace the three duplicated strings.TrimPrefix blocks in WrapStreamingHandler, WrapUnary, and UnaryConnectRequestHeadersAnnotator with a single call to the helper.
  • Add session_test.go covering the helper with 14 scheme-parsing cases, 14 routing cases (PAT, valid JWT, expired JWT, JWT missing JTI, Basic, mixed-case schemes, bare credentials, unknown scheme, empty), and an empty-PAT-prefix edge case.

Behavior

Authorization header Before After
Bearer fpt_xxx token + secret both set token only
bearer fpt_xxx / BEARER fpt_xxx secret slot got junk token only
fpt_xxx (no scheme) token set — accepted nothing set — rejected
Basic Y2lkOnNlYw== secret + token slot got junk secret only
basic Y2lkOnNlYw== secret slot got basic Y2lkOnNlYw== (broken) secret only
Y2lkOnNlYw== (no scheme) secret set — accepted nothing set — rejected
Unknown scheme secret slot got the full header nothing set

Test Plan

  • go test ./pkg/server/connect_interceptors/... — all subtests pass
  • golangci-lint run ./pkg/server/connect_interceptors/... — clean
  • Manual verification with Authorization: Bearer fpt_<pat> against a running server

🤖 Generated with Claude Code

The Authorization header parser used strings.TrimPrefix, which is a no-op
when the prefix is absent. As a result a bare PAT (no scheme) was treated
as a token, a bare base64 credential was treated as a secret, and every
Bearer header also leaked into the secret slot because both blocks ran
unconditionally. Scheme matching was also case-sensitive, breaking
RFC 7235.

Replace the parser with a small helper that requires the scheme, matches
it case-insensitively via strings.EqualFold, and routes Bearer to
UserTokenGatewayKey and Basic to UserSecretGatewayKey — never both.

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

vercel Bot commented Jun 5, 2026

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

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jun 5, 2026 10:27am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

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: 94afa5b2-1320-4f4c-9c9e-55fce84cfd53

📥 Commits

Reviewing files that changed from the base of the PR and between bd6a145 and 4b47824.

📒 Files selected for processing (2)
  • pkg/server/connect_interceptors/session.go
  • pkg/server/connect_interceptors/session_test.go

📝 Walkthrough

Summary by CodeRabbit

  • Refactor

    • Centralized and optimized authorization header processing for improved code consistency and maintainability.
  • Tests

    • Added comprehensive test suite validating credential handling, authentication scheme routing, edge case scenarios, legacy header compatibility, and context propagation across multiple request types.

Walkthrough

This PR consolidates Authorization header parsing across three interceptor methods into shared helper functions, then adds comprehensive test coverage. Bearer/JWT/PAT tokens are routed to the token gateway key, Basic credentials to the secret gateway key, with case-insensitive scheme matching and JWT expiration validation. Tests validate credential extraction, routing across all transports, backward compatibility with existing X-User-Token header, and priority rules.

Changes

Authorization Header Refactoring

Layer / File(s) Summary
Authorization helpers implementation and call-site refactoring
pkg/server/connect_interceptors/session.go
New extractAuthCredentials and applyAuthorizationHeader helpers handle case-insensitive Bearer/Basic scheme matching, JWT expiration validation, and PAT prefix logic. WrapStreamingHandler, WrapUnary, and UnaryConnectRequestHeadersAnnotator now delegate to the shared helper instead of inline token parsing, routing Bearer/JWT/PAT to UserTokenGatewayKey and Basic credentials to UserSecretGatewayKey.
Test infrastructure and helper utilities
pkg/server/connect_interceptors/session_test.go
Test package imports and metadata capture helpers (captureMetadataUnary, captureMetadataStreaming, captureMetadataAnnotator) enable testing across all three transport types. Fake streaming connection and test interceptor factory support test execution.
Credential extraction and authorization routing tests
pkg/server/connect_interceptors/session_test.go
TestExtractAuthCredentials validates case-insensitive matching and separator handling. buildJWT helper constructs signed tokens for testing. TestApplyAuthorizationHeader variants verify Bearer/JWT/PAT and Basic routing, PAT prefix fallback, and rejection of invalid/expired/malformed credentials.
Integration and regression tests
pkg/server/connect_interceptors/session_test.go
TestSessionInterceptor_AuthFlow_EndToEnd validates credential routing across all transports and metadata context propagation. Regression tests confirm X-User-Token backward compatibility, priority when both auth headers present, empty-header behavior, and first-Authorization-header-wins semantics.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@AmanGIT07
Copy link
Copy Markdown
Contributor Author

Test results

Unit tests

go test -race ./pkg/server/connect_interceptors/... — all pass.

Added to session_test.go:

  • TestSessionInterceptor_AuthFlow_EndToEnd — 12 header scenarios × 3 transports (WrapUnary, WrapStreamingHandler, UnaryConnectRequestHeadersAnnotator) = 36 subtests; asserts via authenticate.GetTokenFromContext / GetSecretFromContext
  • TestSessionInterceptor_XUserTokenHeader_StillWorks
  • TestSessionInterceptor_XUserTokenAndAuthorization_CoexistWithoutClobber
  • TestSessionInterceptor_NoAuthHeaders_ProducesEmptyMetadata
  • TestSessionInterceptor_BearerAndBasic_BothPresent_BearerWins

Live E2E against a local Frontier

# Authorization header Endpoint HTTP
1 Bearer <real PAT> GetCurrentUser 200
2 bearer <real PAT> (lowercase) GetCurrentUser 200
3 BEARER <real PAT> (uppercase) GetCurrentUser 200
4 Bearer <real PAT> (extra whitespace) GetCurrentUser 200
5 Bearer <minted JWT> GetCurrentUser 200
6 Basic <real client creds> AuthToken (client_credentials) 200
7 basic <real client creds> (lowercase) AuthToken (client_credentials) 200
8 Cookie sid=... only GetCurrentUser 200
9 Cookie + Bearer <junk> GetCurrentUser 200
10 Cookie + Basic <junk> GetCurrentUser 200
11 <real PAT> (no scheme) GetCurrentUser 401
12 <minted JWT> (no scheme) GetCurrentUser 401
13 <real client creds> (no scheme) AuthToken 401
14 Basic <real PAT> (wrong scheme) GetCurrentUser 401
15 Bearer <real client creds> (wrong scheme) AuthToken 401
16 DPoP <real PAT> (unknown scheme) GetCurrentUser 401
17 Bearer randomstring GetCurrentUser 401
18 Bearer fpt_doesnotexist GetCurrentUser 401
19 Bearer not.a.real.jwt.value GetCurrentUser 401
20 Basic Y2lkOnNlYw== (fake) GetCurrentUser 401
21 Basic garbage GetCurrentUser 401
22 Basic d3JvbmdpZDp3cm9uZ3NlYw== (wrong creds) AuthToken 401
23 (no auth header) GetCurrentUser 401

Rows 11–13 are the rejections that the prior code accepted (bare credentials with no scheme). Rows 14–16 are wrong-scheme combinations that the prior code partially honored by writing the raw header into the secret slot.

@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 27009627415

Coverage increased (+0.2%) to 43.406%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: 23 of 23 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 38036
Covered Lines: 16510
Line Coverage: 43.41%
Coverage Strength: 12.21 hits per line

💛 - Coveralls

@AmanGIT07 AmanGIT07 enabled auto-merge (squash) June 5, 2026 11:52
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