1 # -*- coding: utf-8 -*-
3 oauthlib.oauth2.rfc6749.grant_types
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 from __future__ import unicode_literals, absolute_import
10 from oauthlib import common
11 from oauthlib.uri_validate import is_absolute_uri
13 from .base import GrantTypeBase
15 from ..request_validator import RequestValidator
17 log = logging.getLogger(__name__)
20 class ImplicitGrant(GrantTypeBase):
24 The implicit grant type is used to obtain access tokens (it does not
25 support the issuance of refresh tokens) and is optimized for public
26 clients known to operate a particular redirection URI. These clients
27 are typically implemented in a browser using a scripting language
30 Unlike the authorization code grant type, in which the client makes
31 separate requests for authorization and for an access token, the
32 client receives the access token as the result of the authorization
35 The implicit grant type does not include client authentication, and
36 relies on the presence of the resource owner and the registration of
37 the redirection URI. Because the access token is encoded into the
38 redirection URI, it may be exposed to the resource owner and other
39 applications residing on the same device::
49 +----|-----+ Client Identifier +---------------+
50 | -+----(A)-- & Redirection URI --->| |
51 | User- | | Authorization |
52 | Agent -|----(B)-- User authenticates -->| Server |
54 | |<---(C)--- Redirection URI ----<| |
55 | | with Access Token +---------------+
58 | |----(D)--- Redirection URI ---->| Web-Hosted |
59 | | without Fragment | Client |
61 | (F) |<---(E)------- Script ---------<| |
74 Note: The lines illustrating steps (A) and (B) are broken into two
75 parts as they pass through the user-agent.
77 Figure 4: Implicit Grant Flow
79 The flow illustrated in Figure 4 includes the following steps:
81 (A) The client initiates the flow by directing the resource owner's
82 user-agent to the authorization endpoint. The client includes
83 its client identifier, requested scope, local state, and a
84 redirection URI to which the authorization server will send the
85 user-agent back once access is granted (or denied).
87 (B) The authorization server authenticates the resource owner (via
88 the user-agent) and establishes whether the resource owner
89 grants or denies the client's access request.
91 (C) Assuming the resource owner grants access, the authorization
92 server redirects the user-agent back to the client using the
93 redirection URI provided earlier. The redirection URI includes
94 the access token in the URI fragment.
96 (D) The user-agent follows the redirection instructions by making a
97 request to the web-hosted client resource (which does not
98 include the fragment per [RFC2616]). The user-agent retains the
99 fragment information locally.
101 (E) The web-hosted client resource returns a web page (typically an
102 HTML document with an embedded script) capable of accessing the
103 full redirection URI including the fragment retained by the
104 user-agent, and extracting the access token (and other
105 parameters) contained in the fragment.
107 (F) The user-agent executes the script provided by the web-hosted
108 client resource locally, which extracts the access token.
110 (G) The user-agent passes the access token to the client.
112 See `Section 10.3`_ and `Section 10.16`_ for important security considerations
113 when using the implicit grant.
115 .. _`Implicit Grant`: http://tools.ietf.org/html/rfc6749#section-4.2
116 .. _`Section 10.3`: http://tools.ietf.org/html/rfc6749#section-10.3
117 .. _`Section 10.16`: http://tools.ietf.org/html/rfc6749#section-10.16
120 def __init__(self, request_validator=None):
121 self.request_validator = request_validator or RequestValidator()
123 def create_authorization_response(self, request, token_handler):
124 """Create an authorization response.
125 The client constructs the request URI by adding the following
126 parameters to the query component of the authorization endpoint URI
127 using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
130 REQUIRED. Value MUST be set to "token".
133 REQUIRED. The client identifier as described in `Section 2.2`_.
136 OPTIONAL. As described in `Section 3.1.2`_.
139 OPTIONAL. The scope of the access request as described by
143 RECOMMENDED. An opaque value used by the client to maintain
144 state between the request and callback. The authorization
145 server includes this value when redirecting the user-agent back
146 to the client. The parameter SHOULD be used for preventing
147 cross-site request forgery as described in `Section 10.12`_.
149 The authorization server validates the request to ensure that all
150 required parameters are present and valid. The authorization server
151 MUST verify that the redirection URI to which it will redirect the
152 access token matches a redirection URI registered by the client as
153 described in `Section 3.1.2`_.
155 .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
156 .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
157 .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
158 .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
159 .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
161 return self.create_token_response(request, token_handler)
163 def create_token_response(self, request, token_handler):
164 """Return token or error embedded in the URI fragment.
166 If the resource owner grants the access request, the authorization
167 server issues an access token and delivers it to the client by adding
168 the following parameters to the fragment component of the redirection
169 URI using the "application/x-www-form-urlencoded" format, per
173 REQUIRED. The access token issued by the authorization server.
176 REQUIRED. The type of the token issued as described in
177 `Section 7.1`_. Value is case insensitive.
180 RECOMMENDED. The lifetime in seconds of the access token. For
181 example, the value "3600" denotes that the access token will
182 expire in one hour from the time the response was generated.
183 If omitted, the authorization server SHOULD provide the
184 expiration time via other means or document the default value.
187 OPTIONAL, if identical to the scope requested by the client;
188 otherwise, REQUIRED. The scope of the access token as
189 described by `Section 3.3`_.
192 REQUIRED if the "state" parameter was present in the client
193 authorization request. The exact value received from the
196 The authorization server MUST NOT issue a refresh token.
198 .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
199 .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
200 .. _`Section 7.1`: http://tools.ietf.org/html/rfc6749#section-7.1
203 # request.scopes is only mandated in post auth and both pre and
204 # post auth use validate_authorization_request
205 if not request.scopes:
206 raise ValueError('Scopes must be set on post auth.')
208 self.validate_token_request(request)
210 # If the request fails due to a missing, invalid, or mismatching
211 # redirection URI, or if the client identifier is missing or invalid,
212 # the authorization server SHOULD inform the resource owner of the
213 # error and MUST NOT automatically redirect the user-agent to the
214 # invalid redirection URI.
215 except errors.FatalClientError as e:
216 log.debug('Fatal client error during validation of %r. %r.',
220 # If the resource owner denies the access request or if the request
221 # fails for reasons other than a missing or invalid redirection URI,
222 # the authorization server informs the client by adding the following
223 # parameters to the fragment component of the redirection URI using the
224 # "application/x-www-form-urlencoded" format, per Appendix B:
225 # http://tools.ietf.org/html/rfc6749#appendix-B
226 except errors.OAuth2Error as e:
227 log.debug('Client error during validation of %r. %r.', request, e)
228 return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples,
229 fragment=True)}, None, 302
231 token = token_handler.create_token(request, refresh_token=False)
232 return {'Location': common.add_params_to_uri(request.redirect_uri, token.items(),
233 fragment=True)}, None, 302
235 def validate_authorization_request(self, request):
236 return self.validate_token_request(request)
238 def validate_token_request(self, request):
239 """Check the token request for normal and fatal errors.
241 This method is very similar to validate_authorization_request in
242 the AuthorizationCodeGrant but differ in a few subtle areas.
244 A normal error could be a missing response_type parameter or the client
245 attempting to access scope it is not allowed to ask authorization for.
246 Normal errors can safely be included in the redirection URI and
247 sent back to the client.
249 Fatal errors occur when the client_id or redirect_uri is invalid or
250 missing. These must be caught by the provider and handled, how this
251 is done is outside of the scope of OAuthLib but showing an error
252 page describing the issue is a good idea.
255 # First check for fatal errors
257 # If the request fails due to a missing, invalid, or mismatching
258 # redirection URI, or if the client identifier is missing or invalid,
259 # the authorization server SHOULD inform the resource owner of the
260 # error and MUST NOT automatically redirect the user-agent to the
261 # invalid redirection URI.
263 # REQUIRED. The client identifier as described in Section 2.2.
264 # http://tools.ietf.org/html/rfc6749#section-2.2
265 if not request.client_id:
266 raise errors.MissingClientIdError(request=request)
268 if not self.request_validator.validate_client_id(request.client_id, request):
269 raise errors.InvalidClientIdError(request=request)
271 # OPTIONAL. As described in Section 3.1.2.
272 # http://tools.ietf.org/html/rfc6749#section-3.1.2
273 if request.redirect_uri is not None:
274 request.using_default_redirect_uri = False
275 log.debug('Using provided redirect_uri %s', request.redirect_uri)
276 if not is_absolute_uri(request.redirect_uri):
277 raise errors.InvalidRedirectURIError(request=request)
279 # The authorization server MUST verify that the redirection URI
280 # to which it will redirect the access token matches a
281 # redirection URI registered by the client as described in
283 # http://tools.ietf.org/html/rfc6749#section-3.1.2
284 if not self.request_validator.validate_redirect_uri(
285 request.client_id, request.redirect_uri, request):
286 raise errors.MismatchingRedirectURIError(request=request)
288 request.redirect_uri = self.request_validator.get_default_redirect_uri(
289 request.client_id, request)
290 request.using_default_redirect_uri = True
291 log.debug('Using default redirect_uri %s.', request.redirect_uri)
292 if not request.redirect_uri:
293 raise errors.MissingRedirectURIError(request=request)
294 if not is_absolute_uri(request.redirect_uri):
295 raise errors.InvalidRedirectURIError(request=request)
297 # Then check for normal errors.
299 # If the resource owner denies the access request or if the request
300 # fails for reasons other than a missing or invalid redirection URI,
301 # the authorization server informs the client by adding the following
302 # parameters to the fragment component of the redirection URI using the
303 # "application/x-www-form-urlencoded" format, per Appendix B.
304 # http://tools.ietf.org/html/rfc6749#appendix-B
306 # Note that the correct parameters to be added are automatically
307 # populated through the use of specific exceptions.
308 if request.response_type is None:
309 raise errors.InvalidRequestError(description='Missing response_type parameter.',
312 for param in ('client_id', 'response_type', 'redirect_uri', 'scope', 'state'):
313 if param in request.duplicate_params:
314 raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param, request=request)
316 # REQUIRED. Value MUST be set to "token".
317 if request.response_type != 'token':
318 raise errors.UnsupportedResponseTypeError(request=request)
320 log.debug('Validating use of response_type token for client %r (%r).',
321 request.client_id, request.client)
322 if not self.request_validator.validate_response_type(request.client_id,
323 request.response_type, request.client, request):
324 log.debug('Client %s is not authorized to use response_type %s.',
325 request.client_id, request.response_type)
326 raise errors.UnauthorizedClientError(request=request)
328 # OPTIONAL. The scope of the access request as described by Section 3.3
329 # http://tools.ietf.org/html/rfc6749#section-3.3
330 self.validate_scopes(request)
332 return request.scopes, {
333 'client_id': request.client_id,
334 'redirect_uri': request.redirect_uri,
335 'response_type': request.response_type,
336 'state': request.state,