Skip to content

feat(router): add SetWildcardScope to bypass @requiresScopes checks#2534

Open
jensneuse wants to merge 10 commits intomainfrom
jensneuse/fix-header-propagation
Open

feat(router): add SetWildcardScope to bypass @requiresScopes checks#2534
jensneuse wants to merge 10 commits intomainfrom
jensneuse/fix-header-propagation

Conversation

@jensneuse
Copy link
Copy Markdown
Member

@jensneuse jensneuse commented Feb 19, 2026

Summary by CodeRabbit

  • New Features

    • Wildcard scope support in router authorization, allowing requests to opt into wildcard access when enabled.
    • Public request context API to enable/disable wildcard scope per request.
  • Tests

    • Added comprehensive tests validating wildcard-scope behavior across authenticated, unauthenticated, and mixed authorization scenarios.

Adds SetWildcardScope(bool) to RequestContext, allowing custom modules to mark a request as satisfying all @requiresScopes checks without needing to enumerate every scope from the schema. Authentication is still enforced — unauthenticated requests are rejected before scope checks are evaluated. The flag is stored as a Go context value and checked in CosmoAuthorizer.validateScopes before the OR-of-AND scope matching logic. Closes #2490.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

jensneuse and others added 2 commits February 18, 2026 16:53
Allows custom modules to mark a request as having a wildcard scope that
satisfies all @requiresScopes checks. Authentication is still enforced.

Closes #2490

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace require.Contains with exact require.Equal assertions
- Add test for RejectOperationIfUnauthorized + wildcard scope
- Clarify doc comment: authentication is a prerequisite for wildcard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ae4bc7 and de08650.

📒 Files selected for processing (3)
  • router-tests/modules/set_wildcard_scope_test.go
  • router/core/authorizer.go
  • router/core/context.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • router-tests/modules/set_wildcard_scope_test.go
  • router/core/authorizer.go

Walkthrough

Adds a request-level wildcard-scope flag and plumbing to bypass scope validation, a middleware module to set that flag, and tests exercising authorized/unauthorized behavior under various wildcard configurations. The authorizer checks the flag and skips scope checks when present.

Changes

Cohort / File(s) Summary
Request context API
router/core/context.go
Added SetWildcardScope(bool) to RequestContext and wildcardScopeKey to store a wildcard-scope boolean in the request context.
Authorizer scope bypass
router/core/authorizer.go
Added internal wildcardScopeKey usage and hasWildcardScope(ctx) helper; validateScopes short-circuits when wildcard scope is present.
Middleware module
router-tests/modules/custom-set-wildcard-scope/module.go
New SetWildcardScopeModule implementing core.RouterMiddlewareHandler with Middleware that sets the wildcard flag when enabled and a Module() metadata method.
Tests
router-tests/modules/set_wildcard_scope_test.go
New test suite TestCustomModuleSetWildcardScope covering authenticated/unauthenticated requests, disabled wildcard behavior, and RejectOperationIfUnauthorized permutations validating data and error shapes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. 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 accurately describes the main change: adding SetWildcardScope method to bypass @requiresScopes checks, which is the primary objective of the PR.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #2490: adds SetWildcardScope to RequestContext, modifies CosmoAuthorizer to check the flag in validateScopes, and maintains authentication enforcement while bypassing scope checks.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #2490. The modifications to context.go, authorizer.go, and addition of new module tests are directly aligned with implementing the wildcard scope bypass feature.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 19, 2026

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-de2a96e873e1c78b07931ccf7a6358c4e71774f4-nonroot

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.13%. Comparing base (dcd4e0a) to head (d3d20c2).
⚠️ Report is 43 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2534       +/-   ##
===========================================
+ Coverage   35.53%   63.13%   +27.60%     
===========================================
  Files         129      245      +116     
  Lines       11782    26276    +14494     
  Branches      467        0      -467     
===========================================
+ Hits         4187    16590    +12403     
- Misses       7593     8345      +752     
- Partials        2     1341     +1339     
Files with missing lines Coverage Δ
router/core/authorizer.go 90.72% <100.00%> (ø)
router/core/context.go 74.85% <100.00%> (ø)

... and 372 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread router/core/authorizer.go
Comment on lines +80 to +84
func hasWildcardScope(ctx context.Context) bool {
v, ok := ctx.Value(wildcardScopeKey{}).(bool)
return ok && v
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The setter and getter for a context key should not be in different files

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done — moved wildcardScopeKey type to authorizer.go next to hasWildcardScope. The setter in context.go references it from the same package.

require.Equal(t, http.StatusOK, res.StatusCode)
data, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, `{"errors":[{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",0,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",1,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",2,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",3,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",4,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",5,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",6,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",7,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",8,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}},{"message":"Unauthorized to load field 'Query.employees.startDate', Reason: not authenticated.","path":["employees",9,"startDate"],"extensions":{"code":"UNAUTHORIZED_FIELD_OR_TYPE"}}],"data":{"employees":[null,null,null,null,null,null,null,null,null,null]}}`, string(data))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since all the tests use the same query and these responses are bit unwieldy to verify this way, can we make a struct to unmarshal this into so the assertions are more readable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alternatively if these specific errors are unimportant to read can this be a snapshot test?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done — replaced raw JSON string comparisons with a graphQLResponse struct. Tests now unmarshal and assert on individual fields (error count, error codes, messages, missing scopes coordinates). Much more readable.

@jensneuse
Copy link
Copy Markdown
Member Author

Addressed review feedback:

@endigma re: context key in different files — Moved wildcardScopeKey type to authorizer.go next to hasWildcardScope. The setter in context.go references it from the same package.

@endigma re: unwieldy JSON assertions — Replaced raw JSON string comparisons with structured graphQLResponse type. Tests now unmarshal and assert on individual fields (error count, error codes, messages, missing scopes coordinates). Much more readable.

jensneuse and others added 2 commits February 26, 2026 07:31
- Move wildcardScopeKey type to authorizer.go next to hasWildcardScope
- Replace raw JSON string assertions with structured graphQLResponse
  type for readable test assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jensneuse jensneuse requested a review from endigma February 26, 2026 17:07
@github-actions
Copy link
Copy Markdown

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added Stale and removed Stale labels Mar 17, 2026
@Noroth Noroth requested a review from SkArchon as a code owner March 25, 2026 10:41
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 9, 2026

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added the Stale label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(router): Add SetBypassScopeValidation to skip scope authorization checks

3 participants