• Implementing PKCE with OAuth2

    Why do we need PKCE?

    When using the authorization_code grant type with a public client type, you will be required to implement an OAuth2 extension known as PKCE (Public Key for Code Exchange). This is because public clients leak their client_secret, which means the standard authorization_code flow is not suitable for reasons explained quite well in this medium post.

    It's possible for public clients to use the implicit flow instead, and indeed you should if you can, however the implicit flow has trade offs such as returning no refresh tokens, which in practice means users must go through the oauth authorization dance every time a token expires. This is unaccaptable for many applications, so a solution was proposed as an extension to the OAuth2 spec. That solution was PKCE.

    The flow

    Implementing a PKCE authorization_code flow is very similar to the regular authorization_code flow, so we won't document it in its entirety. Instead we'll focus on the parts that differ, so you will also need to refer to the authorization_code documentation.

    An example implementation in Python can be found on github.

    The first thing we need to do is to generate something called a code_verifier.

    A: Generate a code_verifier

    A unique code_verifier must be generated for each authorization attempt.

    The code_verifier should be a cryptographic random string made up of the characters [A-Z], [a-z], [0-9], -, _, . and ~ between 43 and 128 characters in length.

    B: Generate a code_challenge

    With your code_verifier, we now need to generate a value we call the code_challenge. This must be done in a specific way for the handshake to work, otherwise the authorization server can not verify the authenticity of the code_verifier and your requests will be rejected.

    To generate a code_challenge, we have to first generate a SHA256 hash of our code_verifier, and then we base64 encode that hash in a url-safe manner. A url safe base64 encoding is one that replaces + with - and / with _.

    A good way to check you're generating the code_challenge correctly is to verify that your output matches the below when given the same input (verifier in this case):

    $ python -c 'from __future__ import print_function; import hashlib; import base64; print(base64.urlsafe_b64encode(hashlib.sha256("verifier".encode("utf8")).hexdigest().encode("utf8")))'
    ODhjOWVhZTY4ZWIzMDBiMjk3MWEyYmVjOWU1YTI2ZmY0MTc5ZmQ2NjFkNmI3ZDg2MWU0YzY1NTdiOWFhZWUxNA==
    

    C: Send your code_challenge along with the authorization request

    When redirecting the user to Marvel in order to ask for authorization (step 1), we'll need to send along two extra parameters.

    The first is the code_challenge value we generated earlier. The second is code_challenge_method, which will always have a value of S256.

    The full url should look something similar to this:

    https://marvelapp.com/oauth/authorize?
        state=<random_string>&
        client_id=<client_id>&
        response_type=code&
        redirect_uri=<redirect_uri>&
        scope=<space-separated scopes>&
        code_challenge=<code_challenge>&
        code_challenge_method=S256

    Send your code_verifier along with the token request

    After the user is redirected back to your application in step 2, we'll need to use the same code_verifier we generated the code_challenge from earlier and include that when swapping our auth code for an access token in step 3.

    The server will verify that the code_verifier is the correct counterpart for the code_challenge supplied at the authorization step.

    The full list of parameters needed for the token request are below:

    Name
    Value

    client_id

    <client_id>

    client_secret

    <client_secret>

    code

    <code>

    grant_type

    authorization_code

    redirect_uri

    <redirect_uri>

    code_verifier

    <code_verifier>

    Finish!

    If all went well you should now be the proud owner of an access token. Yay!