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