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

Enable OAuth2 PKCE extension in Doorkeeper #2312

Merged
merged 2 commits into from Aug 22, 2023
Merged

Enable OAuth2 PKCE extension in Doorkeeper #2312

merged 2 commits into from Aug 22, 2023

Conversation

chrisroos
Copy link
Contributor

@chrisroos chrisroos commented Aug 16, 2023

https://trello.com/c/59EBweBx

Signon utilises the OAuth 2.0 Authorization Code Grant type to enable users to authorize client applications to act on their behalf. That page includes:

It is recommended that all clients use the PKCE extension with this flow as well to provide better security.

This PR enables the PKCE extension in Doorkeeper. We're not yet mandating Signon clients to use PKCE but it will be used if the client supplies a code_challenge and code_challenge_method in the request for the Authorization Code.

We hope to mandate the use of PKCE once we're confident all Signon clients are using it.

@chrisroos chrisroos force-pushed the add-pkce branch 8 times, most recently from 9130b04 to 982c440 Compare August 21, 2023 15:50
@chrisroos chrisroos changed the title WIP: Add PKCE Enable OAuth2 PKCE extension in Doorkeeper Aug 21, 2023
@chrisroos chrisroos marked this pull request as ready for review August 21, 2023 16:00
Copy link
Contributor

@floehopper floehopper left a comment

Choose a reason for hiding this comment

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

This looks good to me 👍 I have a few thoughts...

  • I don't feel strongly about keeping CSRF enabled for the tests. Since Capybara::DSL is mixed into ActionDispatch::IntegrationTest would it be feasible/sensible to use those for the user bit of the interaction (i.e. signing in) thus coping with CSRF...? Or is there a problem that Capybara will follow redirects you don't want to...?
  • I suppose ideally you'd use different sessions, although I have to admit I'm only 99% sure that's actually how things work! Having said that, I'm not sure it's worth putting a lot of effort into it. Would using a mixture of Capybara & Rails request helpers (as described in my previous point) achieve separate sessions? If not, could you not just call ActionDispatch::Integration::Session#reset! in the test after signing in and before requesting the authorization code...?
  • These Doorkeeper docs mention checking a couple of things when enabling PKCE - it would be nice to mention somewhere that you have checked them even if they don't apply.

I'm happy to leave all the above to your discretion - essentially it'd be fine to merge as is!

Lastly, don't forget to remove the TODO points from the commit note before you merge!

This test demonstrates/exercises the OAuth2 interactions involved in the
Authorization Code Grant type[1]. This is the "happy path" that's
followed when the user has the signin permission in the application
they're authorizing to make requests on their behalf.

I'm adding this to give me confidence that I'm not breaking this
behaviour when I enable the PKCE extension.

Note that some of our "integration" tests are using Rails'
RequestHelpers[2] (e.g `get` and `post`) while others are using the
Capybara methods (e.g. `visit`). I'm using the Rails RequestHelpers as I
need to simulate requests that happen outside of the browser (e.g.  the
request for the Access Token).

Note that CSRF protection was enabled in our integration tests in
c69288d. It's not particularly useful
in these tests so I'm disabling it rather than working out how to
get/set the authenticity token.

I'm using `ActionDispatch::Integration::Runner#reset!`[3] to reset the
session between the user approving the Authorization Grant (in the form
of the Authorization Code) and the client using that Authorization Grant
to request an Access Token. My hope is that this makes it slightly
clearer that the Authorization Code request is performed by the user's
User Agent and the Access Token request, and subsequent request for the
protected resource, is performed by the client.

[1]: https://oauth.net/2/grant-types/authorization-code/
[2]: https://api.rubyonrails.org/v7.0.7/classes/ActionDispatch/Integration/RequestHelpers.html
[3]: https://api.rubyonrails.org/v7.0.7/classes/ActionDispatch/Integration/Runner.html#method-i-reset-21
I ran `bundle exec rails generate doorkeeper:pkce` to enable the PKCE
extension[1] in Doorkeeper[2]. Rubocop complained about the migration that
was generated so I changed it to use `change_table` instead of two calls
to `add_column`.

The PKCE extension isn't mandatory at this point (as demonstrated by the
"oauth2 authorization code grant type" test continuing to pass) but it
will be used if the client supplies a `code_challenge_method` and
`code_challenge` when requesting the Authorization Code.

It's our intention to mandate the use of PKCE once we can be sure all
clients of Signon are using it.

The Doorkeeper docs[2] include the following 2 points to consider:

1. If you overrode the doorkeeper/authorizations/new.html.erb view, make
   sure that you have the code_challenge and code_challenge_method hidden
   form fields.

