Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support PKCE in OIDC connector #3188

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

vixus0
Copy link

@vixus0 vixus0 commented Nov 8, 2023

Overview

This adds a configuration option enablePKCE to the OIDC connector to send code challenge and verifier parameters as required for PKCE.

What this PR does / why we need it

Some OIDC providers require the PKCE flow, so a code challenge must be passed to the authorize endpoint, and a code verifier must be sent on the subsequent authorization token exchange.

Dex currently supports PKCE for downstream clients, but not for upstream OIDC providers.
Dynamic configuration using the OAuth2 discovery metadata isn't possible as the coreos/go-oidc library currently doesn't expose code_challenge_methods_supported in the Provider.

Special notes for your reviewer

Does this PR introduce a user-facing change?

* Adds the OIDC connector configuration option `enablePKCE` to send code
  challenge and verifier parameters to upstream OIDC providers as required for
  PKCE.

Some OIDC providers require the PKCE flow, so a code challenge must be
passed to the authorize endpoint, and a code verifier must be sent on
the subsequent authorization token exchange.

This adds the configuration option `enablePKCE` to the OIDC connector to
enable these PKCE parameters.

Signed-off-by: Anshul Sirur <anshul@vixus0.dev>
@vixus0 vixus0 changed the title Support PKCE in OIDC connector feat: support PKCE in OIDC connector Nov 8, 2023
@vixus0 vixus0 changed the title feat: support PKCE in OIDC connector feat: Support PKCE in OIDC connector Nov 8, 2023
@cameronbrunner
Copy link

I did a similar implementation here: #3195 with the biggest difference being that mine generates a new verifier for each login session. As 'oauth2.GenerateVerifier()' just gets a single random string it is stated that a new one should be done for each authorization: https://cs.opensource.google/go/x/oauth2/+/refs/tags/v0.15.0:pkce.go;l=22
Any thoughts on my PR?

@cameronbrunner
Copy link

Sorry I missed your PR when I did mine... I had been thinking about it prior to your work and missed this one when I started mine.

Copy link
Member

@nabokihms nabokihms left a comment

Choose a reason for hiding this comment

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

I have some questions regarding the implementation. The idea lgtm!

@@ -184,6 +187,11 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
c.PromptType = "consent"
}

pkceVerifier := ""
if c.EnablePKCE {
Copy link
Member

Choose a reason for hiding this comment

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

Do you know if we need an option for this? We can rely on what the provider exposes via the discovery endpoint.

  "code_challenge_methods_supported": [
    "S256",
    "plain"
  ],

If the code_challenge_methods_supported section is present, we can use PKCE.

Copy link
Member

@nabokihms nabokihms Dec 27, 2023

Choose a reason for hiding this comment

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

@vixus0 my bad, I overlooked the info from your first post

Dynamic configuration using the OAuth2 discovery metadata isn't possible as the coreos/go-oidc library currently doesn't expose code_challenge_methods_supported in the Provider.

Let's leave a comment in the code about this for other developers.

Copy link
Member

Choose a reason for hiding this comment

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

So, after checking the docs once again, there is an option to extract the provider's metadata by using https://pkg.go.dev/github.com/coreos/go-oidc/v3/oidc#Provider.Claims


if c.pkceVerifier != "" {
c.pkceVerifier = oauth2.GenerateVerifier()
opts = append(opts, oauth2.S256ChallengeOption(c.pkceVerifier))
Copy link
Member

Choose a reason for hiding this comment

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

S256 can be unsupported by the provider. It is required to either clarify that Dex only supports the S256 challenge or provide support for others.

We can rename the option EnablePKCE to PKCEChallenge instead of the boolean variable and let users specify the type of challenge they want to use.

@@ -265,6 +275,12 @@ func (c *oidcConnector) LoginURL(s connector.Scopes, callbackURL, state string)
if s.OfflineAccess {
opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", c.promptType))
}

if c.pkceVerifier != "" {
c.pkceVerifier = oauth2.GenerateVerifier()
Copy link

Choose a reason for hiding this comment

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

Isn't this setting just one verifier for all clients? It appears that if 2 clients were logging in at the same time the first one's verifier would be overwritten by the seconds preventing use of the proper verifier during token exchange (line 317-320).

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.

None yet

3 participants