diff --git a/CHANGELOG.md b/CHANGELOG.md index e02ee04de4..a9641d90c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +### 0.5.1 (upcoming) + +* [PLT-3581] Add configurable sign-out URL via `--sign-out-url` / `OAUTH2_PROXY_SIGN_OUT_URL` to support non-standard IdP logout flows (e.g. Autentica/REDSARA) + +## Previous development + ### 0.5.0 (2026-05-11) * [PLT-2583] Bump oauth2-proxy upstream to v7.15.2 (security fixes: authentication bypasses CVE-2026-34986, CVE-2026-32281 and others) diff --git a/docs/docs/configuration/alpha_config.md b/docs/docs/configuration/alpha_config.md index 680741bad5..5a77c4ba0d 100644 --- a/docs/docs/configuration/alpha_config.md +++ b/docs/docs/configuration/alpha_config.md @@ -574,6 +574,7 @@ Provider holds all configuration for a single provider | `googleConfig` | _[GoogleOptions](#googleoptions)_ | GoogleConfig holds all configurations for Google provider. | | `oidcConfig` | _[OIDCOptions](#oidcoptions)_ | OIDCConfig holds all configurations for OIDC provider
or providers utilize OIDC configurations. | | `loginGovConfig` | _[LoginGovOptions](#logingovoptions)_ | LoginGovConfig holds all configurations for LoginGov provider. | +| `sisConfig` | _[SISOptions](#sisoptions)_ | SISConfig holds all configurations for SIS provider. | | `id` | _string_ | ID should be a unique identifier for the provider.
This value is required for all providers. | | `provider` | _[ProviderType](#providertype)_ | Type is the OAuth provider
must be set from the supported providers group,
otherwise 'Google' is set as default | | `name` | _string_ | Name is the providers display name
if set, it will be shown to the users in the login page. | @@ -592,6 +593,7 @@ Provider holds all configuration for a single provider | `code_challenge_method` | _string_ | The code challenge method | | `additionalClaims` | _[]string_ | Additional claims to be obtained from the upstream IDP, either from the id_token or from the userinfo endpoint if configured. | | `backendLogoutURL` | _string_ | URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session | +| `signOutURL` | _string_ | SignOutURL overrides the provider's default sign-out redirect URL | ### ProviderType #### (`string` alias) @@ -615,6 +617,17 @@ AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-con However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). +### SISOptions + +(**Appears on:** [Provider](#provider)) + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `SISRootURL` | _string_ | SISRootURL is the OpenID Connect SISRoot URL | +| `ClearExtraCookieNames` | _[]string_ | ClearExtraCookieNames sets cookie names to clear after sign out | + ### SecretSource (**Appears on:** [ClaimSource](#claimsource), [HeaderValue](#headervalue), [TLS](#tls)) diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go index ff7668b576..48f3996eb6 100644 --- a/pkg/apis/options/legacy_options.go +++ b/pkg/apis/options/legacy_options.go @@ -530,6 +530,7 @@ type LegacyProvider struct { GoogleAdminAPIUserScope string `flag:"google-admin-api-user-scope" cfg:"google_admin_api_user_scope"` SISRootURL string `flag:"sis-root-url" cfg:"sis_root_url"` + SignOutURL string `flag:"sign-out-url" cfg:"sign_out_url"` ClearExtraCookieNames []string `flag:"clear-extra-cookie-names" cfg:"clear_extra_cookie_names"` // These options allow for other providers besides Google, with @@ -720,6 +721,7 @@ func (l *LegacyProvider) convert() (Providers, error) { CodeChallengeMethod: l.CodeChallengeMethod, BackendLogoutURL: l.BackendLogoutURL, AuthRequestResponseMode: l.AuthRequestResponseMode, + SignOutURL: l.SignOutURL, } // This part is out of the switch section for all providers that support OIDC diff --git a/pkg/apis/options/providers.go b/pkg/apis/options/providers.go index a04dc2b37f..6fb71c69f8 100644 --- a/pkg/apis/options/providers.go +++ b/pkg/apis/options/providers.go @@ -141,6 +141,8 @@ type Provider struct { // URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session BackendLogoutURL string `yaml:"backendLogoutURL"` + // SignOutURL overrides the provider's default sign-out redirect URL + SignOutURL string `yaml:"signOutURL,omitempty"` } // ProviderType is used to enumerate the different provider type options diff --git a/providers/providers.go b/providers/providers.go index 7e470ef691..7767fb3df9 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -137,6 +137,7 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData, "profile": {dst: &p.ProfileURL, raw: providerConfig.ProfileURL}, "validate": {dst: &p.ValidateURL, raw: providerConfig.ValidateURL}, "resource": {dst: &p.ProtectedResource, raw: providerConfig.ProtectedResource}, + "signout": {dst: &p.SignOutURL, raw: providerConfig.SignOutURL}, } { var err error *u.dst, err = url.Parse(u.raw) diff --git a/providers/sis.go b/providers/sis.go index 1c6163716a..dc9316ddf1 100644 --- a/providers/sis.go +++ b/providers/sis.go @@ -274,8 +274,8 @@ func (p *SISProvider) GetSignOutURL(redirectURI string) string { // copy URL redirect := *p.SignOutURL if redirectURI != "" { - v := url.Values{} - v.Add("rd", redirectURI) + v := redirect.Query() + v.Set("rd", redirectURI) redirect.RawQuery = v.Encode() } return redirect.String() diff --git a/providers/sis_test.go b/providers/sis_test.go index 0f305f59cd..329762926b 100644 --- a/providers/sis_test.go +++ b/providers/sis_test.go @@ -61,6 +61,41 @@ func TestSISProviderOverrides(t *testing.T) { assert.Equal(t, "profile", p.Data().Scope) } +func TestSISProviderGetSignOutURL(t *testing.T) { + tests := []struct { + name string + signOutURL string + redirectURI string + expected string + }{ + { + name: "no redirect preserves sign-out URL as-is", + signOutURL: "https://sis.example.com/sso/logout", + redirectURI: "", + expected: "https://sis.example.com/sso/logout", + }, + { + name: "redirect appended as rd param", + signOutURL: "https://sis.example.com/sso/logout", + redirectURI: "https://app.example.com/home", + expected: "https://sis.example.com/sso/logout?rd=https%3A%2F%2Fapp.example.com%2Fhome", + }, + { + name: "existing query params preserved when adding rd", + signOutURL: "https://autentica.example.com/logout?appId=5784", + redirectURI: "https://app.example.com/home", + expected: "https://autentica.example.com/logout?appId=5784&rd=https%3A%2F%2Fapp.example.com%2Fhome", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u, _ := url.Parse(tt.signOutURL) + p := NewSISProvider(&ProviderData{SignOutURL: u}, options.SISOptions{}) + assert.Equal(t, tt.expected, p.GetSignOutURL(tt.redirectURI)) + }) + } +} + func TestSISProviderRedeem(t *testing.T) { b := testSISBackend(map[string]string{ "/sso/oauth2.0/accessToken": "access_token=imaginary_access_token&expires=10000",