File: rfc8628.rst

package info (click to toggle)
python-authlib 1.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,016 kB
  • sloc: python: 26,998; makefile: 53; sh: 14
file content (121 lines) | stat: -rw-r--r-- 3,899 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
.. _specs/rfc8628:

RFC8628: OAuth 2.0 Device Authorization Grant
=============================================

.. meta::
    :description: Python API references on RFC8628 DeviceAuthorizationEndpoint
        and DeviceCodeGrant with Authlib implementation.

.. module:: authlib.oauth2.rfc8628

This section contains the generic implementation of RFC8628_. OAuth 2.0 Device
Authorization Grant is usually used when devices have limited input capabilities
or lack a suitable browser, such as smart TVs, media consoles, picture frames,
printers and etc.

To integrate with Authlib :ref:`flask_oauth2_server` or :ref:`django_oauth2_server`,
developers MUST implement the missing methods of the two classes:

1. :class:`DeviceAuthorizationEndpoint`
2. :class:`DeviceCodeGrant`

Device Authorization Endpoint
-----------------------------

There are two missing methods that developers MUST implement::

    from authlib.oauth2.rfc8628 import DeviceAuthorizationEndpoint

    class MyDeviceAuthorizationEndpoint(DeviceAuthorizationEndpoint):
        def get_verification_uri(self):
            return 'https://example.com/active'

        def save_device_credential(self, client_id, scope, data):
            credential = DeviceCredential(
                client_id=client_id,
                scope=scope,
                **data
            )
            credential.save()

    # register it to authorization server
    authorization_server.register_endpoint(MyDeviceAuthorizationEndpoint)

``get_verification_uri`` is the URL that end user will use their browser to
log in and authenticate. See below "Verification Endpoint".

After the registration, you can create a response with::

    @app.route('/device_authorization', methods=['POST'])
    def device_authorization():
        return server.create_endpoint_response('device_authorization')

Device Code Grant
-----------------

With Authlib ``.register_grant``, we can add ``DeviceCodeGrant`` easily.
But first, we need to implement the missing methods::

    from authlib.oauth2.rfc8628 import DeviceCodeGrant

    class MyDeviceCodeGrant(DeviceCodeGrant):
        def query_device_credential(self, device_code):
            return DeviceCredential.query(device_code=device_code)

        def query_user_grant(self, user_code):
            data = redis.get('oauth_user_grant:' + user_code)
            if not data:
                return None

            user_id, allowed = data.split()
            user = User.query.get(user_id)
            return user, bool(allowed)

        def should_slow_down(self, credential, now):
            # developers can return True/False based on credential and now
            return False

    authorization_server.register_grant(MyDeviceCodeGrant)

Note ``query_user_grant``, we are fetching data from redis. This data
was saved from verification endpoint when end user granted the request.

Verification Endpoint
---------------------

Developers MUST implement this part by themselves. Here is a hint on
how to implement this endpoint::

    @app.route('/active', methods=['GET', 'POST'])
    @login_required
    def verify_device_code():
        if request.method == 'GET':
            return render_template('verification.html')

        allowed = request.form['allowed']
        user_code = request.form['user_code']
        key = 'oauth_user_grant:' + user_code
        redis.set(key, f'{current_user.id} {allowed}', 12)
        return render_template('verification.html')

Check points:

1. route should match ``get_verification_uri`` in Device Authorization Endpoint
2. user grant should match ``query_user_grant`` in Device Code Grant


.. _RFC8628: https://tools.ietf.org/html/rfc8628


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

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

.. autoclass:: DeviceCodeGrant
    :member-order: bysource
    :members:
    :inherited-members: