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

Support for OIDC Proof Of Key for Code Exchange (PKCE) #23423

Merged
merged 1 commit into from
Feb 10, 2022

Conversation

sberyozkin
Copy link
Member

@sberyozkin sberyozkin commented Feb 3, 2022

Fixes #12856

This PR resolves one of the oldest open OIDC issues. It introduces PKCE support (without the plain challenge algorithm which is not really required).

It is not really strictly necessary for Quarkus OIDC web-app applications which are confidential OIDC clients capable of storing safely the client secret - thus eliminating the risk of the code being used by someone else to get the token.
However it does add one more layer of protection even in this case, OAuth 2.1 will not accept the code flows without PKCE, and it will help with some integrations, example, with Twitter (@FroMage, FYI).

So it is the right time to add this feature.

The actual PKCE support is not difficult - but I had to introduce an option to encrypt the extra state cookie content to avoid leaking the code verifier. I decided for now not to encrypt it all into the actual state query parameter as it can stress the URI limit, so it is kept in the encrypted form in the state cookie. If the PKCE is not required then the state cookie will not have any encryption applied to it, there is nothing sensitive in it without the PKCE code verifier.

A256KW requiring a 32 byte keys is currently used by default which is very secure but I've opened a smallrye-jwt issue to facilitate the use of A256GCMKW and switch to it a bit later on.

Testing that it works against Keycloak proved easy enough, it had to be done against Keycloak. But testing that it does not work after submitting a code challenge and dropping the code verifier during the code exchange proved trickier, with Wiremock it would be straightforward but with Wiremock we can't really prove that quarkus-oidc calculates the code verifier and challenge correctly.

So I have updated the existing CodeFlowTest https tests to verify the content of the state cookie that it contains the correct code verifier and it matches, after the s256 digest applied to it, the code challenge query parameter, then I created a new test, where this verification also passes, then, before forwarding a redirect from Keycloak to Quarkus, I removed the existing state cookie, created a new one containing only the state value which is also returned from Keycloak, but without the code verifier, and this time it reports 401 as expected. Not sure if anything else can be done to verify this case.

Docs have also been updated.

@FroMage
Copy link
Member

FroMage commented Feb 4, 2022

Twitter support would be good! I didn't figure out how to make it work.

@sberyozkin sberyozkin marked this pull request as ready for review February 4, 2022 15:09
@sberyozkin
Copy link
Member Author

@pedroigor Hi Pedro, this is now ready for review

@sberyozkin
Copy link
Member Author

sberyozkin commented Feb 4, 2022

@FroMage Hi Steph, according to https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code, if you choose OAuth2 (as opposed to OAuth 1.1) settings then it should work with this PR - since they require PKCE, see Authorize URL example there; once you get the code it should all work similarly to GitHub, they should have a UserInfo related endpoint, as I'm not sure they support IdTokens.

@sberyozkin
Copy link
Member Author

@FroMage It is all described here, https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token

You can give it a quick try once PR is merged or even while it is being reviewed

@quarkus-bot
Copy link

quarkus-bot bot commented Feb 4, 2022

This workflow status is outdated as a new workflow run has been triggered.

Failing Jobs - Building 99fde66

Status Name Step Failures Logs Raw logs
Initial JDK 11 Build Build Failures Logs Raw logs

Failures

⚙️ Initial JDK 11 Build #

- Failing: extensions/oidc/runtime 
! Skipped: devtools/bom-descriptor-json docs extensions/keycloak-authorization/deployment and 20 more

📦 extensions/oidc/runtime

Failed to execute goal net.revelc.code.formatter:formatter-maven-plugin:2.17.1:validate (default) on project quarkus-oidc: File '/home/runner/work/quarkus/quarkus/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java' has not been previously formatted. Please format file and commit before running validation!

@sberyozkin
Copy link
Member Author

sberyozkin commented Feb 4, 2022

Hmm, I might've done some JavaDocs updates at the last minute

