Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fabcf86

Browse files
authored
Merge pull request #593 from jvanasco/fix-585_client_id
PR for #585, `client_id` behavior with `prepare_request_body`
2 parents 326456c + 127a3b5 commit fabcf86

10 files changed

+281
-27
lines changed

CHANGELOG.rst

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Changelog
22
=========
33

4+
Unreleased
5+
------------------
6+
7+
* OAuth2's `prepare_token_request` supports sending an empty string for `client_id` (#585)
8+
* OAuth2's `WebApplicationClient.prepare_request_body` was refactored to better
9+
support sending or omitting the `client_id` via a new `include_client_id` kwarg.
10+
By default this is included. The method will also emit a DeprecationWarning if
11+
a `client_id` parameter is submitted; the already configured `self.client_id`
12+
is the preferred option. (#585)
13+
414
2.1.0 (2018-05-21)
515
------------------
616

oauthlib/oauth2/rfc6749/clients/backend_application.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,26 @@ class BackendApplicationClient(Client):
3030
no additional authorization request is needed.
3131
"""
3232

33-
def prepare_request_body(self, body='', scope=None, **kwargs):
33+
def prepare_request_body(self, body='', scope=None,
34+
include_client_id=None, **kwargs):
3435
"""Add the client credentials to the request body.
3536
3637
The client makes a request to the token endpoint by adding the
3738
following parameters using the "application/x-www-form-urlencoded"
3839
format per `Appendix B`_ in the HTTP request entity-body:
3940
41+
:param body: Existing request body (URL encoded string) to embed parameters
42+
into. This may contain extra paramters. Default ''.
4043
:param scope: The scope of the access request as described by
4144
`Section 3.3`_.
45+
46+
:param include_client_id: `True` to send the `client_id` in the body of
47+
the upstream request. Default `None`. This is
48+
required if the client is not authenticating
49+
with the authorization server as described
50+
in `Section 3.2.1`_.
51+
:type include_client_id: Boolean
52+
4253
:param kwargs: Extra credentials to include in the token request.
4354
4455
The client MUST authenticate with the authorization server as
@@ -56,5 +67,7 @@ def prepare_request_body(self, body='', scope=None, **kwargs):
5667
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
5768
.. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
5869
"""
70+
kwargs['client_id'] = self.client_id
71+
kwargs['include_client_id'] = include_client_id
5972
return prepare_token_request('client_credentials', body=body,
6073
scope=scope, **kwargs)

oauthlib/oauth2/rfc6749/clients/base.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ def prepare_token_request(self, token_url, authorization_response=None,
254254
:param redirect_url: The redirect_url supplied with the authorization
255255
request (if there was one).
256256
257-
:param body: Request body (URL encoded string).
257+
:param body: Existing request body (URL encoded string) to embed parameters
258+
into. This may contain extra paramters. Default ''.
258259
259260
:param kwargs: Additional parameters to included in the request.
260261
@@ -286,7 +287,8 @@ def prepare_refresh_token_request(self, token_url, refresh_token=None,
286287
287288
:param refresh_token: Refresh token string.
288289
289-
:param body: Request body (URL encoded string).
290+
:param body: Existing request body (URL encoded string) to embed parameters
291+
into. This may contain extra paramters. Default ''.
290292
291293
:param scope: List of scopes to request. Must be equal to
292294
or a subset of the scopes granted when obtaining the refresh

oauthlib/oauth2/rfc6749/clients/legacy_application.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class LegacyApplicationClient(Client):
3838
def __init__(self, client_id, **kwargs):
3939
super(LegacyApplicationClient, self).__init__(client_id, **kwargs)
4040

41-
def prepare_request_body(self, username, password, body='', scope=None, **kwargs):
41+
def prepare_request_body(self, username, password, body='', scope=None,
42+
include_client_id=None, **kwargs):
4243
"""Add the resource owner password and username to the request body.
4344
4445
The client makes a request to the token endpoint by adding the
@@ -47,8 +48,16 @@ def prepare_request_body(self, username, password, body='', scope=None, **kwargs
4748
4849
:param username: The resource owner username.
4950
:param password: The resource owner password.
51+
:param body: Existing request body (URL encoded string) to embed parameters
52+
into. This may contain extra paramters. Default ''.
5053
:param scope: The scope of the access request as described by
5154
`Section 3.3`_.
55+
:param include_client_id: `True` to send the `client_id` in the body of
56+
the upstream request. Default `None`. This is
57+
required if the client is not authenticating
58+
with the authorization server as described
59+
in `Section 3.2.1`_.
60+
:type include_client_id: Boolean
5261
:param kwargs: Extra credentials to include in the token request.
5362
5463
If the client type is confidential or the client was issued client
@@ -68,5 +77,7 @@ def prepare_request_body(self, username, password, body='', scope=None, **kwargs
6877
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
6978
.. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
7079
"""
80+
kwargs['client_id'] = self.client_id
81+
kwargs['include_client_id'] = include_client_id
7182
return prepare_token_request('password', body=body, username=username,
7283
password=password, scope=scope, **kwargs)

oauthlib/oauth2/rfc6749/clients/service_application.py

+23-8
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def prepare_request_body(self,
7272
issued_at=None,
7373
extra_claims=None,
7474
body='',
75-
scope=None,
75+
scope=None,
76+
include_client_id=None,
7677
**kwargs):
7778
"""Create and add a JWT assertion to the request body.
7879
@@ -97,20 +98,32 @@ def prepare_request_body(self,
9798
:param issued_at: A unix timestamp of when the JWT was created.
9899
Defaults to now, i.e. ``time.time()``.
99100
100-
:param not_before: A unix timestamp after which the JWT may be used.
101-
Not included unless provided.
102-
103-
:param jwt_id: A unique JWT token identifier. Not included unless
104-
provided.
105-
106101
:param extra_claims: A dict of additional claims to include in the JWT.
107102
103+
:param body: Existing request body (URL encoded string) to embed parameters
104+
into. This may contain extra paramters. Default ''.
105+
108106
:param scope: The scope of the access request.
109107
110-
:param body: Request body (string) with extra parameters.
108+
:param include_client_id: `True` to send the `client_id` in the body of
109+
the upstream request. Default `None`. This is
110+
required if the client is not authenticating
111+
with the authorization server as described
112+
in `Section 3.2.1`_.
113+
:type include_client_id: Boolean
114+
115+
:param not_before: A unix timestamp after which the JWT may be used.
116+
Not included unless provided. *
117+
118+
:param jwt_id: A unique JWT token identifier. Not included unless
119+
provided. *
111120
112121
:param kwargs: Extra credentials to include in the token request.
113122
123+
Parameters marked with a `*` above are not explicit arguments in the
124+
function signature, but are specially documented arguments for items
125+
appearing in the generic `**kwargs` keyworded input.
126+
114127
The "scope" parameter may be used, as defined in the Assertion
115128
Framework for OAuth 2.0 Client Authentication and Authorization Grants
116129
[I-D.ietf-oauth-assertions] specification, to indicate the requested
@@ -168,6 +181,8 @@ def prepare_request_body(self,
168181
assertion = jwt.encode(claim, key, 'RS256')
169182
assertion = to_unicode(assertion)
170183

184+
kwargs['client_id'] = self.client_id
185+
kwargs['include_client_id'] = include_client_id
171186
return prepare_token_request(self.grant_type,
172187
body=body,
173188
assertion=assertion,

oauthlib/oauth2/rfc6749/clients/web_application.py

+33-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"""
99
from __future__ import absolute_import, unicode_literals
1010

11+
import warnings
12+
1113
from ..parameters import (parse_authorization_code_response,
1214
parse_token_response, prepare_grant_uri,
1315
prepare_token_request)
@@ -85,24 +87,30 @@ def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
8587
return prepare_grant_uri(uri, self.client_id, 'code',
8688
redirect_uri=redirect_uri, scope=scope, state=state, **kwargs)
8789

88-
def prepare_request_body(self, client_id=None, code=None, body='',
89-
redirect_uri=None, **kwargs):
90+
def prepare_request_body(self, code=None, redirect_uri=None, body='',
91+
include_client_id=True, **kwargs):
9092
"""Prepare the access token request body.
9193
9294
The client makes a request to the token endpoint by adding the
9395
following parameters using the "application/x-www-form-urlencoded"
9496
format in the HTTP request entity-body:
9597
96-
:param client_id: REQUIRED, if the client is not authenticating with the
97-
authorization server as described in `Section 3.2.1`_.
98-
9998
:param code: REQUIRED. The authorization code received from the
10099
authorization server.
101100
102101
:param redirect_uri: REQUIRED, if the "redirect_uri" parameter was included in the
103102
authorization request as described in `Section 4.1.1`_, and their
104103
values MUST be identical.
105104
105+
:param body: Existing request body (URL encoded string) to embed parameters
106+
into. This may contain extra paramters. Default ''.
107+
108+
:param include_client_id: `True` (default) to send the `client_id` in the
109+
body of the upstream request. This is required
110+
if the client is not authenticating with the
111+
authorization server as described in `Section 3.2.1`_.
112+
:type include_client_id: Boolean
113+
106114
:param kwargs: Extra parameters to include in the token request.
107115
108116
In addition OAuthLib will add the ``grant_type`` parameter set to
@@ -120,12 +128,31 @@ def prepare_request_body(self, client_id=None, code=None, body='',
120128
>>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
121129
'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar'
122130
131+
`Section 3.2.1` also states:
132+
In the "authorization_code" "grant_type" request to the token
133+
endpoint, an unauthenticated client MUST send its "client_id" to
134+
prevent itself from inadvertently accepting a code intended for a
135+
client with a different "client_id". This protects the client from
136+
substitution of the authentication code. (It provides no additional
137+
security for the protected resource.)
138+
123139
.. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1
124140
.. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
125141
"""
126142
code = code or self.code
143+
if 'client_id' in kwargs:
144+
warnings.warn("`client_id` has been deprecated in favor of "
145+
"`include_client_id`, a boolean value which will "
146+
"include the already configured `self.client_id`.",
147+
DeprecationWarning)
148+
if kwargs['client_id'] != self.client_id:
149+
raise ValueError("`client_id` was supplied as an argument, but "
150+
"it does not match `self.client_id`")
151+
152+
kwargs['client_id'] = self.client_id
153+
kwargs['include_client_id'] = include_client_id
127154
return prepare_token_request('authorization_code', code=code, body=body,
128-
client_id=client_id, redirect_uri=redirect_uri, **kwargs)
155+
redirect_uri=redirect_uri, **kwargs)
129156

130157
def parse_request_uri_response(self, uri, state=None):
131158
"""Parse the URI query for code and state.

oauthlib/oauth2/rfc6749/parameters.py

+43-6
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
8787
return add_params_to_uri(uri, params)
8888

8989

90-
def prepare_token_request(grant_type, body='', **kwargs):
90+
def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs):
9191
"""Prepare the access token request.
9292
9393
The client makes a request to the token endpoint by adding the
@@ -96,14 +96,38 @@ def prepare_token_request(grant_type, body='', **kwargs):
9696
9797
:param grant_type: To indicate grant type being used, i.e. "password",
9898
"authorization_code" or "client_credentials".
99-
:param body: Existing request body to embed parameters in.
100-
:param code: If using authorization code grant, pass the previously
101-
obtained authorization code as the ``code`` argument.
99+
100+
:param body: Existing request body (URL encoded string) to embed parameters
101+
into. This may contain extra paramters. Default ''.
102+
103+
:param include_client_id: `True` (default) to send the `client_id` in the
104+
body of the upstream request. This is required
105+
if the client is not authenticating with the
106+
authorization server as described in
107+
`Section 3.2.1`_.
108+
:type include_client_id: Boolean
109+
110+
:param client_id: Unicode client identifier. Will only appear if
111+
`include_client_id` is True. *
112+
113+
:param client_secret: Unicode client secret. Will only appear if set to a
114+
value that is not `None`. Invoking this function with
115+
an empty string will send an empty `client_secret`
116+
value to the server. *
117+
118+
:param code: If using authorization_code grant, pass the previously
119+
obtained authorization code as the ``code`` argument. *
120+
102121
:param redirect_uri: If the "redirect_uri" parameter was included in the
103122
authorization request as described in
104-
`Section 4.1.1`_, and their values MUST be identical.
123+
`Section 4.1.1`_, and their values MUST be identical. *
124+
105125
:param kwargs: Extra arguments to embed in the request body.
106126
127+
Parameters marked with a `*` above are not explicit arguments in the
128+
function signature, but are specially documented arguments for items
129+
appearing in the generic `**kwargs` keyworded input.
130+
107131
An example of an authorization code token request body:
108132
109133
.. code-block:: http
@@ -118,6 +142,19 @@ def prepare_token_request(grant_type, body='', **kwargs):
118142
if 'scope' in kwargs:
119143
kwargs['scope'] = list_to_scope(kwargs['scope'])
120144

145+
# pull the `client_id` out of the kwargs.
146+
client_id = kwargs.pop('client_id', None)
147+
if include_client_id:
148+
if client_id is not None:
149+
params.append((unicode_type('client_id'), client_id))
150+
151+
# the kwargs iteration below only supports including boolean truth (truthy)
152+
# values, but some servers may require an empty string for `client_secret`
153+
client_secret = kwargs.pop('client_secret', None)
154+
if client_secret is not None:
155+
params.append((unicode_type('client_secret'), client_secret))
156+
157+
# this handles: `code`, `redirect_uri`, and other undocumented params
121158
for k in kwargs:
122159
if kwargs[k]:
123160
params.append((unicode_type(k), kwargs[k]))
@@ -144,7 +181,7 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
144181
token types. An authorization server MAY ignore
145182
this parameter, particularly if it is able to detect
146183
the token type automatically.
147-
184+
148185
This specification defines two values for `token_type_hint`:
149186
150187
* access_token: An access token as defined in [RFC6749],

tests/oauth2/rfc6749/clients/test_backend_application.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
class BackendApplicationClientTest(TestCase):
1616

1717
client_id = "someclientid"
18+
client_secret = 'someclientsecret'
1819
scope = ["/profile"]
1920
kwargs = {
2021
"some": "providers",

0 commit comments

Comments
 (0)