@@ -40,6 +40,8 @@ type OAuth2Config struct {
4040 Mode string // "native" or "proxy"
4141 Provider string
4242 RedirectURIs string
43+ // FixedRedirectURI is an optional fixed redirect URI used when proxying callbacks
44+ FixedRedirectURI string
4345
4446 // OIDC configuration
4547 Issuer string
@@ -184,21 +186,22 @@ func NewOAuth2ConfigFromConfig(cfg *Config, version string) *OAuth2Config {
184186 }
185187
186188 return & OAuth2Config {
187- Enabled : true ,
188- Mode : cfg .Mode ,
189- Provider : cfg .Provider ,
190- RedirectURIs : cfg .RedirectURIs ,
191- Issuer : cfg .Issuer ,
192- Audience : cfg .Audience ,
193- ClientID : cfg .ClientID ,
194- ClientSecret : cfg .ClientSecret ,
195- Scopes : scopes ,
196- MCPHost : mcpHost ,
197- MCPPort : mcpPort ,
198- MCPURL : mcpURL ,
199- Scheme : scheme ,
200- Version : version ,
201- stateSigningKey : cfg .JWTSecret ,
189+ Enabled : true ,
190+ Mode : cfg .Mode ,
191+ Provider : cfg .Provider ,
192+ RedirectURIs : cfg .RedirectURIs ,
193+ FixedRedirectURI : cfg .FixedRedirectURI ,
194+ Issuer : cfg .Issuer ,
195+ Audience : cfg .Audience ,
196+ ClientID : cfg .ClientID ,
197+ ClientSecret : cfg .ClientSecret ,
198+ Scopes : scopes ,
199+ MCPHost : mcpHost ,
200+ MCPPort : mcpPort ,
201+ MCPURL : mcpURL ,
202+ Scheme : scheme ,
203+ Version : version ,
204+ stateSigningKey : cfg .JWTSecret ,
202205 }
203206}
204207
@@ -298,12 +301,22 @@ func (h *OAuth2Handler) HandleAuthorize(w http.ResponseWriter, r *http.Request)
298301
299302 // Determine redirect URI strategy based on configuration
300303 var redirectURI string
301- hasFixedRedirect := h .config .RedirectURIs != "" && ! strings .Contains (h .config .RedirectURIs , "," )
304+ actualState := state
305+
306+ if h .config .RedirectURIs != "" {
307+ // Allowlist mode: Client's URI must be in allowlist, used directly (no proxy)
308+ if h .isValidRedirectURI (clientRedirectURI ) {
309+ redirectURI = clientRedirectURI
310+ h .logger .Info ("OAuth2: Allowlist mode - using client URI from allowlist: %s" , redirectURI )
311+ } else {
312+ h .logger .Warn ("SECURITY: Redirect URI not in allowlist: %s from %s" , clientRedirectURI , r .RemoteAddr )
313+ h .logger .Info ("SECURITY: Will try fixed redirect mode next" )
314+ }
315+ }
302316
303- if hasFixedRedirect {
317+ if redirectURI == "" && h . config . FixedRedirectURI != "" {
304318 // Fixed redirect mode: Use server's redirect URI to OAuth provider, proxy back to client
305- redirectURI = strings .TrimSpace (h .config .RedirectURIs )
306- h .logger .Info ("OAuth2: Fixed redirect mode - using server URI: %s (will proxy to client: %s)" , redirectURI , clientRedirectURI )
319+ h .logger .Info ("OAuth2: Fixed redirect mode - using server URI: %s (will proxy to client: %s)" , h .config .FixedRedirectURI , clientRedirectURI )
307320
308321 // Validate client redirect URI format and security
309322 if clientRedirectURI == "" {
@@ -347,30 +360,9 @@ func (h *OAuth2Handler) HandleAuthorize(w http.ResponseWriter, r *http.Request)
347360 http .Error (w , "Fixed redirect mode only allows localhost redirect URIs for security. Use allowlist mode for production." , http .StatusBadRequest )
348361 return
349362 }
350-
363+ redirectURI = strings . TrimSpace ( h . config . FixedRedirectURI )
351364 h .logger .Info ("OAuth2: Validated localhost redirect URI for proxy: %s" , clientRedirectURI )
352- } else if h .config .RedirectURIs != "" {
353- // Allowlist mode: Client's URI must be in allowlist, used directly (no proxy)
354- if ! h .isValidRedirectURI (clientRedirectURI ) {
355- h .logger .Warn ("SECURITY: Redirect URI not in allowlist: %s from %s" , clientRedirectURI , r .RemoteAddr )
356- http .Error (w , "Invalid redirect_uri" , http .StatusBadRequest )
357- return
358- }
359- redirectURI = clientRedirectURI
360- h .logger .Info ("OAuth2: Allowlist mode - using client URI from allowlist: %s" , redirectURI )
361- } else {
362- // No configuration: Reject for security
363- h .logger .Warn ("SECURITY: No redirect URIs configured, rejecting: %s from %s" , clientRedirectURI , r .RemoteAddr )
364- http .Error (w , "Invalid redirect_uri" , http .StatusBadRequest )
365- return
366- }
367-
368- // Update OAuth2 config with redirect URI
369- h .oauth2Config .RedirectURL = redirectURI
370-
371- // For fixed redirect mode, create signed state with client redirect URI
372- actualState := state
373- if hasFixedRedirect {
365+ // For fixed redirect mode, create signed state with client redirect URI
374366 // Create state data with redirect URI
375367 stateData := map [string ]string {
376368 "state" : state ,
@@ -389,6 +381,16 @@ func (h *OAuth2Handler) HandleAuthorize(w http.ResponseWriter, r *http.Request)
389381 h .logger .Info ("OAuth2: Signed state for proxy callback (length: %d)" , len (signedState ))
390382 }
391383
384+ if redirectURI == "" {
385+ // No configuration: Reject for security
386+ h .logger .Warn ("SECURITY: No redirect URIs configured, rejecting: %s from %s" , clientRedirectURI , r .RemoteAddr )
387+ http .Error (w , "Invalid redirect_uri" , http .StatusBadRequest )
388+ return
389+ }
390+
391+ // Update OAuth2 config with redirect URI
392+ h .oauth2Config .RedirectURL = redirectURI
393+
392394 // Create authorization URL
393395 authURL := h .oauth2Config .AuthCodeURL (actualState , oauth2 .AccessTypeOffline )
394396
@@ -449,7 +451,7 @@ func (h *OAuth2Handler) HandleCallback(w http.ResponseWriter, r *http.Request) {
449451 }
450452
451453 // If using fixed redirect URI, handle proxy callback
452- if h .config .RedirectURIs != "" && ! strings . Contains ( h . config . RedirectURIs , "," ) {
454+ if h .config .FixedRedirectURI != "" {
453455 // Verify and decode signed state parameter
454456 stateData , err := h .verifyState (state )
455457 if err != nil {
@@ -546,8 +548,8 @@ func (h *OAuth2Handler) HandleToken(w http.ResponseWriter, r *http.Request) {
546548
547549 // Set redirect URI for token exchange
548550 redirectURI := clientRedirectURI
549- if h .config .RedirectURIs != "" && ! strings . Contains ( h . config . RedirectURIs , "," ) {
550- redirectURI = strings .TrimSpace (h .config .RedirectURIs )
551+ if h .config .FixedRedirectURI != "" && ! h . isValidRedirectURI ( clientRedirectURI ) {
552+ redirectURI = strings .TrimSpace (h .config .FixedRedirectURI )
551553 h .logger .Info ("OAuth2: Token exchange using fixed redirect URI: %s" , redirectURI )
552554 }
553555
@@ -802,8 +804,6 @@ func isLocalhostURI(uri string) bool {
802804// isValidRedirectURI validates redirect URI against allowlist for security
803805func (h * OAuth2Handler ) isValidRedirectURI (uri string ) bool {
804806 if h .config .RedirectURIs == "" {
805- // No redirect URIs configured - reject all redirects for security
806- h .logger .Warn ("WARNING: No OAuth redirect URIs configured, rejecting redirect: %s" , uri )
807807 return false
808808 }
809809
0 commit comments