@sberyozkin sberyozkin force-pushed the oidc_pkce branch 2 times, most recently from 6045c83 to b889e60 Compare February 7, 2022 10:56
Copy link
Contributor

@pedroigor pedroigor left a comment

Choose a reason for hiding this comment

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

@sberyozkin Nice !

Sorry if I missed it, but perhaps we should also make sure the AS supports S256 method when bootstrapping?

Or perhaps PKCE should be enabled whenever the AS supports method S256 with the option to disable it using the new option?

Other than those comments, LGTM.

@sberyozkin
Copy link
Member Author

sberyozkin commented Feb 10, 2022

Hi @pedroigor Thanks for the helpful comments, see some of the responses above, and will follow with a charset update shortly

Sorry if I missed it, but perhaps we should also make sure the AS supports S256 method when bootstrapping?

It makes sense but would be a bit concerned that it can be problematic in cases where the provider does not support the discovery (Twitter probably does not) or when they just don't return the info about PKCE

Or perhaps PKCE should be enabled whenever the AS supports method S256 with the option to disable it using the new option?

That is interesting. I was contemplating something along these lines, but then decided not to because of

  • there is some extra cost associated with supporting PKCE (namely the code verifier encryption/decryption) - it is not a big cost but for ex making it a default for all Keycloak web-app users might be of concern
  • but I guess more important is that if the quarkus endpoint has a client id and a client secret (or JWT or MTLS authentication) then this Quarkus endpoint is a confidential client, so the code, even if hijacked, would fail with only the client id but without the secret, so PKCE is not strictly necessary in this case.

But, once we implement your idea of a secure profile - then I think it will have to be automatically enabled if the provider supports it unless the user has set pkce-required=false - as per your proposal.

Does it sound OK ?

However if Quarkus web-app has a public client then PKCE would certainly help - but there'd be no way to encrypt the code verifier in the state cookie since no secrets will be configured.

I think you reminding about nonce would work best when it comes to dealing with Quarkus web-app public clients - as it can always be transparently added. We can probably add it for confidential clients by default as well, let me open an issue a bit later on and discuss,

Hope it makes sense

Copy link
Contributor

@pedroigor pedroigor left a comment

Choose a reason for hiding this comment

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

@sberyozkin Thanks for the clarifications.

@sberyozkin
Copy link
Member Author

Hey @pedroigor

I've opened #23579 and #23580, the last one is easier so I guess I'll tackle it first :-), the charset update is on the way, but I'd also like to tweak the code a bit and precalculate a secret key if only a default tenant is set (as related to your comment in the other PR, #23557)

@sberyozkin
Copy link
Member Author

Hi @pedroigor I forgot each tenant has a corresponding TenantConfigContext so it can hold nicely the precalculated and verified secret key, I updated the code, thanks for the idea, will follow with the same in #23557

@sberyozkin sberyozkin merged commit 849f171 into quarkusio:main Feb 10, 2022
@quarkus-bot quarkus-bot bot added this to the 2.8 - main milestone Feb 10, 2022
@sberyozkin sberyozkin deleted the oidc_pkce branch February 10, 2022 19:05
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Feb 10, 2022
@rolfedh
Copy link
Contributor

rolfedh commented Oct 6, 2022

Hi, @sberyozkin
Would you be willing to review the following release note content?

From:


Content:

- 1.3.1. Cloud

1.3.1.1. Support for Proof Key for Code Exchange (PKCE) in OIDC
This update adds support for Proof Key for Code Exchange (PKCE) to the 
Quarkus implementation of the OpenID Connect (OIDC) protocol. PKCE 
minimizes the risk of authorization code interception for Quarkus web 
applications that use OIDC. For more information, see 
[the PKCE-related section](https://quarkus.io/guides/security-openid-connect-web-authentication#proof-of-key-for-code-exchange-pkce) of the Quarkus "OpenID Connect (OIDC) Authorization 
Code Flow Mechanism" guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Support for OAuth 2.0 Authorization Code with PKCE Flow
4 participants