diff --git a/CHANGELOG.md b/CHANGELOG.md index d98b209..c640f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Changes since v3.1.0 - [#68](https://github.com/pusher/oauth2_proxy/pull/68) forward X-Auth-Access-Token header (@davidholsgrove) +- [#41](https://github.com/pusher/oauth2_proxy/pull/41) Added option to manually specify OIDC endpoints instead of relying on discovery # v3.1.0 diff --git a/README.md b/README.md index db39635..6accae3 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,26 @@ OpenID Connect is a spec for OAUTH 2.0 + identity that is implemented by many ma -cookie-secure=false -email-domain example.com +#### Skip OIDC discovery + +Some providers do not support OIDC discovery via their issuer URL, so oauth2_proxy cannot simply grab the authorization, token and jwks URI endpoints from the provider's metadata. + +In this case, you can set the `-skip-oidc-discovery` option, and supply those required endpoints manually: + +``` + -provider oidc + -client-id oauth2_proxy + -client-secret proxy + -redirect-url http://127.0.0.1:4180/oauth2/callback + -oidc-issuer-url http://127.0.0.1:5556 + -skip-oidc-discovery + -login-url http://127.0.0.1:5556/authorize + -redeem-url http://127.0.0.1:5556/token + -oidc-jwks-url http://127.0.0.1:5556/keys + -cookie-secure=false + -email-domain example.com +``` + ## Email Authentication To authorize by email domain use `--email-domain=yourcompany.com`. To authorize individual email addresses use `--authenticated-emails-file=/path/to/file` with one email per line. To authorize all email addresses use `--email-domain=*`. @@ -213,6 +233,7 @@ Usage of oauth2_proxy: -https-address string: : to listen on for HTTPS clients (default ":443") -login-url string: Authentication endpoint -oidc-issuer-url: 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: pass OAuth access_token to upstream via X-Forwarded-Access-Token header -pass-authorization-header: pass OIDC IDToken to upstream via Authorization Bearer header -pass-basic-auth: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream (default true) @@ -232,6 +253,7 @@ Usage of oauth2_proxy: -signature-key string: GAP-Signature request signature key (algorithm:secretkey) -skip-auth-preflight: will skip authentication for OPTIONS requests -skip-auth-regex value: bypass authentication for requests path's that match (may be given multiple times) + -skip-oidc-discovery: bypass OIDC endpoint discovery. login-url, redeem-url and oidc-jwks-url must be configured in this case -skip-provider-button: will skip sign-in-page to directly reach the next step: oauth/start -ssl-insecure-skip-verify: skip validation of certificates presented when using HTTPS -tls-cert string: path to certificate file diff --git a/main.go b/main.go index 57a6f22..93f799c 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,8 @@ func main() { flagSet.String("provider", "google", "OAuth provider") flagSet.String("oidc-issuer-url", "", "OpenID Connect issuer URL (ie: https://accounts.google.com)") + 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") flagSet.String("redeem-url", "", "Token redemption endpoint") flagSet.String("profile-url", "", "Profile access endpoint") diff --git a/options.go b/options.go index e096003..666b517 100644 --- a/options.go +++ b/options.go @@ -71,6 +71,8 @@ type Options struct { // potential overrides. Provider string `flag:"provider" cfg:"provider"` OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url"` + SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery"` + OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url"` LoginURL string `flag:"login-url" cfg:"login_url"` RedeemURL string `flag:"redeem-url" cfg:"redeem_url"` ProfileURL string `flag:"profile-url" cfg:"profile_url"` @@ -121,6 +123,7 @@ func NewOptions() *Options { PassAuthorization: false, ApprovalPrompt: "force", RequestLogging: true, + SkipOIDCDiscovery: false, RequestLoggingFormat: defaultRequestLoggingFormat, } } @@ -161,16 +164,40 @@ func (o *Options) Validate() error { } if o.OIDCIssuerURL != "" { - // Configure discoverable provider data. - provider, err := oidc.NewProvider(context.Background(), o.OIDCIssuerURL) - if err != nil { - return err + + ctx := context.Background() + + // 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 + // the provider are configured. + if o.SkipOIDCDiscovery { + if o.LoginURL == "" { + msgs = append(msgs, "missing setting: login-url") + } + if o.RedeemURL == "" { + msgs = append(msgs, "missing setting: redeem-url") + } + if o.OIDCJwksURL == "" { + msgs = append(msgs, "missing setting: oidc-jwks-url") + } + keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL) + o.oidcVerifier = oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{ + ClientID: o.ClientID, + }) + } else { + // Configure discoverable provider data. + provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL) + if err != nil { + return err + } + o.oidcVerifier = provider.Verifier(&oidc.Config{ + ClientID: o.ClientID, + }) + + o.LoginURL = provider.Endpoint().AuthURL + o.RedeemURL = provider.Endpoint().TokenURL } - o.oidcVerifier = provider.Verifier(&oidc.Config{ - ClientID: o.ClientID, - }) - o.LoginURL = provider.Endpoint().AuthURL - o.RedeemURL = provider.Endpoint().TokenURL if o.Scope == "" { o.Scope = "openid email profile" } diff --git a/options_test.go b/options_test.go index fd1489b..7b12a2b 100644 --- a/options_test.go +++ b/options_test.go @@ -251,3 +251,20 @@ func TestValidateCookieBadName(t *testing.T) { assert.Equal(t, err.Error(), "Invalid configuration:\n"+ fmt.Sprintf(" invalid cookie name: %q", o.CookieName)) } + +func TestSkipOIDCDiscovery(t *testing.T) { + o := testOptions() + o.Provider = "oidc" + o.OIDCIssuerURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/v2.0/" + o.SkipOIDCDiscovery = true + + err := o.Validate() + assert.Equal(t, "Invalid configuration:\n"+ + fmt.Sprintf(" missing setting: login-url\n missing setting: redeem-url\n missing setting: oidc-jwks-url"), err.Error()) + + o.LoginURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_sign_in" + o.RedeemURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_sign_in" + o.OIDCJwksURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys" + + assert.Equal(t, nil, o.Validate()) +}