Skip to content

Resolve Permission resolution in authorization_code/CIBA flows is not scoped to the requested resource server - #4190

Merged
rajithacharith merged 1 commit into
thunder-id:mainfrom
ImalshaD:fix/resource-server-aware-authorization
Jul 24, 2026
Merged

Resolve Permission resolution in authorization_code/CIBA flows is not scoped to the requested resource server#4190
rajithacharith merged 1 commit into
thunder-id:mainfrom
ImalshaD:fix/resource-server-aware-authorization

Conversation

@ImalshaD

@ImalshaD ImalshaD commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fixes a security issue where requested permissions were evaluated without being scoped to the resource server targeted by the authorization request. When multiple resource servers defined permissions with the same name, a user’s permission on one resource server could incorrectly authorize that permission for another resource server.

This change ensures that permission evaluation and token issuance consistently use the resource server resolved from the OAuth 2.0 Resource Indicator. It also prevents resource-server context from a previous request from leaking through SSO session reuse.

Approach

  • Resolve the applicable resource server from the authorization request’s resource parameter or the application’s configured default resource server.

  • Preserve the existing client-ID audience fallback when only OIDC scopes are requested and no resource server is applicable.

  • Carry the resolved internal resource-server ID through the authorization runtime context.

  • Scope authorization evaluations to that resource-server ID.

  • Validate and propagate the resource binding consistently across:
    - Authorization Code
    - Pushed Authorization Requests
    - CIBA
    - Refresh Token flows

    • Exclude the request-scoped resource-server ID from reusable SSO checkpoint data, preventing a previous request’s resource binding from overriding a
      subsequent authorization request.

    • Add unit and integration tests covering:

      • Permission-name collisions across resource servers.
      • Explicit and default resource-server resolution.
      • PAR resource handling.
      • CIBA resource binding.
      • Refresh-token resource consistency.
      • Permission isolation during SSO session reuse.

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

Summary by CodeRabbit

  • New Features

    • OAuth/OIDC (including CIBA, PAR, and authorization-code/token flows) now binds permission evaluations to the requested resource server and propagates that target through to token issuance.
    • Resource-bound behavior works for explicit targets and for configured default resource-server selection when no target is provided.
  • Bug Fixes

    • Prevented permission/scope leakage across resource servers, including during SSO session reuse.
    • Permission evaluation now fails safely or returns no permissions when no valid resource-server target can be determined.
    • Request-scoped resource binding is no longer replayed across checkpoint reloads.
  • Documentation

    • Updated resource-indicators guidance to clarify per-request resource binding and SSO reuse behavior.

Copilot AI review requested due to automatic review settings July 21, 2026 05:10
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Resource-server resolution is delegated to a default-aware provider, OAuth flows propagate the selected identifier into flow runtime data, and authorization evaluations are scoped to that server. PAR, CIBA, SSO, refresh-token, end-to-end, and integration tests cover explicit, default, missing, and cross-resource-server scenarios.

Resource-server-scoped authorization

