.. _specs/rfc7591:

RFC7591: OAuth 2.0 Dynamic Client Registration Protocol
=======================================================

.. meta::
    :description: Python API references on RFC7591 OAuth 2.0 Dynamic Client
        Registration Protocol in Python with Authlib implementation.

.. module:: authlib.oauth2.rfc7591

This section contains the generic implementation of RFC7591_. OAuth 2.0 Dynamic
Client Registration Protocol allows developers creating OAuth client via API
through Authorization Server.

To integrate with Authlib :ref:`flask_oauth2_server` or :ref:`django_oauth2_server`,
developers MUST implement the missing methods of :class:`ClientRegistrationEndpoint`.

.. _RFC7591: https://tools.ietf.org/html/rfc7591

Client Registration Endpoint
----------------------------

The client registration endpoint accepts client metadata as JSON payload via
POST request. The metadata may contain a :ref:`JWT <jwt_guide>` ``software_statement``
value. Endpoint can choose if it support ``software_statement``, it is not enabled
by default.

.. versionchanged:: v0.15

    ClientRegistrationEndpoint has a breaking change in v0.15. Method of
    ``authenticate_user`` is replaced by ``authenticate_token``, and parameters in
    ``save_client`` is also changed.

Before register the endpoint, developers MUST implement the missing methods::

    from authlib.oauth2.rfc7591 import ClientRegistrationEndpoint

    class MyClientRegistrationEndpoint(ClientRegistrationEndpoint):
        def authenticate_token(self, request):
            # this method is used to find who is going to create the client
            auth_header = request.headers.get('Authorization')
            # bearer a-token-string
            bearer_token = auth_header.split()[1]
            token = Token.query.get(bearer_token)
            return token

        def save_client(self, client_info, client_metadata, request):
            client = OAuthClient(
                user_id=request.credential.user_id,
                client_id=client_info['client_id'],
                client_secret=client_info['client_secret'],
                **client_metadata,
            )
            client.save()
            return client

        def generate_client_registration_info(self, client, request):
            return {
                'registration_client_uri': url_for("client_management_endpoint", client_id=client.client_id),
                'registration_access_token': create_management_access_token_for_client(client),
            }

If developers want to support ``software_statement``, additional methods
should be implemented::

    class MyClientRegistrationEndpoint(ClientRegistrationEndpoint):
        # adding this to support JWT with RS256 alg, you may change it to other alg values
        software_statement_alg_values_supported = ['RS256']

        def resolve_public_key(self, request):
            # the authenticated user's public key
            # can be a string, bytes, jwk and jwk set
            return request.user.public_jwk_set

Register this endpoint and use this endpoint in routes::

    authorization_server.register_endpoint(MyClientRegistrationEndpoint)

    # for Flask
    @app.route('/register', methods=['POST'])
    def client_registration():
        return authorization_server.create_endpoint_response('client_registration')

    # for Django
    from django.views.decorators.http import require_http_methods

    @require_http_methods(["POST"])
    def client_registration(request):
        return authorization_server.create_endpoint_response('client_registration', request)

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

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

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