diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b961a10..5b84bfce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - [#486](https://github.com/oauth2-proxy/oauth2-proxy/pull/486) Add new linters (@johejo) - [#440](https://github.com/oauth2-proxy/oauth2-proxy/pull/440) Switch Azure AD Graph API to Microsoft Graph API (@johejo) - [#453](https://github.com/oauth2-proxy/oauth2-proxy/pull/453) Prevent browser caching during auth flow (@johejo) +- [#467](https://github.com/oauth2-proxy/oauth2-proxy/pull/467) Allow OIDC issuer verification to be skipped (@chkohner) - [#481](https://github.com/oauth2-proxy/oauth2-proxy/pull/481) Update Okta docs (@trevorbox) - [#474](https://github.com/oauth2-proxy/oauth2-proxy/pull/474) Always log hasMember request error object (@jbielick) - [#468](https://github.com/oauth2-proxy/oauth2-proxy/pull/468) Implement graceful shutdown and propagate request context (@johejo) diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 8f9539fc..e528cd8d 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -71,6 +71,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | `-jwt-key-file` | string | path to the private key file in PEM format used to sign the JWT so that you can say something like `-jwt-key-file=/etc/ssl/private/jwt_signing_key.pem`: required by login.gov | | | `-login-url` | string | Authentication endpoint | | | `-insecure-oidc-allow-unverified-email` | bool | don't fail if an email address in an id_token is not verified | false | +| `-insecure-oidc-skip-issuer-verification` | bool | allow the OIDC issuer URL to differ from the expected (currently required for Azure multi-tenant compatibility) | false | | `-oidc-issuer-url` | string | the OpenID Connect issuer URL. ie: `"https://accounts.google.com"` | | | `-oidc-jwks-url` | string | OIDC JWKS URI for token verification; required if OIDC discovery is disabled | | | `-pass-access-token` | bool | pass OAuth access_token to upstream via X-Forwarded-Access-Token header | false | diff --git a/main.go b/main.go index b1e67205..33a6c87d 100644 --- a/main.go +++ b/main.go @@ -128,6 +128,7 @@ func main() { flagSet.String("provider-display-name", "", "Provider display name") flagSet.String("oidc-issuer-url", "", "OpenID Connect issuer URL (ie: https://accounts.google.com)") flagSet.Bool("insecure-oidc-allow-unverified-email", false, "Don't fail if an email address in an id_token is not verified") + flagSet.Bool("insecure-oidc-skip-issuer-verification", false, "Do not verify if issuer matches OIDC discovery URL") flagSet.Bool("skip-oidc-discovery", false, "Skip OIDC discovery and use manually supplied Endpoints") flagSet.String("oidc-jwks-url", "", "OpenID Connect JWKS URL (ie: https://www.googleapis.com/oauth2/v3/certs)") flagSet.String("login-url", "", "Authentication endpoint") diff --git a/options.go b/options.go index f9abe3c5..1b6b962a 100644 --- a/options.go +++ b/options.go @@ -22,6 +22,7 @@ import ( sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" "github.com/oauth2-proxy/oauth2-proxy/providers" "gopkg.in/natefinch/lumberjack.v2" @@ -91,20 +92,21 @@ type Options struct { // These options allow for other providers besides Google, with // potential overrides. - Provider string `flag:"provider" cfg:"provider" env:"OAUTH2_PROXY_PROVIDER"` - ProviderName string `flag:"provider-display-name" cfg:"provider_display_name" env:"OAUTH2_PROXY_PROVIDER_DISPLAY_NAME"` - OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url" env:"OAUTH2_PROXY_OIDC_ISSUER_URL"` - InsecureOIDCAllowUnverifiedEmail bool `flag:"insecure-oidc-allow-unverified-email" cfg:"insecure_oidc_allow_unverified_email" env:"OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL"` - SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery" env:"OAUTH2_PROXY_SKIP_OIDC_DISCOVERY"` - OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url" env:"OAUTH2_PROXY_OIDC_JWKS_URL"` - LoginURL string `flag:"login-url" cfg:"login_url" env:"OAUTH2_PROXY_LOGIN_URL"` - RedeemURL string `flag:"redeem-url" cfg:"redeem_url" env:"OAUTH2_PROXY_REDEEM_URL"` - ProfileURL string `flag:"profile-url" cfg:"profile_url" env:"OAUTH2_PROXY_PROFILE_URL"` - ProtectedResource string `flag:"resource" cfg:"resource" env:"OAUTH2_PROXY_RESOURCE"` - ValidateURL string `flag:"validate-url" cfg:"validate_url" env:"OAUTH2_PROXY_VALIDATE_URL"` - Scope string `flag:"scope" cfg:"scope" env:"OAUTH2_PROXY_SCOPE"` - Prompt string `flag:"prompt" cfg:"prompt" env:"OAUTH2_PROXY_PROMPT"` - ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt" env:"OAUTH2_PROXY_APPROVAL_PROMPT"` // Deprecated by OIDC 1.0 + Provider string `flag:"provider" cfg:"provider" env:"OAUTH2_PROXY_PROVIDER"` + ProviderName string `flag:"provider-display-name" cfg:"provider_display_name" env:"OAUTH2_PROXY_PROVIDER_DISPLAY_NAME"` + OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url" env:"OAUTH2_PROXY_OIDC_ISSUER_URL"` + InsecureOIDCAllowUnverifiedEmail bool `flag:"insecure-oidc-allow-unverified-email" cfg:"insecure_oidc_allow_unverified_email" env:"OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL"` + InsecureOIDCSkipIssuerVerification bool `flag:"insecure-oidc-skip-issuer-verification" cfg:"insecure_oidc_skip_issuer_verification" env:"OAUTH2_PROXY_INSECURE_OIDC_SKIP_ISSUER_VERIFICATION"` + SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery" env:"OAUTH2_PROXY_SKIP_OIDC_DISCOVERY"` + OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url" env:"OAUTH2_PROXY_OIDC_JWKS_URL"` + LoginURL string `flag:"login-url" cfg:"login_url" env:"OAUTH2_PROXY_LOGIN_URL"` + RedeemURL string `flag:"redeem-url" cfg:"redeem_url" env:"OAUTH2_PROXY_REDEEM_URL"` + ProfileURL string `flag:"profile-url" cfg:"profile_url" env:"OAUTH2_PROXY_PROFILE_URL"` + ProtectedResource string `flag:"resource" cfg:"resource" env:"OAUTH2_PROXY_RESOURCE"` + ValidateURL string `flag:"validate-url" cfg:"validate_url" env:"OAUTH2_PROXY_VALIDATE_URL"` + Scope string `flag:"scope" cfg:"scope" env:"OAUTH2_PROXY_SCOPE"` + Prompt string `flag:"prompt" cfg:"prompt" env:"OAUTH2_PROXY_PROMPT"` + ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt" env:"OAUTH2_PROXY_APPROVAL_PROMPT"` // Deprecated by OIDC 1.0 // Configuration values for logging LoggingFilename string `flag:"logging-filename" cfg:"logging_filename" env:"OAUTH2_PROXY_LOGGING_FILENAME"` @@ -254,6 +256,44 @@ func (o *Options) Validate() error { ctx := context.Background() + if o.InsecureOIDCSkipIssuerVerification && !o.SkipOIDCDiscovery { + // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call + // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if + // we get the URLs, we'll just use the non-discovery path. + + logger.Printf("Performing OIDC Discovery...") + + if req, err := http.NewRequest("GET", strings.TrimSuffix(o.OIDCIssuerURL, "/")+"/.well-known/openid-configuration", nil); err == nil { + if body, err := requests.Request(req); err == nil { + + // Prefer manually configured URLs. It's a bit unclear + // why you'd be doing discovery and also providing the URLs + // explicitly though... + if o.LoginURL == "" { + o.LoginURL = body.Get("authorization_endpoint").MustString() + } + + if o.RedeemURL == "" { + o.RedeemURL = body.Get("token_endpoint").MustString() + } + + if o.OIDCJwksURL == "" { + o.OIDCJwksURL = body.Get("jwks_uri").MustString() + } + + if o.ProfileURL == "" { + o.ProfileURL = body.Get("userinfo_endpoint").MustString() + } + + o.SkipOIDCDiscovery = true + } else { + logger.Printf("error: failed to discover OIDC configuration: %v", err) + } + } else { + logger.Printf("error: failed parsing OIDC discovery URL: %v", err) + } + } + // Construct a manual IDTokenVerifier from issuer URL & JWKS URI // instead of metadata discovery if we enable -skip-oidc-discovery. // In this case we need to make sure the required endpoints for @@ -270,7 +310,8 @@ func (o *Options) Validate() error { } keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL) o.oidcVerifier = oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{ - ClientID: o.ClientID, + ClientID: o.ClientID, + SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification, }) } else { // Configure discoverable provider data. @@ -279,7 +320,8 @@ func (o *Options) Validate() error { return err } o.oidcVerifier = provider.Verifier(&oidc.Config{ - ClientID: o.ClientID, + ClientID: o.ClientID, + SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification, }) o.LoginURL = provider.Endpoint().AuthURL