Skip to content

Conversation

@sehkone
Copy link
Contributor

@sehkone sehkone commented Jan 26, 2026

Closes #772
Closes #768
Closes #770

- implement mTLS validation and context JWT checks with dedicated errors
- gate jwt-only GraphQL modules/tests for auth-mtls builds
- add mTLS-focused tests and adjust feature-specific test setup

Closes #768
@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 79.46210% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.92%. Comparing base (35f683b) to head (1489c47).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/lib.rs 66.47% 58 Missing ⚠️
src/auth/mtls.rs 89.47% 24 Missing ⚠️
src/graphql/cert.rs 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #773      +/-   ##
==========================================
+ Coverage   75.27%   75.92%   +0.65%     
==========================================
  Files          71       72       +1     
  Lines       19779    19935     +156     
==========================================
+ Hits        14888    15135     +247     
+ Misses       4891     4800      -91     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sehkone sehkone requested a review from sophie-cluml January 26, 2026 06:15
}

let client_verifier = rustls::server::WebPkiClientVerifier::builder(Arc::new(root_store))
.allow_unauthenticated()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is allow_unauthenticated adopted here as a temporary workaround? My understanding of the goal is to "enforce client certificate verification", and if that is true, I think we might want to remove allow_unauthenticated in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rationale for allow_unauthenticated():

  1. Keep the loopback bypass working without extra ports or listeners.
  2. Allow the app layer to return explicit HTTP auth errors instead of failing the TLS handshake.

If we later remove the loopback bypass or move it to a separate port/listener, we could enforce TLS client auth here. That change is optional, not required.

Comment on lines 199 to 200
#[tokio::test]
async fn mtls_graphql_request_succeeds() -> anyhow::Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you be open to adding tests for non-successful scenarios? Since auth/mtls.rs defines specific ERR_* constants, I think we might benefit from verifying the rejection behavior for those cases. If adding them now causes is a delay, I think we can create a follow-up issue to track it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sophie-cluml, what you pointed out is something I definitely missed. I appreciate it. I’ve added tests covering the rejection paths.

Comment on lines +414 to +419
if is_local(addr) {
return Ok(schema
.execute(request.data(RoleGuard::Local).data(addr))
.await
.into());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think RoleGuard::Local in auth-jwt feature build served a good purpose, since the GraphQL API endpoint reset_admin_password was only for Local role. However, in auth-mtls feature build, I can see that the account module (and thus reset_admin_password in graphql/account.rs) is not part of the GraphQL API schema at all, so I think the original rationale for is_local() no longer applies for this auth-mtls feature build.

I think it rather has drawback. Currently graphql_handler checks for is_local(addr) and forces RoleGuard::Local. That means even a valid mTLS + Context JWT request from localhost comes in, the request gets downgraded to Local role and the request will be rejected by role guards of other GraphQL API endpoints.

Therefore, I think it might be worth considering to remove the if is_local(addr) branch in L414, and possibly remove the introduction of DISABLE_LOCAL_AUTH_BYPASS_ENV.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the thoughtful feedback—it’s a valid concern. I agree the Local bypass can have drawbacks in auth-mtls, especially when a valid mTLS + Context JWT request comes from localhost.

That said, I’d like to defer this decision until after we fully remove the auth-jwt feature. Once auth-jwt is gone, we can reassess whether the local bypass (and DISABLE_LOCAL_AUTH_BYPASS_ENV) still makes sense and adjust the behavior accordingly.

@sehkone sehkone merged commit f2d6adc into main Jan 27, 2026
18 checks passed
@sehkone sehkone deleted the issue-772 branch January 27, 2026 01:44
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.

Rebase auth-mtls work onto latest main in a new branch Support multi-customer / all-customer access in mTLS Context JWT mTLS + Context JWT

3 participants