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(oauth): Add RFC7636 aka PKCE support. #244

Closed
wants to merge 3 commits into from
Closed

feat(oauth): Add RFC7636 aka PKCE support. #244

wants to merge 3 commits into from

Conversation

Zenithar
Copy link

Ref #213

I have implemented RFC7636 aka PKCE.
But not really happy about Session usage in order to recevive CodeChallenge on token endpoint, maybe need some refactoring.

@coveralls
Copy link

Coverage Status

Coverage decreased (-19.8%) to 63.978% when pulling 53e628d on Zenithar:feat-pkce into 1ef3041 on ory:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.3%) to 82.477% when pulling 2a908a1 on Zenithar:feat-pkce into 1ef3041 on ory:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.0%) to 82.749% when pulling d724a36 on Zenithar:feat-pkce into 1ef3041 on ory:master.

Copy link
Member

@aeneasr aeneasr left a comment

Choose a reason for hiding this comment

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

Thank you for your work and these changes! I did not have the time yet to go through all the changes as it's a ton of code (~2500 LOCs). The first thing I noticed is that PKCE is hard-coded in the request handlers, but in the terms of the RFC it's really an add-in on top of OAuth2. Thus I'm really not sure if this should be handled there but instead be seen as a custom handler (like the authorize code flow or openid connect) which would fit more in the existing composition process (instead of setting some boolean in the main struct)

What do you think?

@@ -89,6 +91,30 @@ func (f *Fosite) NewAccessRequest(ctx context.Context, r *http.Request, session
}
accessRequest.Client = client

// PKCE Handling
Copy link
Member

Choose a reason for hiding this comment

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

I really think this should be a custom handler and not hardcoded here

Copy link
Author

Choose a reason for hiding this comment

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

Ok it's more like an OAuth2 extension. I'm not really aware of project code organization yet.

Copy link
Author

Choose a reason for hiding this comment

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

Most of the code are (re)generated mocks ^^

Copy link
Author

Choose a reason for hiding this comment

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

About PKCE settings (RequiredForPublicClient), maybe via a decorator (sort of middleware) in the authorize and token endpoints.

This could be helpful to add / process parameters before handling core OAuth operations.

Copy link
Member

Choose a reason for hiding this comment

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

The idea of this framework is to have handlers such as this one for the auth endpoint which implement parts of the oauth2 spec. What you would do (probably) is some checks at the /auth endpoint and then some checks or updates to the response at the /token endpoint

@@ -25,6 +25,11 @@ type AuthorizeRequest struct {
State string `json:"state" gorethink:"state"`
HandledResponseTypes Arguments `json:"handledResponseTypes" gorethink:"handledResponseTypes"`

// Optional code_challenge as described in rfc7636
CodeChallenge string `json:"code_challenge" gorethink:"code_challenge"`
Copy link
Member

Choose a reason for hiding this comment

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

I really think this can be covered by Request.Form

Copy link
Author

Choose a reason for hiding this comment

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

I consider that CodeChallenge is like State that's why in coded it here.

Copy link
Author

Choose a reason for hiding this comment

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

My first problem was the Session usage, due to the fact it's an interface and shared in openid and oauth2 handler (I fix missing implementation on first commit). I had to store code_challenge to compare it on the token retrieval step, maybe Request.Form is a better place if it is shareable like session.

Copy link
Member

Choose a reason for hiding this comment

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

So the Request.Form thing will contain all request parameters from the /auth endpoint, it will thus also contain code_challenge if that parameter is given to the /auth endpoint. You won't have to deal with any extra sessions or stuff there.

// https://tools.ietf.org/html/rfc7636
//
// PKCE is only for code response type
if request.ResponseTypes.Has("code") {
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here, this shouldn't be a hardcoded thing but instead an add-in via handlers

Copy link
Author

Choose a reason for hiding this comment

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

Yep I'm agree. Applying "Do it, do it right, to do better" philosophy ^^

@aeneasr
Copy link
Member

aeneasr commented Jan 15, 2018

@Zenithar can I help you some way to push this forward? :)

@Zenithar
Copy link
Author

Yes no problem ^^

@aeneasr
Copy link
Member

aeneasr commented Jan 16, 2018

Ok, so I'm not really fluent yet on PCKE internals but as far as I understood it works like this:

  1. Client generates verifier + challenge
  2. Client sends challenge to /auth
  3. Server responds with auth code code
  4. Client sends verifier + auth code to /token
  5. Server validates verifier with challenge from auth code

This is very easy to achieve with a new custom handler:

  1. PKCEHandler.HandleAuthorizeEndpointRequest checks if the client is public and if so, checks if a challenge was set. Depending on wether or not PKCE is forced (this can be a field in PKCEHandler) an error is thrown or not.
  2. PKCEHandler. HandleTokenEndpointRequest checks if the client is public, if yes, checks if a challenge was set (using AuthorizeRequest.Form["code_challenge"]. Depending on the wether or not PKCE is forced, an error is returned if code challenge is not set.
  3. If code challenge is set, it validates the challenge using the verifier. If no verifier was given an error is returned. If the verifier is invalid, an error is returned as well.

Does this make sense?

@Zenithar
Copy link
Author

Yes, it is exactly what it should work.

@Zenithar
Copy link
Author

This flow is mainly used by mobile application.

@aeneasr
Copy link
Member

aeneasr commented Feb 6, 2018

I took a shot at it in #246

@aeneasr aeneasr closed this Feb 6, 2018
@Zenithar Zenithar deleted the feat-pkce branch February 14, 2018 09:16
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