We never render this template and haven't overridden it so this is of no
concern to us.

2. Also, in case your client is public (e.g. mobile app, single page
   app) note that the Doorkeeper::Application for the client should have
   confidential: false.

We only support confidential clients at the moment so this is of no
concern to us. Note that we set `Doorkeeper::Application#confidential`
to true in schema.rb[3] and don't allow it to be overridden in the UI.

[1]: https://datatracker.ietf.org/doc/html/rfc7636
[2]: https://doorkeeper.gitbook.io/guides/ruby-on-rails/pkce-flow
[3]: https://github.com/alphagov/signon/blob/78ebf57fe0a787d1e462b39f2475d3b74f085511/db/schema.rb#L114
@chrisroos
Copy link
Contributor Author

Thanks, @floehopper 👍

This looks good to me 👍 I have a few thoughts...

  • I don't feel strongly about keeping CSRF enabled for the tests. Since Capybara::DSL is mixed into ActionDispatch::IntegrationTest would it be feasible/sensible to use those for the user bit of the interaction (i.e. signing in) thus coping with CSRF...? Or is there a problem that Capybara will follow redirects you don't want to...?

I haven't tried but I worry that it might be confusing to mix the two styles in a single test. Especially as I'd prefer to more explicitly split the integration-style and the system-style tests that already make up our "integration tests".

  • I suppose ideally you'd use different sessions, although I have to admit I'm only 99% sure that's actually how things work! Having said that, I'm not sure it's worth putting a lot of effort into it. Would using a mixture of Capybara & Rails request helpers (as described in my previous point) achieve separate sessions? If not, could you not just call ActionDispatch::Integration::Session#reset! in the test after signing in and before requesting the authorization code...?

I've updated the commit to use reset! as you've suggested. I'm not entirely sure how much it helps but hopefully it gives some clue that the exchange of an Authorization Code for an Access Token, and the use of the Access Token to request a protected resource, are triggered from the client and don't require the user to be signed in to Signon.

  • These Doorkeeper docs mention checking a couple of things when enabling PKCE - it would be nice to mention somewhere that you have checked them even if they don't apply.

I've updated the commit note to explain why these don't concern us.

I'm happy to leave all the above to your discretion - essentially it'd be fine to merge as is!

Thank you!

Lastly, don't forget to remove the TODO points from the commit note before you merge!

Done.

@chrisroos chrisroos merged commit dc6e39f into main Aug 22, 2023
6 checks passed
@chrisroos chrisroos deleted the add-pkce branch August 22, 2023 14:55
chrisroos added a commit to alphagov/gds-sso that referenced this pull request Aug 23, 2023
https://trello.com/c/59EBweBx

In alphagov/signon#2312 we enabled the [OAuth2
PKCE extension](https://datatracker.ietf.org/doc/html/rfc7636) in
Signon.

In this commit we update our GDS OAuth2 OmniAuth Strategy to make use of
the PKCE extension. This means that any of our apps using this Gem will
benefit from the additional protection offered by the PKCE extension.
chrisroos added a commit to alphagov/gds-sso that referenced this pull request Aug 23, 2023
https://trello.com/c/59EBweBx

In alphagov/signon#2312 we enabled the OAuth2
PKCE extension[1] in Signon.

In this commit we update our GDS OAuth2 OmniAuth Strategy to make use of
the PKCE extension. This means that any of our apps using this Gem will
benefit from the additional protection offered by the PKCE extension.

[1]: https://datatracker.ietf.org/doc/html/rfc7636
chrisroos added a commit to alphagov/gds-sso that referenced this pull request Aug 23, 2023
https://trello.com/c/59EBweBx

In alphagov/signon#2312 we enabled the OAuth2
PKCE extension[1] in Signon.

In this commit we update our GDS OAuth2 OmniAuth Strategy to make use of
the PKCE extension. This means that any of our apps using this Gem will
benefit from the additional protection offered by the PKCE extension.

[1]: https://datatracker.ietf.org/doc/html/rfc7636
chrisroos added a commit to alphagov/gds-sso that referenced this pull request Aug 24, 2023
https://trello.com/c/59EBweBx

In alphagov/signon#2312 we enabled the OAuth2
PKCE extension[1] in Signon.

In this commit we update our GDS OAuth2 OmniAuth Strategy to make use of
the PKCE extension. This means that any of our apps using this Gem will
benefit from the additional protection offered by the PKCE extension.

[1]: https://datatracker.ietf.org/doc/html/rfc7636
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

2 participants