1# -*- coding: utf-8 -*-
2"""
3oauthlib.oauth2.rfc6749.grant_types
4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5"""
6from __future__ import absolute_import, unicode_literals
7
8import logging
9
10log = logging.getLogger(__name__)
11
12
13class RequestValidator(object):
14
15    def client_authentication_required(self, request, *args, **kwargs):
16        """Determine if client authentication is required for current request.
17
18        According to the rfc6749, client authentication is required in the following cases:
19            - Resource Owner Password Credentials Grant, when Client type is Confidential or when
20              Client was issued client credentials or whenever Client provided client
21              authentication, see `Section 4.3.2`_.
22            - Authorization Code Grant, when Client type is Confidential or when Client was issued
23              client credentials or whenever Client provided client authentication,
24              see `Section 4.1.3`_.
25            - Refresh Token Grant, when Client type is Confidential or when Client was issued
26              client credentials or whenever Client provided client authentication, see
27              `Section 6`_
28
29        :param request: oauthlib.common.Request
30        :rtype: True or False
31
32        Method is used by:
33            - Authorization Code Grant
34            - Resource Owner Password Credentials Grant
35            - Refresh Token Grant
36
37        .. _`Section 4.3.2`: https://tools.ietf.org/html/rfc6749#section-4.3.2
38        .. _`Section 4.1.3`: https://tools.ietf.org/html/rfc6749#section-4.1.3
39        .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6
40        """
41        return True
42
43    def authenticate_client(self, request, *args, **kwargs):
44        """Authenticate client through means outside the OAuth 2 spec.
45
46        Means of authentication is negotiated beforehand and may for example
47        be `HTTP Basic Authentication Scheme`_ which utilizes the Authorization
48        header.
49
50        Headers may be accesses through request.headers and parameters found in
51        both body and query can be obtained by direct attribute access, i.e.
52        request.client_id for client_id in the URL query.
53
54        :param request: oauthlib.common.Request
55        :rtype: True or False
56
57        Method is used by:
58            - Authorization Code Grant
59            - Resource Owner Password Credentials Grant (may be disabled)
60            - Client Credentials Grant
61            - Refresh Token Grant
62
63        .. _`HTTP Basic Authentication Scheme`: https://tools.ietf.org/html/rfc1945#section-11.1
64        """
65        raise NotImplementedError('Subclasses must implement this method.')
66
67    def authenticate_client_id(self, client_id, request, *args, **kwargs):
68        """Ensure client_id belong to a non-confidential client.
69
70        A non-confidential client is one that is not required to authenticate
71        through other means, such as using HTTP Basic.
72
73        Note, while not strictly necessary it can often be very convenient
74        to set request.client to the client object associated with the
75        given client_id.
76
77        :param request: oauthlib.common.Request
78        :rtype: True or False
79
80        Method is used by:
81            - Authorization Code Grant
82        """
83        raise NotImplementedError('Subclasses must implement this method.')
84
85    def confirm_redirect_uri(self, client_id, code, redirect_uri, client, request,
86                             *args, **kwargs):
87        """Ensure that the authorization process represented by this authorization
88        code began with this 'redirect_uri'.
89
90        If the client specifies a redirect_uri when obtaining code then that
91        redirect URI must be bound to the code and verified equal in this
92        method, according to RFC 6749 section 4.1.3.  Do not compare against
93        the client's allowed redirect URIs, but against the URI used when the
94        code was saved.
95
96        :param client_id: Unicode client identifier
97        :param code: Unicode authorization_code.
98        :param redirect_uri: Unicode absolute URI
99        :param client: Client object set by you, see authenticate_client.
100        :param request: The HTTP Request (oauthlib.common.Request)
101        :rtype: True or False
102
103        Method is used by:
104            - Authorization Code Grant (during token request)
105        """
106        raise NotImplementedError('Subclasses must implement this method.')
107
108    def get_default_redirect_uri(self, client_id, request, *args, **kwargs):
109        """Get the default redirect URI for the client.
110
111        :param client_id: Unicode client identifier
112        :param request: The HTTP Request (oauthlib.common.Request)
113        :rtype: The default redirect URI for the client
114
115        Method is used by:
116            - Authorization Code Grant
117            - Implicit Grant
118        """
119        raise NotImplementedError('Subclasses must implement this method.')
120
121    def get_default_scopes(self, client_id, request, *args, **kwargs):
122        """Get the default scopes for the client.
123
124        :param client_id: Unicode client identifier
125        :param request: The HTTP Request (oauthlib.common.Request)
126        :rtype: List of default scopes
127
128        Method is used by all core grant types:
129            - Authorization Code Grant
130            - Implicit Grant
131            - Resource Owner Password Credentials Grant
132            - Client Credentials grant
133        """
134        raise NotImplementedError('Subclasses must implement this method.')
135
136    def get_original_scopes(self, refresh_token, request, *args, **kwargs):
137        """Get the list of scopes associated with the refresh token.
138
139        :param refresh_token: Unicode refresh token
140        :param request: The HTTP Request (oauthlib.common.Request)
141        :rtype: List of scopes.
142
143        Method is used by:
144            - Refresh token grant
145        """
146        raise NotImplementedError('Subclasses must implement this method.')
147
148    def is_within_original_scope(self, request_scopes, refresh_token, request, *args, **kwargs):
149        """Check if requested scopes are within a scope of the refresh token.
150
151        When access tokens are refreshed the scope of the new token
152        needs to be within the scope of the original token. This is
153        ensured by checking that all requested scopes strings are on
154        the list returned by the get_original_scopes. If this check
155        fails, is_within_original_scope is called. The method can be
156        used in situations where returning all valid scopes from the
157        get_original_scopes is not practical.
158
159        :param request_scopes: A list of scopes that were requested by client
160        :param refresh_token: Unicode refresh_token
161        :param request: The HTTP Request (oauthlib.common.Request)
162        :rtype: True or False
163
164        Method is used by:
165            - Refresh token grant
166        """
167        return False
168
169    def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs):
170        """Invalidate an authorization code after use.
171
172        :param client_id: Unicode client identifier
173        :param code: The authorization code grant (request.code).
174        :param request: The HTTP Request (oauthlib.common.Request)
175
176        Method is used by:
177            - Authorization Code Grant
178        """
179        raise NotImplementedError('Subclasses must implement this method.')
180
181    def revoke_token(self, token, token_type_hint, request, *args, **kwargs):
182        """Revoke an access or refresh token.
183
184        :param token: The token string.
185        :param token_type_hint: access_token or refresh_token.
186        :param request: The HTTP Request (oauthlib.common.Request)
187
188        Method is used by:
189            - Revocation Endpoint
190        """
191        raise NotImplementedError('Subclasses must implement this method.')
192
193    def rotate_refresh_token(self, request):
194        """Determine whether to rotate the refresh token. Default, yes.
195
196        When access tokens are refreshed the old refresh token can be kept
197        or replaced with a new one (rotated). Return True to rotate and
198        and False for keeping original.
199
200        :param request: oauthlib.common.Request
201        :rtype: True or False
202
203        Method is used by:
204            - Refresh Token Grant
205        """
206        return True
207
208    def save_authorization_code(self, client_id, code, request, *args, **kwargs):
209        """Persist the authorization_code.
210
211        The code should at minimum be stored with:
212            - the client_id (client_id)
213            - the redirect URI used (request.redirect_uri)
214            - a resource owner / user (request.user)
215            - the authorized scopes (request.scopes)
216            - the client state, if given (code.get('state'))
217
218        The 'code' argument is actually a dictionary, containing at least a
219        'code' key with the actual authorization code:
220
221            {'code': 'sdf345jsdf0934f'}
222
223        It may also have a 'state' key containing a nonce for the client, if it
224        chose to send one.  That value should be saved and used in
225        'validate_code'.
226
227        It may also have a 'claims' parameter which, when present, will be a dict
228        deserialized from JSON as described at
229        http://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
230        This value should be saved in this method and used again in 'validate_code'.
231
232        :param client_id: Unicode client identifier
233        :param code: A dict of the authorization code grant and, optionally, state.
234        :param request: The HTTP Request (oauthlib.common.Request)
235
236        Method is used by:
237            - Authorization Code Grant
238        """
239        raise NotImplementedError('Subclasses must implement this method.')
240
241    def save_token(self, token, request, *args, **kwargs):
242        """Persist the token with a token type specific method.
243
244        Currently, only save_bearer_token is supported.
245        """
246        return self.save_bearer_token(token, request, *args, **kwargs)
247
248    def save_bearer_token(self, token, request, *args, **kwargs):
249        """Persist the Bearer token.
250
251        The Bearer token should at minimum be associated with:
252            - a client and it's client_id, if available
253            - a resource owner / user (request.user)
254            - authorized scopes (request.scopes)
255            - an expiration time
256            - a refresh token, if issued
257            - a claims document, if present in request.claims
258
259        The Bearer token dict may hold a number of items::
260
261            {
262                'token_type': 'Bearer',
263                'access_token': 'askfjh234as9sd8',
264                'expires_in': 3600,
265                'scope': 'string of space separated authorized scopes',
266                'refresh_token': '23sdf876234',  # if issued
267                'state': 'given_by_client',  # if supplied by client
268            }
269
270        Note that while "scope" is a string-separated list of authorized scopes,
271        the original list is still available in request.scopes
272
273        Also note that if an Authorization Code grant request included a valid claims
274        parameter (for OpenID Connect) then the request.claims property will contain
275        the claims dict, which should be saved for later use when generating the
276        id_token and/or UserInfo response content.
277
278        :param client_id: Unicode client identifier
279        :param token: A Bearer token dict
280        :param request: The HTTP Request (oauthlib.common.Request)
281        :rtype: The default redirect URI for the client
282
283        Method is used by all core grant types issuing Bearer tokens:
284            - Authorization Code Grant
285            - Implicit Grant
286            - Resource Owner Password Credentials Grant (might not associate a client)
287            - Client Credentials grant
288        """
289        raise NotImplementedError('Subclasses must implement this method.')
290
291    def get_id_token(self, token, token_handler, request):
292        """
293        In the OpenID Connect workflows when an ID Token is requested this method is called.
294        Subclasses should implement the construction, signing and optional encryption of the
295        ID Token as described in the OpenID Connect spec.
296
297        In addition to the standard OAuth2 request properties, the request may also contain
298        these OIDC specific properties which are useful to this method:
299
300            - nonce, if workflow is implicit or hybrid and it was provided
301            - claims, if provided to the original Authorization Code request
302
303        The token parameter is a dict which may contain an ``access_token`` entry, in which
304        case the resulting ID Token *should* include a calculated ``at_hash`` claim.
305
306        Similarly, when the request parameter has a ``code`` property defined, the ID Token
307        *should* include a calculated ``c_hash`` claim.
308
309        http://openid.net/specs/openid-connect-core-1_0.html (sections `3.1.3.6`_, `3.2.2.10`_, `3.3.2.11`_)
310
311        .. _`3.1.3.6`: http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
312        .. _`3.2.2.10`: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDToken
313        .. _`3.3.2.11`: http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
314
315        :param token: A Bearer token dict
316        :param token_handler: the token handler (BearerToken class)
317        :param request: the HTTP Request (oauthlib.common.Request)
318        :return: The ID Token (a JWS signed JWT)
319        """
320        # the request.scope should be used by the get_id_token() method to determine which claims to include in the resulting id_token
321        raise NotImplementedError('Subclasses must implement this method.')
322
323    def validate_bearer_token(self, token, scopes, request):
324        """Ensure the Bearer token is valid and authorized access to scopes.
325
326        :param token: A string of random characters.
327        :param scopes: A list of scopes associated with the protected resource.
328        :param request: The HTTP Request (oauthlib.common.Request)
329
330        A key to OAuth 2 security and restricting impact of leaked tokens is
331        the short expiration time of tokens, *always ensure the token has not
332        expired!*.
333
334        Two different approaches to scope validation:
335
336            1) all(scopes). The token must be authorized access to all scopes
337                            associated with the resource. For example, the
338                            token has access to ``read-only`` and ``images``,
339                            thus the client can view images but not upload new.
340                            Allows for fine grained access control through
341                            combining various scopes.
342
343            2) any(scopes). The token must be authorized access to one of the
344                            scopes associated with the resource. For example,
345                            token has access to ``read-only-images``.
346                            Allows for fine grained, although arguably less
347                            convenient, access control.
348
349        A powerful way to use scopes would mimic UNIX ACLs and see a scope
350        as a group with certain privileges. For a restful API these might
351        map to HTTP verbs instead of read, write and execute.
352
353        Note, the request.user attribute can be set to the resource owner
354        associated with this token. Similarly the request.client and
355        request.scopes attribute can be set to associated client object
356        and authorized scopes. If you then use a decorator such as the
357        one provided for django these attributes will be made available
358        in all protected views as keyword arguments.
359
360        :param token: Unicode Bearer token
361        :param scopes: List of scopes (defined by you)
362        :param request: The HTTP Request (oauthlib.common.Request)
363        :rtype: True or False
364
365        Method is indirectly used by all core Bearer token issuing grant types:
366            - Authorization Code Grant
367            - Implicit Grant
368            - Resource Owner Password Credentials Grant
369            - Client Credentials Grant
370        """
371        raise NotImplementedError('Subclasses must implement this method.')
372
373    def validate_client_id(self, client_id, request, *args, **kwargs):
374        """Ensure client_id belong to a valid and active client.
375
376        Note, while not strictly necessary it can often be very convenient
377        to set request.client to the client object associated with the
378        given client_id.
379
380        :param request: oauthlib.common.Request
381        :rtype: True or False
382
383        Method is used by:
384            - Authorization Code Grant
385            - Implicit Grant
386        """
387        raise NotImplementedError('Subclasses must implement this method.')
388
389    def validate_code(self, client_id, code, client, request, *args, **kwargs):
390        """Verify that the authorization_code is valid and assigned to the given
391        client.
392
393        Before returning true, set the following based on the information stored
394        with the code in 'save_authorization_code':
395
396            - request.user
397            - request.state (if given)
398            - request.scopes
399            - request.claims (if given)
400        OBS! The request.user attribute should be set to the resource owner
401        associated with this authorization code. Similarly request.scopes
402        must also be set.
403
404        The request.claims property, if it was given, should assigned a dict.
405
406        :param client_id: Unicode client identifier
407        :param code: Unicode authorization code
408        :param client: Client object set by you, see authenticate_client.
409        :param request: The HTTP Request (oauthlib.common.Request)
410        :rtype: True or False
411
412        Method is used by:
413            - Authorization Code Grant
414        """
415        raise NotImplementedError('Subclasses must implement this method.')
416
417    def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs):
418        """Ensure client is authorized to use the grant_type requested.
419
420        :param client_id: Unicode client identifier
421        :param grant_type: Unicode grant type, i.e. authorization_code, password.
422        :param client: Client object set by you, see authenticate_client.
423        :param request: The HTTP Request (oauthlib.common.Request)
424        :rtype: True or False
425
426        Method is used by:
427            - Authorization Code Grant
428            - Resource Owner Password Credentials Grant
429            - Client Credentials Grant
430            - Refresh Token Grant
431        """
432        raise NotImplementedError('Subclasses must implement this method.')
433
434    def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):
435        """Ensure client is authorized to redirect to the redirect_uri requested.
436
437        All clients should register the absolute URIs of all URIs they intend
438        to redirect to. The registration is outside of the scope of oauthlib.
439
440        :param client_id: Unicode client identifier
441        :param redirect_uri: Unicode absolute URI
442        :param request: The HTTP Request (oauthlib.common.Request)
443        :rtype: True or False
444
445        Method is used by:
446            - Authorization Code Grant
447            - Implicit Grant
448        """
449        raise NotImplementedError('Subclasses must implement this method.')
450
451    def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs):
452        """Ensure the Bearer token is valid and authorized access to scopes.
453
454        OBS! The request.user attribute should be set to the resource owner
455        associated with this refresh token.
456
457        :param refresh_token: Unicode refresh token
458        :param client: Client object set by you, see authenticate_client.
459        :param request: The HTTP Request (oauthlib.common.Request)
460        :rtype: True or False
461
462        Method is used by:
463            - Authorization Code Grant (indirectly by issuing refresh tokens)
464            - Resource Owner Password Credentials Grant (also indirectly)
465            - Refresh Token Grant
466        """
467        raise NotImplementedError('Subclasses must implement this method.')
468
469    def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs):
470        """Ensure client is authorized to use the response_type requested.
471
472        :param client_id: Unicode client identifier
473        :param response_type: Unicode response type, i.e. code, token.
474        :param client: Client object set by you, see authenticate_client.
475        :param request: The HTTP Request (oauthlib.common.Request)
476        :rtype: True or False
477
478        Method is used by:
479            - Authorization Code Grant
480            - Implicit Grant
481        """
482        raise NotImplementedError('Subclasses must implement this method.')
483
484    def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
485        """Ensure the client is authorized access to requested scopes.
486
487        :param client_id: Unicode client identifier
488        :param scopes: List of scopes (defined by you)
489        :param client: Client object set by you, see authenticate_client.
490        :param request: The HTTP Request (oauthlib.common.Request)
491        :rtype: True or False
492
493        Method is used by all core grant types:
494            - Authorization Code Grant
495            - Implicit Grant
496            - Resource Owner Password Credentials Grant
497            - Client Credentials Grant
498        """
499        raise NotImplementedError('Subclasses must implement this method.')
500
501    def validate_silent_authorization(self, request):
502        """Ensure the logged in user has authorized silent OpenID authorization.
503
504        Silent OpenID authorization allows access tokens and id tokens to be
505        granted to clients without any user prompt or interaction.
506
507        :param request: The HTTP Request (oauthlib.common.Request)
508        :rtype: True or False
509
510        Method is used by:
511            - OpenIDConnectAuthCode
512            - OpenIDConnectImplicit
513            - OpenIDConnectHybrid
514        """
515        raise NotImplementedError('Subclasses must implement this method.')
516
517    def validate_silent_login(self, request):
518        """Ensure session user has authorized silent OpenID login.
519
520        If no user is logged in or has not authorized silent login, this
521        method should return False.
522
523        If the user is logged in but associated with multiple accounts and
524        not selected which one to link to the token then this method should
525        raise an oauthlib.oauth2.AccountSelectionRequired error.
526
527        :param request: The HTTP Request (oauthlib.common.Request)
528        :rtype: True or False
529
530        Method is used by:
531            - OpenIDConnectAuthCode
532            - OpenIDConnectImplicit
533            - OpenIDConnectHybrid
534        """
535        raise NotImplementedError('Subclasses must implement this method.')
536
537    def validate_user(self, username, password, client, request, *args, **kwargs):
538        """Ensure the username and password is valid.
539
540        OBS! The validation should also set the user attribute of the request
541        to a valid resource owner, i.e. request.user = username or similar. If
542        not set you will be unable to associate a token with a user in the
543        persistance method used (commonly, save_bearer_token).
544
545        :param username: Unicode username
546        :param password: Unicode password
547        :param client: Client object set by you, see authenticate_client.
548        :param request: The HTTP Request (oauthlib.common.Request)
549        :rtype: True or False
550
551        Method is used by:
552            - Resource Owner Password Credentials Grant
553        """
554        raise NotImplementedError('Subclasses must implement this method.')
555
556    def validate_user_match(self, id_token_hint, scopes, claims, request):
557        """Ensure client supplied user id hint matches session user.
558
559        If the sub claim or id_token_hint is supplied then the session
560        user must match the given ID.
561
562        :param id_token_hint: User identifier string.
563        :param scopes: List of OAuth 2 scopes and OpenID claims (strings).
564        :param claims: OpenID Connect claims dict.
565        :param request: The HTTP Request (oauthlib.common.Request)
566        :rtype: True or False
567
568        Method is used by:
569            - OpenIDConnectAuthCode
570            - OpenIDConnectImplicit
571            - OpenIDConnectHybrid
572        """
573        raise NotImplementedError('Subclasses must implement this method.')
574