-
Notifications
You must be signed in to change notification settings - Fork 255
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: add PKCE to 3 Legged OAuth exchange #471
Conversation
README.md
Outdated
request.session['code_verifier'] ||= authorizer.generate_code_verifier | ||
authorizer.code_verifier = request.session['code_verifier'] |
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.
Is this how a user can generate the code verifier? It may be useful to give a short overview on what PKCE is
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.
Yes. Cool, added an overview.
lib/googleauth/user_authorizer.rb
Outdated
# Random string of 43-128 chars used to verify the key exchange using | ||
# PKCE. Auto-generated if not provided | ||
def initialize client_id, scope, token_store, | ||
callback_uri = nil, code_verifier = nil |
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 recommend using keyword arguments for additional optional arguments when the list gets this long. (I also recommend deprecating the existing callback_uri
optional argument and providing a keyword argument replacement.)
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.
Updated code_verifier
to be a keyword argument.
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'll raise another PR for deprecating callback_uri
.
lib/googleauth/user_authorizer.rb
Outdated
def initialize client_id, scope, token_store, callback_uri = nil | ||
# @param [String] code_verifier | ||
# Random string of 43-128 chars used to verify the key exchange using | ||
# PKCE. Auto-generated if not provided |
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.
The code doesn't look like it "auto-generates" the verifier if not provided. It looks like it simply doesn't include the code challenge.
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.
Ah yes! That was the implementation I was trying to follow but it doesn't work given the way our 3LO code is implemented. Removed that.
lib/googleauth/user_authorizer.rb
Outdated
|
||
# Generate the code verifier needed to be sent while fetching | ||
# authorization URL. | ||
def generate_code_verifier |
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.
Should this be a class method instead of an instance method? If it's an instance method, it kinda implies it might set @code_verifier
which it doesn't.
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 was also thinking which will be a better implementation. Class method makes more sense here and I've changed it accordingly.
lib/googleauth/user_authorizer.rb
Outdated
# authorization URL. | ||
def generate_code_verifier | ||
random_number = rand 32..96 | ||
SecureRandom.alphanumeric(random_number).to_str |
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 SecureRandom.alphanumeric
returns a string. Do we need to call to_str
on the result?
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.
No, we don't. Missed that.
7e6b9dc
to
556c6c0
Compare
Hey @clundin25 , I have addressed all the review comments. Can you please take a look at it again? |
b10b100
to
bcb501d
Compare
user_id = request.session['user_id'] | ||
# User needs to take care of generating the code_verifier and storing it in | ||
# the session. | ||
request.session['code_verifier'] ||= Google::Auth::WebUserAuthorizer.generate_code_verifier |
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.
What is the reason for storing the code_verifier
in the session? I understand the code was stateless so state needs to be stored elsewhere.
In the example, does it make sense to illustrate where code_verifier
is needed again?
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.
What is the reason for storing the `code_verifier` in the session? I understand the code was stateless so state needs to be stored elsewhere.
Yes, the reason behind storing code_verifier
in the session is to make sure we maintain the state and pass on the same code_verifier
in the subsequent call. Either we store the code_verifier
itself in the session or we make sure that we're using the same authorizer
object for further call, is upto the user how they want to design it.
In the example, does it make sense to illustrate where `code_verifier` is needed again?
I have done that on Line #123. I can write a comment on top and explain it further if it's not clear. WDYT ?
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.
Hmm I think the disconnect for me was in this example, why have the step of storing it in the session, instead of just setting it directly
Is the code reading it from the session somewhere, or is the user supposed to read it from the session later?
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.
Hmm I think the disconnect for me was in this example, why have the step of storing it in the session, instead of just setting it directly
The code is a property of Authorizer. But we may create a new instance of the Authorizer for the second call and lose the code.
Is the code reading it from the session somewhere, or is the user supposed to read it from the session later?
User is supposed to store it in the session when we create it for the first time. And then retrieve it from the session and set in the Authorizer object.
So it goes like:
- Generate a new code ( if it's not already there in the session ) and store it in the session for future call.
session[:code_verifier] ||= Google::Auth::WebUserAuthorizer.generate_code_verifier
- Assign it to Authorizer.
authorizer = Google::Auth::WebUserAuthorizer.new(..,.., code_verifier: session[:code_verifier])
authorizer
passes on the code withadditional_parameter
when you callauthorizer.get_credential()
which happens here
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.
Okay got it, thank you!
bcb501d
to
6cdcd21
Compare
b/270203889