1"""
2oauthlib.oauth2.rfc6749.grant_types
3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4"""
5import logging
6
7from oauthlib import common
8
9from .. import errors
10from .base import GrantTypeBase
11
12log = logging.getLogger(__name__)
13
14
15class ImplicitGrant(GrantTypeBase):
16
17 """`Implicit Grant`_
18
19 The implicit grant type is used to obtain access tokens (it does not
20 support the issuance of refresh tokens) and is optimized for public
21 clients known to operate a particular redirection URI. These clients
22 are typically implemented in a browser using a scripting language
23 such as JavaScript.
24
25 Unlike the authorization code grant type, in which the client makes
26 separate requests for authorization and for an access token, the
27 client receives the access token as the result of the authorization
28 request.
29
30 The implicit grant type does not include client authentication, and
31 relies on the presence of the resource owner and the registration of
32 the redirection URI. Because the access token is encoded into the
33 redirection URI, it may be exposed to the resource owner and other
34 applications residing on the same device::
35
36 +----------+
37 | Resource |
38 | Owner |
39 | |
40 +----------+
41 ^
42 |
43 (B)
44 +----|-----+ Client Identifier +---------------+
45 | -+----(A)-- & Redirection URI --->| |
46 | User- | | Authorization |
47 | Agent -|----(B)-- User authenticates -->| Server |
48 | | | |
49 | |<---(C)--- Redirection URI ----<| |
50 | | with Access Token +---------------+
51 | | in Fragment
52 | | +---------------+
53 | |----(D)--- Redirection URI ---->| Web-Hosted |
54 | | without Fragment | Client |
55 | | | Resource |
56 | (F) |<---(E)------- Script ---------<| |
57 | | +---------------+
58 +-|--------+
59 | |
60 (A) (G) Access Token
61 | |
62 ^ v
63 +---------+
64 | |
65 | Client |
66 | |
67 +---------+
68
69 Note: The lines illustrating steps (A) and (B) are broken into two
70 parts as they pass through the user-agent.
71
72 Figure 4: Implicit Grant Flow
73
74 The flow illustrated in Figure 4 includes the following steps:
75
76 (A) The client initiates the flow by directing the resource owner's
77 user-agent to the authorization endpoint. The client includes
78 its client identifier, requested scope, local state, and a
79 redirection URI to which the authorization server will send the
80 user-agent back once access is granted (or denied).
81
82 (B) The authorization server authenticates the resource owner (via
83 the user-agent) and establishes whether the resource owner
84 grants or denies the client's access request.
85
86 (C) Assuming the resource owner grants access, the authorization
87 server redirects the user-agent back to the client using the
88 redirection URI provided earlier. The redirection URI includes
89 the access token in the URI fragment.
90
91 (D) The user-agent follows the redirection instructions by making a
92 request to the web-hosted client resource (which does not
93 include the fragment per [RFC2616]). The user-agent retains the
94 fragment information locally.
95
96 (E) The web-hosted client resource returns a web page (typically an
97 HTML document with an embedded script) capable of accessing the
98 full redirection URI including the fragment retained by the
99 user-agent, and extracting the access token (and other
100 parameters) contained in the fragment.
101
102 (F) The user-agent executes the script provided by the web-hosted
103 client resource locally, which extracts the access token.
104
105 (G) The user-agent passes the access token to the client.
106
107 See `Section 10.3`_ and `Section 10.16`_ for important security considerations
108 when using the implicit grant.
109
110 .. _`Implicit Grant`: https://tools.ietf.org/html/rfc6749#section-4.2
111 .. _`Section 10.3`: https://tools.ietf.org/html/rfc6749#section-10.3
112 .. _`Section 10.16`: https://tools.ietf.org/html/rfc6749#section-10.16
113 """
114
115 response_types = ['token']
116 grant_allows_refresh_token = False
117
118 def create_authorization_response(self, request, token_handler):
119 """Create an authorization response.
120
121 :param request: OAuthlib request.
122 :type request: oauthlib.common.Request
123 :param token_handler: A token handler instance, for example of type
124 oauthlib.oauth2.BearerToken.
125
126 The client constructs the request URI by adding the following
127 parameters to the query component of the authorization endpoint URI
128 using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
129
130 response_type
131 REQUIRED. Value MUST be set to "token" for standard OAuth2 implicit flow
132 or "id_token token" or just "id_token" for OIDC implicit flow
133
134 client_id
135 REQUIRED. The client identifier as described in `Section 2.2`_.
136
137 redirect_uri
138 OPTIONAL. As described in `Section 3.1.2`_.
139
140 scope
141 OPTIONAL. The scope of the access request as described by
142 `Section 3.3`_.
143
144 state
145 RECOMMENDED. An opaque value used by the client to maintain
146 state between the request and callback. The authorization
147 server includes this value when redirecting the user-agent back
148 to the client. The parameter SHOULD be used for preventing
149 cross-site request forgery as described in `Section 10.12`_.
150
151 The authorization server validates the request to ensure that all
152 required parameters are present and valid. The authorization server
153 MUST verify that the redirection URI to which it will redirect the
154 access token matches a redirection URI registered by the client as
155 described in `Section 3.1.2`_.
156
157 .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
158 .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
159 .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
160 .. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
161 .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
162 """
163 return self.create_token_response(request, token_handler)
164
165 def create_token_response(self, request, token_handler):
166 """Return token or error embedded in the URI fragment.
167
168 :param request: OAuthlib request.
169 :type request: oauthlib.common.Request
170 :param token_handler: A token handler instance, for example of type
171 oauthlib.oauth2.BearerToken.
172
173 If the resource owner grants the access request, the authorization
174 server issues an access token and delivers it to the client by adding
175 the following parameters to the fragment component of the redirection
176 URI using the "application/x-www-form-urlencoded" format, per
177 `Appendix B`_:
178
179 access_token
180 REQUIRED. The access token issued by the authorization server.
181
182 token_type
183 REQUIRED. The type of the token issued as described in
184 `Section 7.1`_. Value is case insensitive.
185
186 expires_in
187 RECOMMENDED. The lifetime in seconds of the access token. For
188 example, the value "3600" denotes that the access token will
189 expire in one hour from the time the response was generated.
190 If omitted, the authorization server SHOULD provide the
191 expiration time via other means or document the default value.
192
193 scope
194 OPTIONAL, if identical to the scope requested by the client;
195 otherwise, REQUIRED. The scope of the access token as
196 described by `Section 3.3`_.
197
198 state
199 REQUIRED if the "state" parameter was present in the client
200 authorization request. The exact value received from the
201 client.
202
203 The authorization server MUST NOT issue a refresh token.
204
205 .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
206 .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
207 .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
208 """
209 try:
210 self.validate_token_request(request)
211
212 # If the request fails due to a missing, invalid, or mismatching
213 # redirection URI, or if the client identifier is missing or invalid,
214 # the authorization server SHOULD inform the resource owner of the
215 # error and MUST NOT automatically redirect the user-agent to the
216 # invalid redirection URI.
217 except errors.FatalClientError as e:
218 log.debug('Fatal client error during validation of %r. %r.',
219 request, e)
220 raise
221
222 # If the resource owner denies the access request or if the request
223 # fails for reasons other than a missing or invalid redirection URI,
224 # the authorization server informs the client by adding the following
225 # parameters to the fragment component of the redirection URI using the
226 # "application/x-www-form-urlencoded" format, per Appendix B:
227 # https://tools.ietf.org/html/rfc6749#appendix-B
228 except errors.OAuth2Error as e:
229 log.debug('Client error during validation of %r. %r.', request, e)
230 return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples,
231 fragment=True)}, None, 302
232
233 # In OIDC implicit flow it is possible to have a request_type that does not include the access_token!
234 # "id_token token" - return the access token and the id token
235 # "id_token" - don't return the access token
236 token = token_handler.create_token(request, refresh_token=False) if 'token' in request.response_type.split() else {}
237
238 if request.state is not None:
239 token['state'] = request.state
240
241 for modifier in self._token_modifiers:
242 token = modifier(token, token_handler, request)
243
244 # In OIDC implicit flow it is possible to have a request_type that does
245 # not include the access_token! In this case there is no need to save a token.
246 if "token" in request.response_type.split():
247 self.request_validator.save_token(token, request)
248
249 return self.prepare_authorization_response(
250 request, token, {}, None, 302)
251
252 def validate_authorization_request(self, request):
253 """
254 :param request: OAuthlib request.
255 :type request: oauthlib.common.Request
256 """
257 return self.validate_token_request(request)
258
259 def validate_token_request(self, request):
260 """Check the token request for normal and fatal errors.
261
262 :param request: OAuthlib request.
263 :type request: oauthlib.common.Request
264
265 This method is very similar to validate_authorization_request in
266 the AuthorizationCodeGrant but differ in a few subtle areas.
267
268 A normal error could be a missing response_type parameter or the client
269 attempting to access scope it is not allowed to ask authorization for.
270 Normal errors can safely be included in the redirection URI and
271 sent back to the client.
272
273 Fatal errors occur when the client_id or redirect_uri is invalid or
274 missing. These must be caught by the provider and handled, how this
275 is done is outside of the scope of OAuthLib but showing an error
276 page describing the issue is a good idea.
277 """
278
279 # First check for fatal errors
280
281 # If the request fails due to a missing, invalid, or mismatching
282 # redirection URI, or if the client identifier is missing or invalid,
283 # the authorization server SHOULD inform the resource owner of the
284 # error and MUST NOT automatically redirect the user-agent to the
285 # invalid redirection URI.
286
287 # First check duplicate parameters
288 for param in ('client_id', 'response_type', 'redirect_uri', 'scope', 'state'):
289 try:
290 duplicate_params = request.duplicate_params
291 except ValueError:
292 raise errors.InvalidRequestFatalError(description='Unable to parse query string', request=request)
293 if param in duplicate_params:
294 raise errors.InvalidRequestFatalError(description='Duplicate %s parameter.' % param, request=request)
295
296 # REQUIRED. The client identifier as described in Section 2.2.
297 # https://tools.ietf.org/html/rfc6749#section-2.2
298 if not request.client_id:
299 raise errors.MissingClientIdError(request=request)
300
301 if not self.request_validator.validate_client_id(request.client_id, request):
302 raise errors.InvalidClientIdError(request=request)
303
304 # OPTIONAL. As described in Section 3.1.2.
305 # https://tools.ietf.org/html/rfc6749#section-3.1.2
306 self._handle_redirects(request)
307
308 # Then check for normal errors.
309
310 request_info = self._run_custom_validators(request,
311 self.custom_validators.all_pre)
312
313 # If the resource owner denies the access request or if the request
314 # fails for reasons other than a missing or invalid redirection URI,
315 # the authorization server informs the client by adding the following
316 # parameters to the fragment component of the redirection URI using the
317 # "application/x-www-form-urlencoded" format, per Appendix B.
318 # https://tools.ietf.org/html/rfc6749#appendix-B
319
320 # Note that the correct parameters to be added are automatically
321 # populated through the use of specific exceptions
322
323 # REQUIRED.
324 if request.response_type is None:
325 raise errors.MissingResponseTypeError(request=request)
326 # Value MUST be one of our registered types: "token" by default or if using OIDC "id_token" or "id_token token"
327 elif not set(request.response_type.split()).issubset(self.response_types):
328 raise errors.UnsupportedResponseTypeError(request=request)
329
330 log.debug('Validating use of response_type token for client %r (%r).',
331 request.client_id, request.client)
332 if not self.request_validator.validate_response_type(request.client_id,
333 request.response_type,
334 request.client, request):
335
336 log.debug('Client %s is not authorized to use response_type %s.',
337 request.client_id, request.response_type)
338 raise errors.UnauthorizedClientError(request=request)
339
340 # OPTIONAL. The scope of the access request as described by Section 3.3
341 # https://tools.ietf.org/html/rfc6749#section-3.3
342 self.validate_scopes(request)
343
344 request_info.update({
345 'client_id': request.client_id,
346 'redirect_uri': request.redirect_uri,
347 'response_type': request.response_type,
348 'state': request.state,
349 'request': request,
350 })
351
352 request_info = self._run_custom_validators(
353 request,
354 self.custom_validators.all_post,
355 request_info
356 )
357
358 return request.scopes, request_info
359
360 def _run_custom_validators(self,
361 request,
362 validations,
363 request_info=None):
364 # Make a copy so we don't modify the existing request_info dict
365 request_info = {} if request_info is None else request_info.copy()
366 # For implicit grant, auth_validators and token_validators are
367 # basically equivalent since the token is returned from the
368 # authorization endpoint.
369 for validator in validations:
370 result = validator(request)
371 if result is not None:
372 request_info.update(result)
373 return request_info