From e75885e4c2d8761794fcf595c178149959280b7c Mon Sep 17 00:00:00 2001 From: Michael Kelly Date: Wed, 22 Dec 2021 09:37:46 -0800 Subject: [PATCH] Add device token fetch URI generator In order to perform the full device authorization flow it's necessary to first generate the device code and get the authorization flow URL. prepare_request_uri() allows us to do this while providing scopes and additional parameters. --- oauthlib/oauth2/rfc8628/clients/device.py | 26 +++++++++++++++++++++ tests/oauth2/rfc8628/clients/test_device.py | 25 +++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/oauthlib/oauth2/rfc8628/clients/device.py b/oauthlib/oauth2/rfc8628/clients/device.py index c2b2360d..cd7e8cc8 100644 --- a/oauthlib/oauth2/rfc8628/clients/device.py +++ b/oauthlib/oauth2/rfc8628/clients/device.py @@ -8,7 +8,10 @@ """ from oauthlib.oauth2 import BackendApplicationClient, Client +from oauthlib.oauth2.rfc6749.errors import InsecureTransportError from oauthlib.oauth2.rfc6749.parameters import prepare_token_request +from oauthlib.oauth2.rfc6749.utils import is_secure_transport, list_to_scope +from oauthlib.common import add_params_to_uri class DeviceClient(Client): @@ -26,6 +29,29 @@ class DeviceClient(Client): grant_type = 'urn:ietf:params:oauth:grant-type:device_code' + def __init__(self, client_id, **kwargs): + super().__init__(client_id, **kwargs) + self.client_secret = kwargs.get('client_secret') + + def prepare_request_uri(self, uri, scope=None, **kwargs): + if not is_secure_transport(uri): + raise InsecureTransportError() + + scope = self.scope if scope is None else scope + params = [(('client_id', self.client_id)), (('grant_type', self.grant_type))] + + if self.client_secret is not None: + params.append(('client_secret', self.client_secret)) + + if scope: + params.append(('scope', list_to_scope(scope))) + + for k in kwargs: + if kwargs[k]: + params.append((str(k), kwargs[k])) + + return add_params_to_uri(uri, params) + def prepare_request_body(self, device_code, body='', scope=None, include_client_id=False, **kwargs): """Add device_code to request body diff --git a/tests/oauth2/rfc8628/clients/test_device.py b/tests/oauth2/rfc8628/clients/test_device.py index 943bf1b9..9881731c 100644 --- a/tests/oauth2/rfc8628/clients/test_device.py +++ b/tests/oauth2/rfc8628/clients/test_device.py @@ -17,13 +17,23 @@ class DeviceClientTest(TestCase): "require": "extra arguments" } + client_secret = "asecret" + + device_code = "somedevicecode" + + scope = ["profile", "email"] + body = "not=empty" body_up = "not=empty&grant_type=urn:ietf:params:oauth:grant-type:device_code" body_code = body_up + "&device_code=somedevicecode" body_kwargs = body_code + "&some=providers&require=extra+arguments" - device_code = 'somedevicecode' + uri = "https://example.com/path?query=world" + uri_id = uri + "&client_id=" + client_id + uri_grant = uri_id + "&grant_type=urn:ietf:params:oauth:grant-type:device_code" + uri_secret = uri_grant + "&client_secret=asecret" + uri_scope = uri_secret + "&scope=profile+email" def test_request_body(self): client = DeviceClient(self.client_id) @@ -40,3 +50,16 @@ def test_request_body(self): body = client.prepare_request_body( self.device_code, body=self.body, **self.kwargs) self.assertFormBodyEqual(body, self.body_kwargs) + + def test_request_uri(self): + client = DeviceClient(self.client_id) + + uri = client.prepare_request_uri(self.uri) + self.assertURLEqual(uri, self.uri_grant) + + client = DeviceClient(self.client_id, client_secret=self.client_secret) + uri = client.prepare_request_uri(self.uri) + self.assertURLEqual(uri, self.uri_secret) + + uri = client.prepare_request_uri(self.uri, scope=self.scope) + self.assertURLEqual(uri, self.uri_scope)