Skip to content

Feat: support allowed domains for fixed redirect mode#21

Merged
tuannvm merged 1 commit intotuannvm:mainfrom
trygforsikring:support-allowed-domains-for-fixed-redirect-mode
Feb 10, 2026
Merged

Feat: support allowed domains for fixed redirect mode#21
tuannvm merged 1 commit intotuannvm:mainfrom
trygforsikring:support-allowed-domains-for-fixed-redirect-mode

Conversation

@WhammyLeaf
Copy link
Contributor

@WhammyLeaf WhammyLeaf commented Jan 16, 2026

Summary

This PR adds a new config variable AllowedClientRedirectDomains.

When AllowedClientRedirectDomains is set, to a comma-separated string of one or more valid domains, callback URIs with those domains will, in addition to localhost based callback URIs, be allowed for fixed redirect mode.

When AllowedClientRedirectDomains is unset, the current behaviour remains unchanged: only localhost based callback URIs are allowed for fixed redirect mode.

Changes

  • config.go updated with new AllowedClientRedirectDomains variable
  • The handleAuthorize and handleCallback methods in handlers.go have been updated to allow callback URIs that are not localhost as long as their domain is trusted.

Testing

  • Tests pass locally
  • New tests added (if applicable)

Related Issues

#22

Summary by CodeRabbit

  • New Features

    • Add configurable allowed client redirect domain suffixes so non-localhost HTTPS redirects (including subdomains) can be permitted in fixed-redirect mode.
  • Bug Fixes / Behavior Changes

    • Redirect validation now accepts configured domain suffixes while still always allowing localhost; error messages updated to reflect the broader allowed-domain policy and HTTPS requirement for non-localhost.
  • Tests

    • New unit tests covering localhost, domain-suffix rules, HTTPS requirement, malformed URIs, and edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Walkthrough

Adds a new AllowedClientRedirectDomains config field (env + builder), threads it into OAuth2Config, changes fixed-redirect validation to allow localhost or configured domain suffixes (requiring HTTPS for non-localhost), and adds unit tests for the new validation.

Changes

Cohort / File(s) Summary
Config: new field & builder
config.go
Adds AllowedClientRedirectDomains string to Config, adds WithAllowedClientRedirectDomains(domains string) to ConfigBuilder, and loads OAUTH_ALLOWED_CLIENT_REDIRECT_DOMAINS in env-based assembly.
Handler validation & propagation
handlers.go
Adds AllowedClientRedirectDomains to OAuth2Config, propagates it in NewOAuth2ConfigFromConfig, introduces (*OAuth2Handler).isAllowedClientRedirectURI(uri string) bool, replaces localhost-only fixed-redirect checks with allowlist-based checks permitting localhost or configured domain suffixes, and enforces HTTPS for non-localhost redirects with updated error messaging and proxy handling.
Tests: validation coverage
security_test.go
Adds TestIsAllowedClientRedirectURI covering IPv4/IPv6 localhost, empty allowlist rejection for non-localhost, exact and subdomain suffix matches, trimmed-space handling in allowlist, partial-suffix rejection, HTTP vs HTTPS behavior, and invalid URIs.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I hopped through config and trimmed each name,
localhost comfy, suffixes added to the game.
HTTPS for strangers, safe paths I trace,
Domains now welcome — I twitched with grace. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding support for allowed domains in fixed redirect mode, which is reflected across all modified files.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
handlers.go (2)

361-369: Update log message to reflect broader domain support.

Line 369 says "Validated localhost redirect URI" but the URI could now be a non-localhost domain from the allowed list, making the log message misleading during debugging.

Suggested fix
-		h.logger.Info("OAuth2: Validated localhost redirect URI for proxy: %s", clientRedirectURI)
+		h.logger.Info("OAuth2: Validated client redirect URI for proxy: %s", clientRedirectURI)

474-481: Update log message to reflect broader domain support.

Line 481 says "proxying callback to localhost client" but the redirect URI could now be a non-localhost domain from the allowed list.

Suggested fix
-			h.logger.Info("OAuth2: State verified, proxying callback to localhost client: %s", originalRedirectURI)
+			h.logger.Info("OAuth2: State verified, proxying callback to client: %s", originalRedirectURI)
🧹 Nitpick comments (1)
handlers.go (1)

809-848: Implementation is secure; consider documenting domain suffix configuration risks.

The domain suffix matching logic is correctly implemented:

  • Requires HTTPS for non-localhost URIs
  • Uses . prefix for subdomain matching, preventing partial matches (e.g., malicious-example.com won't match suffix example.com)
  • Defaults to rejecting all non-localhost URIs when unconfigured

Operational consideration: If an operator mistakenly configures a broad suffix like com or a short domain, it could allow unintended redirect URIs. Consider adding documentation or a startup warning for very short domain suffixes (e.g., single-part TLDs) to help operators avoid misconfiguration.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bfe704 and 9915c42.

📒 Files selected for processing (2)
  • config.go
  • handlers.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Never log raw OAuth tokens; only log SHA-256 hash of token formatted as fmt.Sprintf("%x", sha256.Sum256([]byte(token)))[:16]

Files:

  • config.go
  • handlers.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Security Scan
🔇 Additional comments (5)
config.go (3)

14-18: LGTM!

The new AllowedClientRedirectDomains field is well-documented and properly placed within the Config struct. The backward-compatible default (empty string) ensures existing deployments continue to allow only localhost redirects.


208-212: LGTM!

The builder method follows the established fluent API pattern and is consistent with other builder methods in the file.


337-337: LGTM!

Environment variable integration follows established naming conventions with the OAUTH_ prefix and properly defaults to an empty string for backward compatibility.

handlers.go (2)

46-48: LGTM!

The field is properly documented and mirrors the Config struct addition.


193-209: LGTM!

The AllowedClientRedirectDomains field is properly propagated from the source Config to OAuth2Config.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@WhammyLeaf WhammyLeaf changed the title support allwoed domains for fixed redirect mode Feat: support allowed domains for fixed redirect mode Jan 16, 2026
@WhammyLeaf WhammyLeaf force-pushed the support-allowed-domains-for-fixed-redirect-mode branch from 9915c42 to 7547006 Compare January 16, 2026 14:14
@WhammyLeaf
Copy link
Contributor Author

@tuannvm Thank you for merging the prior PRs. I have another one here I hope you can take a look at when you have the time 😄

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
handlers.go (2)

361-369: Misleading log message.

Line 369 logs "Validated localhost redirect URI for proxy" but the URI could be a non-localhost configured domain that passed the isAllowedClientRedirectURI check. This could cause confusion during debugging.

Proposed fix
-		h.logger.Info("OAuth2: Validated localhost redirect URI for proxy: %s", clientRedirectURI)
+		h.logger.Info("OAuth2: Validated client redirect URI for proxy: %s", clientRedirectURI)

474-481: Same log message inconsistency.

Line 481 says "proxying callback to localhost client" but the URI could be a non-localhost configured domain.

Proposed fix
-			h.logger.Info("OAuth2: State verified, proxying callback to localhost client: %s", originalRedirectURI)
+			h.logger.Info("OAuth2: State verified, proxying callback to client: %s", originalRedirectURI)
🧹 Nitpick comments (1)
handlers.go (1)

809-848: Domain suffix matching logic is secure.

The implementation correctly prevents subdomain confusion attacks by requiring either an exact match or a .-prefixed suffix match (e.g., example.com matches example.com and sub.example.com, but not evilexample.com). HTTPS enforcement for non-localhost and fail-closed behavior when no domains are configured are good security practices.

Consider validating configured domain suffixes.

If an administrator mistakenly configures AllowedClientRedirectDomains=https://example.com or example.com/path, the matching will silently fail. Optional validation during startup or first use could help catch misconfiguration early.

Optional: Add suffix validation helper
// validateDomainSuffix checks if a configured suffix is a valid domain (no scheme, no path)
func validateDomainSuffix(suffix string) bool {
	// Should not contain scheme
	if strings.Contains(suffix, "://") {
		return false
	}
	// Should not contain path
	if strings.Contains(suffix, "/") {
		return false
	}
	return len(suffix) > 0
}

This could be called when parsing the configuration to log warnings for invalid entries.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9915c42 and 7547006.

📒 Files selected for processing (2)
  • config.go
  • handlers.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • config.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Never log raw OAuth tokens; only log SHA-256 hash of token formatted as fmt.Sprintf("%x", sha256.Sum256([]byte(token)))[:16]

Files:

  • handlers.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Verification
🔇 Additional comments (2)
handlers.go (2)

46-49: LGTM!

The new field is well-documented and the comment clearly explains its purpose and usage in fixed redirect mode.


193-210: LGTM!

The new AllowedClientRedirectDomains field is correctly propagated from the generic Config to OAuth2Config.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@WhammyLeaf WhammyLeaf force-pushed the support-allowed-domains-for-fixed-redirect-mode branch 2 times, most recently from 2271d82 to 43a8be3 Compare January 19, 2026 09:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
handlers.go (2)

361-369: Log message is misleading for non-localhost URIs.

Line 369 logs "Validated localhost redirect URI" but after this change, the URI could also be from an allowed domain suffix, not just localhost.

Suggested fix
-		h.logger.Info("OAuth2: Validated localhost redirect URI for proxy: %s", clientRedirectURI)
+		h.logger.Info("OAuth2: Validated client redirect URI for proxy: %s", clientRedirectURI)

474-481: Log message is misleading for non-localhost URIs.

Line 481 logs "proxying callback to localhost client" but the URI could be from an allowed domain, not localhost.

Suggested fix
-			h.logger.Info("OAuth2: State verified, proxying callback to localhost client: %s", originalRedirectURI)
+			h.logger.Info("OAuth2: State verified, proxying callback to client: %s", originalRedirectURI)

Signed-off-by: Christian Troelsen <christian.troelsen@tryg.dk>

add some tests

expand formatting

update again
@WhammyLeaf WhammyLeaf force-pushed the support-allowed-domains-for-fixed-redirect-mode branch from 43a8be3 to be67bd0 Compare January 19, 2026 09:25
@WhammyLeaf
Copy link
Contributor Author

@tuannvm Comments have been addressed 😄

@WhammyLeaf
Copy link
Contributor Author

@tuannvm Could you take a look at this?

@tuannvm tuannvm merged commit 6eda873 into tuannvm:main Feb 10, 2026
8 of 9 checks passed
@tuannvm
Copy link
Owner

tuannvm commented Feb 10, 2026

thanks @WhammyLeaf !

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