Layer / File(s) Summary
Provider-driven resource resolution
backend/internal/oauth/oauth2/resourceindicators/*, backend/internal/resource/*
Default resource resolution uses the resource provider, with explicit handling for missing resources, provider errors, and nil providers.
OAuth audience binding and runtime propagation
backend/internal/oauth/...
Authorization, CIBA, PAR, and grant handlers resolve or validate bindings, preserve raw PAR scopes, propagate the selected identifier, and remove direct server-config dependencies.
Runtime propagation and scoped evaluation
backend/internal/flow/...
Authorization execution resolves the resource server from runtime data, user inputs, or defaults, scopes evaluation requests, fails closed without a target, and excludes the request-scoped key from snapshots.
Service and executor wiring
backend/cmd/server/servicemanager.go, backend/pkg/thunderidengine/engine.go
Server and embedded-engine initialization provides resource providers to OAuth and flow executors.
Integration validation
tests/integration/oauth/..., tests/integration/flow/..., tests/e2e/*
Tests verify resource-bound audiences and permission isolation across authorization, SSO reuse, refresh-token, authentication, and direct flow execution paths.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OAuthService
  participant ResourceProvider
  participant FlowExecutor
  participant AuthorizationService
  Client->>OAuthService: request resource-bound authorization
  OAuthService->>ResourceProvider: resolve target resource server
  OAuthService->>FlowExecutor: store resource-server identifier
  FlowExecutor->>ResourceProvider: resolve resource-server ID
  FlowExecutor->>AuthorizationService: evaluate permissions for that resource server
Loading

Possibly related PRs

Suggested reviewers: thamindudilshan, thiva-k, senthalan

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.54% 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
Title check ✅ Passed The title clearly names the main fix: scoping authorization_code/CIBA permission resolution to the requested resource server.
Description check ✅ Passed The PR description covers Purpose, Approach, Related Issues, Checklist, and Security checks; only the optional breaking-changes section is omitted.
Linked Issues check ✅ Passed The changes scope auth_code and CIBA permission evaluation to the resolved resource server and add tests for the collision case described in #4091.
Out of Scope Changes check ✅ Passed The broader refactors across shared resource resolution and related OAuth flows support the stated PR objective rather than introduce unrelated changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

docs/content/guides/protocols/oauth-oidc/resource-indicators.mdx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

tests/e2e/utils/authentication/admin-api-auth.ts

Parsing error: error TS5012: Cannot read file '/tsconfig.json': ENOENT: no such file or directory, open '/tsconfig.json'.

tests/e2e/utils/server-setup/mfa-setup.ts

Parsing error: error TS5012: Cannot read file '/tsconfig.json': ENOENT: no such file or directory, open '/tsconfig.json'.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/internal/flow/common/constants.go`:
- Around line 141-143: Update the relevant OAuth 2.0 documentation to describe
resource-server binding and downscoping via RFC 8707’s resource parameter or the
application default, including that permission evaluation is scoped to the
selected server. Document that SSO sessions are resource-bound and cannot reuse
permission scopes across different resource servers, referencing
RuntimeKeyResourceServerID for the corresponding behavior.

In `@backend/internal/oauth/oauth2/authz/service.go`:
- Around line 317-353: The new audience-binding behavior requires documentation
updates: document explicit resource resolution, defaultResourceServer fallback,
and invalid_target behavior for OAuth2 authorize/PAR/CIBA flows in the relevant
API guide; document the defaultResourceServer configuration key and
ResourceServerID value; and update SDK documentation to explain
resource-server-specific token aud claims and permission scopes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c5e40906-33ad-40e1-9ca5-5e17f26339d7

📥 Commits

Reviewing files that changed from the base of the PR and between 34d5e25 and cfa0fac.

📒 Files selected for processing (19)
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/init.go
  • backend/internal/oauth/oauth2/authz/init_test.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/par/init.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • tests/integration/oauth/authz/authz_scope_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
  • tests/integration/oauth/token/refresh_token_test.go

Comment thread backend/internal/flow/common/constants.go Outdated
Comment thread backend/internal/oauth/oauth2/authz/service.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a security flaw in ThunderID’s OAuth/OIDC flows where permission evaluation could ignore the targeted resource server, allowing same-named permissions on different resource servers to be incorrectly treated as equivalent. It introduces a request-scoped internal resource-server binding (resolved from RFC 8707 resource or a configured default) and ensures authorization decisions, token issuance, PAR redemption, CIBA, refresh-token behavior, and SSO session reuse consistently honor that binding.

Changes:

  • Resolve and carry a single target resource server (or leave unbound for OIDC-only/scopeless requests), and thread its internal ID through the flow runtime context.
  • Scope authorization evaluations to the bound resource-server ID and prevent that request-scoped binding from being reused via SSO checkpoints.
  • Add/extend unit and integration tests covering cross-resource-server permission collisions, PAR validation behavior, CIBA runtime propagation, and SSO session reuse isolation.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/integration/oauth/token/refresh_token_test.go Updates the auth-code portion of the refresh-token integration flow to include an explicit RFC 8707 resource binding.
tests/integration/oauth/sso/suite_test.go Adds fixtures and helpers to create two colliding-permission resource servers and drive resource-bound authorize/token exchanges for SSO reuse testing.
tests/integration/oauth/sso/sso_reuse_test.go Adds an integration regression test ensuring SSO checkpoint reuse does not leak resource-server permission context across requests.
tests/integration/oauth/authz/authz_scope_test.go Extends integration coverage to validate permission isolation across two resource servers with colliding scope strings.
backend/internal/oauth/oauth2/par/service.go Validates resource binding feasibility at PAR push-time (including default RS handling) while deferring authoritative binding/downscoping to redeem-time.
backend/internal/oauth/oauth2/par/service_test.go Updates PAR unit tests for the new validation behavior and adds coverage for “no resource + (no/default) RS” cases.
backend/internal/oauth/oauth2/par/init.go Wires server-config service into PAR initialization for default resource server resolution.
backend/internal/oauth/oauth2/ciba/service.go Propagates the resolved resource-server ID into flow runtime data for CIBA initiations.
backend/internal/oauth/oauth2/ciba/service_test.go Adds unit coverage ensuring the CIBA runtime resource-server ID is set (or empty for OIDC-only).
backend/internal/oauth/oauth2/authz/service.go Centralizes resource binding + per-resource-server downscoping in the shared initiation path and carries resource-server ID through runtime data.
backend/internal/oauth/oauth2/authz/service_test.go Adds unit tests for explicit resource binding, default RS fallback, OIDC-only unbound behavior, and “no resource + no default” rejection.
backend/internal/oauth/oauth2/authz/init.go Wires server-config service into the authorize endpoint initialization.
backend/internal/oauth/oauth2/authz/init_test.go Updates init tests for the new authorize initializer signature.
backend/internal/oauth/init.go Passes server-config service through to PAR and authorize initialization at the OAuth module level.
backend/internal/flow/executor/session_executor.go Prevents request-scoped resource-server binding from being snapshotted into reusable SSO checkpoint data.
backend/internal/flow/executor/session_executor_test.go Verifies resource-server ID is excluded from sanitized checkpoint snapshots.
backend/internal/flow/executor/authz_executor.go Scopes access evaluations by including the runtime resource-server ID in authorization requests.
backend/internal/flow/executor/authz_executor_test.go Adds unit coverage asserting evaluations are scoped to the requested resource server.
backend/internal/flow/common/constants.go Introduces a runtime key for the internal resource-server ID binding.

@ImalshaD ImalshaD changed the title fix 4091 Resolve Permission resolution in authorization_code/CIBA flows is not scoped to the requested resource server Jul 21, 2026
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.72414% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
backend/internal/oauth/oauth2/authz/service.go 70.83% 6 Missing and 1 partial ⚠️
backend/internal/flow/executor/authz_executor.go 84.37% 4 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI review requested due to automatic review settings July 21, 2026 07:25
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from cfa0fac to 2b1edcf Compare July 21, 2026 07:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/guides/guides/protocols/oauth-oidc/resource-indicators.mdx`:
- Line 18: Revise the paragraph describing SSO binding so it applies only to
permission-bearing requests. State that requests with a resource bind to that
resource, while OIDC-only or scopeless requests without resource remain unbound
and use the client ID as the audience; remove the claim that every request uses
the configured default resource server.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 86772555-95c9-4264-858d-fa973465e217

📥 Commits

Reviewing files that changed from the base of the PR and between cfa0fac and 2b1edcf.

📒 Files selected for processing (20)
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/init.go
  • backend/internal/oauth/oauth2/authz/init_test.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/par/init.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • docs/content/guides/guides/protocols/oauth-oidc/resource-indicators.mdx
  • tests/integration/oauth/authz/authz_scope_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
  • tests/integration/oauth/token/refresh_token_test.go
🚧 Files skipped from review as they are similar to previous changes (18)
  • backend/internal/oauth/oauth2/authz/init_test.go
  • backend/internal/flow/executor/session_executor.go
  • tests/integration/oauth/token/refresh_token_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • backend/internal/oauth/oauth2/authz/init.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/oauth2/par/init.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • tests/integration/oauth/sso/suite_test.go
  • tests/integration/oauth/authz/authz_scope_test.go

Comment thread docs/content/guides/guides/protocols/oauth-oidc/resource-indicators.mdx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread tests/integration/oauth/sso/suite_test.go
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from 2b1edcf to e57feb6 Compare July 21, 2026 08:46
Copilot AI review requested due to automatic review settings July 21, 2026 08:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread backend/internal/flow/executor/authz_executor.go Outdated
Copilot AI review requested due to automatic review settings July 21, 2026 09:27
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from e57feb6 to 95c13de Compare July 21, 2026 09:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

tests/integration/oauth/sso/suite_test.go:321

  • TearDownSuite currently logs (and effectively ignores) any error deleting rs-A/rs-B, even though the comment says only RES-1006 dependency errors are expected. This can mask real cleanup failures and leak fixtures into subsequent integration runs.
	// rs-A/rs-B carry actions; deletion may report a dependency error (RES-1006) which is harmless on
	// the temporary test database, so log rather than fail.
	for _, rsID := range []string{ts.rsAID, ts.rsBID} {
		if rsID != "" {
			if err := testutils.DeleteResourceServer(rsID); err != nil {
				ts.T().Logf("Failed to delete SSO scope resource server %s: %v", rsID, err)
			}

Comment thread tests/integration/oauth/sso/suite_test.go
Comment thread tests/integration/oauth/sso/suite_test.go
Copilot AI review requested due to automatic review settings July 21, 2026 13:34
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from 95c13de to 962a9fb Compare July 21, 2026 13:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Comment thread backend/internal/flow/executor/authz_executor.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/internal/flow/executor/authz_executor.go (1)

197-210: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Type-assertion failure is silently swallowed.

cfg, _ := merged.(resource.DefaultResourceServerConfig) discards the ok result. If GetMergedConfig ever returns an unexpected type (contract drift, misregistration), this silently falls through to cfg.ResourceServerID == "", causing all requested permissions to be dropped with no diagnostic trail.

♻️ Proposed fix
-	cfg, _ := merged.(resource.DefaultResourceServerConfig)
-	return cfg.ResourceServerID
+	cfg, ok := merged.(resource.DefaultResourceServerConfig)
+	if !ok {
+		a.logger.Error(ctx.Context, "Unexpected type for default resource server config")
+		return ""
+	}
+	return cfg.ResourceServerID
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/flow/executor/authz_executor.go` around lines 197 - 210, The
defaultResourceServerID method must validate the type assertion from
GetMergedConfig instead of discarding its success result. Check whether merged
is a resource.DefaultResourceServerConfig, log an error through a.logger with
the relevant context when it is not, and return an empty string; preserve the
existing ResourceServerID return for valid configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/internal/flow/executor/authz_executor.go`:
- Around line 197-210: The defaultResourceServerID method must validate the type
assertion from GetMergedConfig instead of discarding its success result. Check
whether merged is a resource.DefaultResourceServerConfig, log an error through
a.logger with the relevant context when it is not, and return an empty string;
preserve the existing ResourceServerID return for valid configuration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e16d367a-8d0d-4c72-b903-dfdae7424c40

📥 Commits

Reviewing files that changed from the base of the PR and between 95c13de and 962a9fb.

📒 Files selected for processing (25)
  • backend/cmd/server/servicemanager.go
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/flow/executor/register.go
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/init.go
  • backend/internal/oauth/oauth2/authz/init_test.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/par/init.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • backend/pkg/thunderidengine/engine.go
  • docs/content/guides/guides/protocols/oauth-oidc/resource-indicators.mdx
  • tests/e2e/run-e2e.sh
  • tests/integration/flow/authentication/authz_test.go
  • tests/integration/oauth/authz/authz_scope_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
  • tests/integration/oauth/token/refresh_token_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/init.go
  • tests/integration/oauth/token/refresh_token_test.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/par/init.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/authz/authz_scope_test.go
  • backend/internal/oauth/oauth2/par/service_test.go

Copilot AI review requested due to automatic review settings July 21, 2026 14:44
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from 962a9fb to 39f860b Compare July 21, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Comment thread backend/internal/oauth/oauth2/authz/service.go
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from 39f860b to 2726ee6 Compare July 24, 2026 02:44
@ImalshaD
ImalshaD requested a review from senthalan as a code owner July 24, 2026 02:44
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from 2726ee6 to a544df7 Compare July 24, 2026 02:49
@ImalshaD

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/internal/oauth/oauth2/granthandlers/refresh_token.go (1)

140-192: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the non-RS identifier from refresh token validation

ResolveDefaultAudience(ClientID) can return a configured AccessToken.DefaultAudience that is not the client_id, and refresh-token audiences are persisted as []string. The current audience == tokenRequest.ClientID check can therefore misclassify an OIDC-only refresh token and call GetResourceServerByIdentifier with that custom audience. Derive the "not resource-server-bound" identifier from the validated refresh token claims before comparing it to the requested resource or resolving a resource server.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/oauth/oauth2/granthandlers/refresh_token.go` around lines
140 - 192, Update the refresh-token audience handling around the `audience ==
tokenRequest.ClientID` check to derive the validated non-resource-server
identifier from the refresh token claims, rather than assuming it is
`tokenRequest.ClientID`. Use that identifier when validating the requested
resource and determining whether to keep only OIDC scopes; resolve a resource
server only for other audiences.
backend/internal/oauth/oauth2/authz/service_test.go (1)

2032-2033: 📐 Maintainability & Code Quality | 🔴 Critical | ⚡ Quick win

🔴 Incorrect product name: thunder must be ThunderID (or the appropriate template placeholder for the file type). Bare thunder/Thunder/THUNDER is not an accepted short form of the product name.

This occurs in the changed ACR test literals on Lines 2032, 2064, 2096, 2123, 2154, and 2182.

Also applies to: 2064-2065, 2095-2096, 2123-2124, 2154-2155, 2182-2182

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/oauth/oauth2/authz/service_test.go` around lines 2032 -
2033, Update the changed ACR test literals in the relevant test cases to use the
product name “ThunderID” instead of the bare “thunder” prefix, including every
occurrence of the password and generated-code ACR values identified in the
comment. Preserve the existing ACR value structure and test behavior.

Source: Path instructions

🧹 Nitpick comments (2)
backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go (1)

114-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate resource-mock stub between SetupTest and stubDefaultResourceServer.

Lines 116-126 duplicate stubDefaultResourceServer (lines 176-186) verbatim. Have SetupTest call the helper instead of inlining the same stub twice.

♻️ Proposed dedup
 	suite.mockResourceService = resourcemock.NewResourceServiceInterfaceMock(suite.T())
-
-	// Resolve any explicit resource identifier to an echo RS (ID == Identifier); an empty identifier
-	// resolves to the configured default resource server, as the default-aware provider does.
-	suite.mockResourceService.On("GetResourceServerByIdentifier", mock.Anything, mock.Anything).
-		Return(func(_ context.Context, identifier string) *providers.ResourceServer {
-			if identifier == "" {
-				return &providers.ResourceServer{ID: testDefaultRSID, Identifier: testDefaultRSIdentifier}
-			}
-			return &providers.ResourceServer{ID: identifier, Identifier: identifier}
-		}, func(_ context.Context, _ string) *tidcommon.ServiceError {
-			return nil
-		}).Maybe()
-	suite.mockResourceService.On("ValidatePermissions", mock.Anything, mock.Anything, mock.Anything).
-		Return([]string{}, nil).Maybe()
+	suite.stubDefaultResourceServer()

Also applies to: 173-187

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go`
around lines 114 - 127, Update SetupTest to call the existing
stubDefaultResourceServer helper instead of defining the duplicate
GetResourceServerByIdentifier and ValidatePermissions mocks inline. Remove the
redundant inline stubs while preserving the helper’s default and explicit
resource-server behavior.
backend/pkg/thunderidengine/engine.go (1)

95-95: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm the hardcoded brand string.

Is this hardcoded brand name intentional? If it is configurable, source it from runtime configuration or a named constant instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/pkg/thunderidengine/engine.go` at line 95, Update the debug message
in the runtimeCryptoSvc initialization path to avoid embedding the hardcoded
“ThunderID” brand directly; use the appropriate runtime configuration value or
existing named constant if the brand is configurable, while preserving the
message’s meaning.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/internal/oauth/oauth2/authz/service_test.go`:
- Around line 2032-2033: Update the changed ACR test literals in the relevant
test cases to use the product name “ThunderID” instead of the bare “thunder”
prefix, including every occurrence of the password and generated-code ACR values
identified in the comment. Preserve the existing ACR value structure and test
behavior.

In `@backend/internal/oauth/oauth2/granthandlers/refresh_token.go`:
- Around line 140-192: Update the refresh-token audience handling around the
`audience == tokenRequest.ClientID` check to derive the validated
non-resource-server identifier from the refresh token claims, rather than
assuming it is `tokenRequest.ClientID`. Use that identifier when validating the
requested resource and determining whether to keep only OIDC scopes; resolve a
resource server only for other audiences.

---

Nitpick comments:
In `@backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go`:
- Around line 114-127: Update SetupTest to call the existing
stubDefaultResourceServer helper instead of defining the duplicate
GetResourceServerByIdentifier and ValidatePermissions mocks inline. Remove the
redundant inline stubs while preserving the helper’s default and explicit
resource-server behavior.

In `@backend/pkg/thunderidengine/engine.go`:
- Line 95: Update the debug message in the runtimeCryptoSvc initialization path
to avoid embedding the hardcoded “ThunderID” brand directly; use the appropriate
runtime configuration value or existing named constant if the brand is
configurable, while preserving the message’s meaning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e4cf6d4-0157-46a2-b450-e4df3e78ae56

📥 Commits

Reviewing files that changed from the base of the PR and between 95c13de and a544df7.

📒 Files selected for processing (33)
  • backend/cmd/server/servicemanager.go
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/flow/executor/register.go
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/ciba/init.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials_test.go
  • backend/internal/oauth/oauth2/granthandlers/init.go
  • backend/internal/oauth/oauth2/granthandlers/jwt_bearer.go
  • backend/internal/oauth/oauth2/granthandlers/jwt_bearer_test.go
  • backend/internal/oauth/oauth2/granthandlers/provider.go
  • backend/internal/oauth/oauth2/granthandlers/provider_test.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token_test.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange_test.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators_test.go
  • backend/internal/resource/default_aware_provider.go
  • backend/internal/resource/default_aware_provider_test.go
  • backend/pkg/thunderidengine/engine.go
💤 Files with no reviewable changes (1)
  • backend/internal/oauth/oauth2/granthandlers/init.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go

@KD23243 KD23243 added Type/Bug trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes labels Jul 24, 2026
@ImalshaD
ImalshaD force-pushed the fix/resource-server-aware-authorization branch from a544df7 to 7bf3ecc Compare July 24, 2026 03:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/e2e/utils/authentication/admin-api-auth.ts (1)

32-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate admin-token-acquisition logic; this PR had to patch it twice.

Both files implement the same admin /flow/execute login sequence with identical hardcoded app ID and flow secret. Adding resource_server_identifier required editing both independently — exactly the drift risk duplicated logic creates.

  • tests/e2e/utils/authentication/admin-api-auth.ts#L32-L66: keep this as the single canonical getAdminToken(request) implementation.
  • tests/e2e/utils/server-setup/mfa-setup.ts#L181-L231: replace the private getAdminToken body with a call to the exported getAdminToken from admin-api-auth.ts, passing this.request, instead of maintaining a second copy.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/utils/authentication/admin-api-auth.ts` around lines 32 - 66, Use
getAdminToken in tests/e2e/utils/authentication/admin-api-auth.ts:32-66 as the
canonical implementation and leave it unchanged. In
tests/e2e/utils/server-setup/mfa-setup.ts:181-231, remove the private duplicated
login logic and delegate to the exported getAdminToken, passing this.request;
update imports and callers as needed.
tests/integration/oauth/sso/suite_test.go (1)

252-288: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Same resource-server-A/B + scope-role fixture is duplicated across sibling suites.

Based on cross-file evidence, the same rs-A/rs-B/scope-role setup and teardown (identical SetupSuite/TearDownSuite bodies) also appears in other integration suites in this PR's cohort (e.g. authz_scope_test.go, refresh_token_test.go, authz_test.go). Consider extracting a shared testutils helper (e.g. CreateCrossResourceServerFixture) that returns the two resource-server IDs, scope user ID, and role ID, to avoid four near-identical copies drifting independently. Not blocking for this PR given the project's stated preference for minimal, focused changes.

Also applies to: 310-323

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/oauth/sso/suite_test.go` around lines 252 - 288, Extract
the duplicated cross-resource-server setup and teardown used by the sibling
integration suites into a shared testutils helper, such as
CreateCrossResourceServerFixture. Have it create both resource servers, the
scope user, and the scope role, returning their IDs; update the affected
SetupSuite and TearDownSuite implementations to use the helper while preserving
the existing fixture values and cleanup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/guides/protocols/oauth-oidc/resource-indicators.mdx`:
- Around line 18-19: Update the final sentence in the resource-binding
description to state that unbound OIDC-only and scopeless requests use
token.accessToken.defaultAudience as the audience when configured, falling back
to client_id only when it is unset.

---

Nitpick comments:
In `@tests/e2e/utils/authentication/admin-api-auth.ts`:
- Around line 32-66: Use getAdminToken in
tests/e2e/utils/authentication/admin-api-auth.ts:32-66 as the canonical
implementation and leave it unchanged. In
tests/e2e/utils/server-setup/mfa-setup.ts:181-231, remove the private duplicated
login logic and delegate to the exported getAdminToken, passing this.request;
update imports and callers as needed.

In `@tests/integration/oauth/sso/suite_test.go`:
- Around line 252-288: Extract the duplicated cross-resource-server setup and
teardown used by the sibling integration suites into a shared testutils helper,
such as CreateCrossResourceServerFixture. Have it create both resource servers,
the scope user, and the scope role, returning their IDs; update the affected
SetupSuite and TearDownSuite implementations to use the helper while preserving
the existing fixture values and cleanup behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fdc5330d-00f2-4ad4-87d7-47307149a607

📥 Commits

Reviewing files that changed from the base of the PR and between a544df7 and 7bf3ecc.

📒 Files selected for processing (43)
  • backend/cmd/server/servicemanager.go
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/flow/executor/authz_executor_test.go
  • backend/internal/flow/executor/register.go
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/ciba/init.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/ciba/service_test.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials_test.go
  • backend/internal/oauth/oauth2/granthandlers/init.go
  • backend/internal/oauth/oauth2/granthandlers/jwt_bearer.go
  • backend/internal/oauth/oauth2/granthandlers/jwt_bearer_test.go
  • backend/internal/oauth/oauth2/granthandlers/provider.go
  • backend/internal/oauth/oauth2/granthandlers/provider_test.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token_test.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange_test.go
  • backend/internal/oauth/oauth2/par/service.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators_test.go
  • backend/internal/resource/default_aware_provider.go
  • backend/internal/resource/default_aware_provider_test.go
  • backend/pkg/thunderidengine/engine.go
  • docs/content/guides/protocols/oauth-oidc/resource-indicators.mdx
  • tests/e2e/run-e2e.sh
  • tests/e2e/tests/sample-app-authentication/README-MFA.md
  • tests/e2e/utils/authentication/admin-api-auth.ts
  • tests/e2e/utils/server-setup/mfa-setup.ts
  • tests/integration/flow/authentication/authz_test.go
  • tests/integration/oauth/authz/authz_scope_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
  • tests/integration/oauth/token/refresh_token_test.go
💤 Files with no reviewable changes (2)
  • backend/internal/oauth/oauth2/granthandlers/provider_test.go
  • backend/internal/oauth/oauth2/granthandlers/init.go
🚧 Files skipped from review as they are similar to previous changes (33)
  • backend/internal/flow/executor/session_executor.go
  • backend/internal/flow/executor/register.go
  • backend/internal/flow/common/constants.go
  • backend/internal/oauth/oauth2/par/service.go
  • tests/integration/oauth/token/refresh_token_test.go
  • backend/internal/flow/executor/session_executor_test.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange.go
  • backend/internal/resource/default_aware_provider.go
  • backend/internal/oauth/oauth2/ciba/init.go
  • tests/integration/flow/authentication/authz_test.go
  • backend/internal/resource/default_aware_provider_test.go
  • backend/pkg/thunderidengine/engine.go
  • backend/internal/oauth/init.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code.go
  • backend/internal/oauth/oauth2/ciba/service.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials.go
  • backend/internal/oauth/oauth2/granthandlers/provider.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • backend/internal/oauth/oauth2/authz/service.go
  • backend/internal/oauth/oauth2/granthandlers/client_credentials_test.go
  • backend/internal/flow/executor/authz_executor.go
  • backend/internal/oauth/oauth2/granthandlers/authorization_code_test.go
  • backend/internal/oauth/oauth2/granthandlers/refresh_token_test.go
  • backend/internal/oauth/oauth2/granthandlers/jwt_bearer_test.go
  • backend/cmd/server/servicemanager.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators.go
  • backend/internal/oauth/oauth2/authz/service_test.go
  • backend/internal/oauth/oauth2/resourceindicators/resourceindicators_test.go
  • tests/integration/oauth/authz/authz_scope_test.go
  • backend/internal/oauth/oauth2/granthandlers/token_exchange_test.go
  • backend/internal/oauth/oauth2/par/service_test.go
  • backend/internal/oauth/oauth2/ciba/service_test.go

Comment thread docs/content/guides/protocols/oauth-oidc/resource-indicators.mdx
// NewDefaultAwareResourceServerProvider wraps base so that GetResourceServerByIdentifier resolves the
// configured default resource server when the identifier is empty. base and serverConfigService must
// both be non-nil.
func NewDefaultAwareResourceServerProvider(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need to revisit the naming and initialization pattern in a follow-up

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will bring with a followup PR

"No resource server service available; dropping requested permission scopes")
return ""
}
rs, svcErr := a.resourceService.GetResourceServerByIdentifier(ctx.Context, identifier)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't we handle server errors here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will bring with a followup PR

@rajithacharith
rajithacharith added this pull request to the merge queue Jul 24, 2026
Merged via the queue into thunder-id:main with commit 00091d3 Jul 24, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes Type/Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Permission resolution in authorization_code/CIBA flows is not scoped to the requested resource server

5 participants