-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Ignore PKCE params for non-PKCE grants #1415
Conversation
@@ -93,7 +84,7 @@ def validate_redirect_uri | |||
# if either side (server or client) request PKCE, check the verifier | |||
# against the DB - if PKCE is supported | |||
def validate_code_verifier | |||
return true unless grant.uses_pkce? || code_verifier | |||
return true unless grant.uses_pkce? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check had to be removed as well, and caused the changed spec in spec/requests/flows/authorization_code_spec.rb
to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can thinking about validate_code_verifier
as following:
-
(1)
pkce non supported
(pkce_supported? == false) -> return true -
pkce is supported
(pkce_supported? == true)
code_challenge.present? | code_challenge.blank? | |
---|---|---|
code_verifier.present? | (2) run validate | (3) run validate |
code_verifier.blank? | (5) (already validated at validate_params)return false | (4) return true |
So, as the table, currently changes (Line 87 and 88) will not run validated on (2), when code_verifier is present, and code_challenge is not sent from client at authorization request, this can causing Downgrade Attack on PKCE
About (5)th condition, it is currently validated at validate_params
, but I still check it in the code 👇, because I think we should consider this is independent from validate_params
, any change to validate_params
should not break validate_code_verifier
(because validate_params
's task is to informs that client missing necessary parameter)
So I think this validation could be written as below:
def validate_code_verifier
return true unless pkce_supported? # for (1)
if code_verifier.blank?
return true if grant.code_challenge.blank? # for (4)
return false # for (5)
end
# run validate for (2) (3)
if grant.code_challenge_method == "S256"
.....
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linhdangduy thanks, this makes sense to me. 👍
I updated the method now as per your suggestion, though I simplified the 4/5 case into return grant.code_challenge.blank? if code_verifier.blank?
and added a spec for the case where PKCE is enabled but both code_challenge
and code_verifier
are blank. I think the remaining cases should already be covered by the specs in spec/lib/oauth/authorization_code_request_spec.rb
.
"error" => "invalid_grant", | ||
"error_description" => translated_error_message(:invalid_grant), | ||
) | ||
url_should_have_param("code", Doorkeeper::AccessGrant.first.token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused by the PKCE specs in this file because it seems we don't mock Doorkeeper::AccessGrant).pkce_supported?
, or set it up through other ways.
Should we add this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbulaj I saw now that we indirectly set this up by adding the relevant columns: https://github.com/toupeira/doorkeeper/blob/4a7a106dc03411d9ada0a627f6b46e9e8022c393/spec/dummy/db/schema.rb#L25-L28
Running these specs with WITHOUT_PKCE=1
makes them fail, so I think we're good here and I retract my concern 😁
@@ -55,14 +54,6 @@ def pkce_supported? | |||
Doorkeeper.config.access_grant_model.pkce_supported? | |||
end | |||
|
|||
def validate_pkce_support | |||
@invalid_request_reason = :not_support_pkce if grant && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because not_support_pkce
is used only here, so should remove from other files, too. (en.yml, invalid_request_response.rb)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linhdangduy thanks, missed those and removed them now 👍
@@ -93,7 +84,7 @@ def validate_redirect_uri | |||
# if either side (server or client) request PKCE, check the verifier | |||
# against the DB - if PKCE is supported | |||
def validate_code_verifier | |||
return true unless grant.uses_pkce? || code_verifier | |||
return true unless grant.uses_pkce? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can thinking about validate_code_verifier
as following:
-
(1)
pkce non supported
(pkce_supported? == false) -> return true -
pkce is supported
(pkce_supported? == true)
code_challenge.present? | code_challenge.blank? | |
---|---|---|
code_verifier.present? | (2) run validate | (3) run validate |
code_verifier.blank? | (5) (already validated at validate_params)return false | (4) return true |
So, as the table, currently changes (Line 87 and 88) will not run validated on (2), when code_verifier is present, and code_challenge is not sent from client at authorization request, this can causing Downgrade Attack on PKCE
About (5)th condition, it is currently validated at validate_params
, but I still check it in the code 👇, because I think we should consider this is independent from validate_params
, any change to validate_params
should not break validate_code_verifier
(because validate_params
's task is to informs that client missing necessary parameter)
So I think this validation could be written as below:
def validate_code_verifier
return true unless pkce_supported? # for (1)
if code_verifier.blank?
return true if grant.code_challenge.blank? # for (4)
return false # for (5)
end
# run validate for (2) (3)
if grant.code_challenge_method == "S256"
.....
end
end
This aligns with sections 3.1 and 3.2 from RFC 6749: > The authorization server MUST ignore unrecognized request parameters. https://tools.ietf.org/html/rfc6749#section-3.1 https://tools.ietf.org/html/rfc6749#section-3.2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linhdangduy @nbulaj thanks for your feedback! 🙏 Your comments should be addressed now.
"error" => "invalid_grant", | ||
"error_description" => translated_error_message(:invalid_grant), | ||
) | ||
url_should_have_param("code", Doorkeeper::AccessGrant.first.token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbulaj I saw now that we indirectly set this up by adding the relevant columns: https://github.com/toupeira/doorkeeper/blob/4a7a106dc03411d9ada0a627f6b46e9e8022c393/spec/dummy/db/schema.rb#L25-L28
Running these specs with WITHOUT_PKCE=1
makes them fail, so I think we're good here and I retract my concern 😁
@@ -55,14 +54,6 @@ def pkce_supported? | |||
Doorkeeper.config.access_grant_model.pkce_supported? | |||
end | |||
|
|||
def validate_pkce_support | |||
@invalid_request_reason = :not_support_pkce if grant && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linhdangduy thanks, missed those and removed them now 👍
@@ -93,7 +84,7 @@ def validate_redirect_uri | |||
# if either side (server or client) request PKCE, check the verifier | |||
# against the DB - if PKCE is supported | |||
def validate_code_verifier | |||
return true unless grant.uses_pkce? || code_verifier | |||
return true unless grant.uses_pkce? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linhdangduy thanks, this makes sense to me. 👍
I updated the method now as per your suggestion, though I simplified the 4/5 case into return grant.code_challenge.blank? if code_verifier.blank?
and added a spec for the case where PKCE is enabled but both code_challenge
and code_verifier
are blank. I think the remaining cases should already be covered by the specs in spec/lib/oauth/authorization_code_request_spec.rb
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚝
LGTM
Thanks @toupeira ! 🤝 |
Summary
This aligns the behavior with sections 3.1 and 3.2 from RFC 6749:
https://tools.ietf.org/html/rfc6749#section-3.1
https://tools.ietf.org/html/rfc6749#section-3.2
Other Information
Closes #1411