.. _specs/rfc7636:

RFC7636: Proof Key for Code Exchange by OAuth Public Clients
============================================================

.. meta::
    :description: API references on RFC76736 Proof Key for Code Exchange
        by OAuth Public Clients implementation, guide on how to add it to
        OAuth 2.0 authorization server.

This RFC7636_ is used to improve the security of Authorization Code flow
for public clients by sending extra "code_challenge" and "code_verifier"
to the authorization server.

.. _RFC7636: https://tools.ietf.org/html/rfc7636

.. module:: authlib.oauth2.rfc7636


Using RFC7636 in Authorization Code Grant
-----------------------------------------

.. versionadded:: v0.10

In order to apply proof key for code exchange, you need to register the
:class:`CodeChallenge` extension to ``AuthorizationCodeGrant``. But before
that, we need to re-design our AuthorizationCode database.

* For Flask Developers, check the section :ref:`flask_oauth2_code_grant`.
* For Django Developers, check the section :ref:`django_oauth2_code_grant`.

The new database SHOULD contain two more columns:

1. code_challenge: A VARCHAR
2. code_challenge_method: A VARCHAR

And the ``AuthorizationCodeGrant`` should record the ``code_challenge`` and
``code_challenge_method`` into database in ``create_authorization_code``
method::


    class MyAuthorizationCodeGrant(AuthorizationCodeGrant):
        def create_authorization_code(self, client, grant_user, request):
            code = generate_token(48)

            # NOTICE BELOW
            code_challenge = request.data.get('code_challenge')
            code_challenge_method = request.data.get('code_challenge_method')
            item = AuthorizationCode(
                code=code,
                client_id=client.client_id,
                redirect_uri=request.redirect_uri,
                scope=request.scope,
                user_id=grant_user.get_user_id(),
                code_challenge=code_challenge,
                code_challenge_method=code_challenge_method,
            )
            db.session.add(item)
            db.session.commit()
            return code


Now you can register your ``AuthorizationCodeGrant`` with the extension::

    from authlib.oauth2.rfc7636 import CodeChallenge
    server.register_grant(MyAuthorizationCodeGrant, [CodeChallenge(required=True)])

If ``required=True``, code challenge is required for authorization code flow.
If ``required=False``, it is optional, it will only valid the code challenge
when clients send these parameters.

Using ``code_challenge`` in Client
----------------------------------

Read the **Code Challenge** section in the :ref:`frameworks_clients`.

It is also possible to add ``code_challenge`` in ``OAuth2Session``,
consider that we already have a ``session``::

    >>> from authlib.oauth2.rfc7636 import create_s256_code_challenge
    >>> code_verifier = generate_token(48)
    >>> code_challenge = create_s256_code_challenge(code_verifier)
    >>> uri, state = session.create_authorization_url(authorize_url, code_challenge=code_challenge, code_challenge_method='S256')
    >>> # visit uri, get the response
    >>> authorization_response = 'https://example.com/auth?code=42..e9&state=d..t'
    >>> token = session.fetch_token(token_endpoint, authorization_response=authorization_response, code_verifier=code_verifier)

The authorization flow is the same as in :ref:`oauth_2_session`, what you need
to do is:

1. adding ``code_challenge`` and ``code_challenge_method`` in ``.create_authorization_url``.
2. adding ``code_verifier`` in ``.fetch_token``.

API Reference
-------------

.. autoclass:: CodeChallenge
    :member-order: bysource
    :members:
