auth0_jwt_validator.jwt_verifier
View Source
0# Built-in packages 1import time 2import typing 3import urllib.request 4 5# Third-party packages 6from authlib.jose import JsonWebToken 7from authlib.jose.errors import InvalidClaimError, MissingClaimError 8 9# Local packages 10 11jwt = JsonWebToken(["RS256"]) 12 13 14class JwtVerifier: 15 """ 16 Attributes 17 ---------- 18 jwks_uri : str 19 The JSON Web Key Set URI where the public keys are located. 20 issuer : str 21 The client that issued the JWT. 22 audience : str 23 The recipient that the JWT is intended for. 24 25 Methods 26 ------- 27 get_token_type() 28 Return the token type used by the verify methods. This method raise an 29 error because this method should be implemented in the sub-class. 30 31 verify(auth0_token) 32 Return the payload from an access token or ID token 33 34 get_auth0_signing_keys(jwks_uri) 35 Return the signing keys from a given JSON Web Key Set URI. 36 37 verify_payload(*args, **kwargs) 38 This method raise an error because this method should be implemented in 39 the sub-class. 40 41 Examples 42 -------- 43 Custom Token Verifier 44 45 ```python 46 class CustomTokenVerifier(JwtVerifier): 47 def get_token_type(): 48 return "custom token" 49 50 def verify(self, auth0_token: str) -> dict: 51 payload: dict = super().verify(auth0_token) 52 return payload 53 54 def verify_payload(self, payload: dict) -> None: 55 # validate just the issuer 56 self.verify_issuer(payload) 57 ``` 58 """ 59 60 def __init__( 61 self, 62 jwks_uri: str, 63 issuer: str, 64 audience: str, 65 /, 66 leeway: int = 0, 67 ) -> None: 68 """ 69 Parameters 70 ---------- 71 jwks_uri : str 72 The JSON Web Key Set URI where the public keys are located. 73 issuer : str 74 The client that issued the JWT. 75 audience : str 76 The recipient that the JWT is intended for. 77 """ 78 79 self.jwks_uri = jwks_uri 80 self.issuer = issuer 81 self.audience = audience 82 self.leeway = leeway 83 84 def get_token_type(self) -> None: 85 """ 86 Raises 87 ------ 88 NotImplementedError 89 Raise an error because this method should be implemented in a 90 sub-class. 91 """ 92 93 raise NotImplementedError("get_token_type method should be implementend") 94 95 def get_auth0_signing_keys(self, jwks_uri: str) -> str: # pragma: no cover 96 """ 97 98 Parameters 99 ---------- 100 jwks_uri : str 101 The JSON Web Key Set URI where the public keys are located. 102 103 Returns 104 ------- 105 auth0_signing_keys : str 106 Return the auth0 public keys as JSON. 107 """ 108 109 with urllib.request.urlopen(jwks_uri) as r: 110 return r.read().decode("utf-8") 111 112 def verify(self, auth0_token: str) -> dict: 113 claims = jwt.decode(auth0_token, self.get_auth0_signing_keys(self.jwks_uri)) 114 return claims.copy() 115 116 def verify_payload(self, *args, **kwargs) -> None: 117 """ 118 Raises 119 ------ 120 NotImplementedError 121 Raise an error because this method should be implemented in a 122 sub-class. 123 """ 124 125 raise NotImplementedError("verify_payload method should be implementend") 126 127 def verify_issuer(self, payload: typing.TypedDict) -> None: 128 """ 129 Verify the issuer 130 131 Parameters 132 ---------- 133 payload : dict 134 The payload which contains the claims. Claims are statements about 135 an entity (typically, the user) and additional data. 136 137 Returns 138 ------- 139 None 140 141 Raises 142 ------ 143 MissingClaimError 144 If some field in the claim (payload) is missing this error is 145 raised. 146 InvalidClaimError 147 If some field in the claim (payload) doesn't match what's expected 148 this error is raised. 149 """ 150 151 if "iss" not in payload or not isinstance(payload["iss"], str): 152 raise MissingClaimError( 153 f"Issuer (iss) claim must be a string present in the {self.get_token_type()}" 154 ) 155 if payload["iss"] != self.issuer: 156 raise InvalidClaimError( 157 f"Issuer (iss) claim mismatch in the {self.get_token_type()}; expected" 158 f' {self.issuer}, found {payload["iss"]}' 159 ) 160 161 def verify_subject(self, payload: typing.TypedDict) -> None: 162 """ 163 Verify the subject 164 165 Parameters 166 ---------- 167 payload : dict 168 The payload which contains the claims. Claims are statements about 169 an entity (typically, the user) and additional data. 170 171 Returns 172 ------- 173 None 174 175 Raises 176 ------ 177 MissingClaimError 178 If some field in the claim (payload) is missing this error is 179 raised. 180 InvalidClaimError 181 If some field in the claim (payload) doesn't match what's expected 182 this error is raised. 183 """ 184 185 if "sub" not in payload or not isinstance(payload["sub"], str): 186 raise MissingClaimError( 187 f"Subject (sub) claim must be a string present in the {self.get_token_type()}" 188 ) 189 190 def verify_audience(self, payload: typing.TypedDict) -> None: 191 """ 192 Verify the audience 193 194 Parameters 195 ---------- 196 payload : dict 197 The payload which contains the claims. Claims are statements about 198 an entity (typically, the user) and additional data. 199 200 Returns 201 ------- 202 None 203 204 Raises 205 ------ 206 MissingClaimError 207 If some field in the claim (payload) is missing this error is 208 raised. 209 InvalidClaimError 210 If some field in the claim (payload) doesn't match what's expected 211 this error is raised. 212 """ 213 214 if "aud" not in payload or not isinstance(payload["aud"], (str, list)): 215 raise MissingClaimError( 216 "Audience (aud) claim must be a string or array of strings present in the " 217 f"{self.get_token_type()}" 218 ) 219 220 if isinstance(payload["aud"], list) and self.audience not in payload["aud"]: 221 raise InvalidClaimError( 222 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected " 223 f"{self.audience} but was not one of {', '.join(payload['aud'])}" 224 ) 225 226 elif isinstance(payload["aud"], str) and payload["aud"] != self.audience: 227 raise InvalidClaimError( 228 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected" 229 f' {self.audience} but found {payload["aud"]}' 230 ) 231 232 def verify_expiration_time(self, payload: typing.TypedDict) -> None: 233 """ 234 Verify the expiration time 235 236 Parameters 237 ---------- 238 payload : dict 239 The payload which contains the claims. Claims are statements about 240 an entity (typically, the user) and additional data. 241 242 Returns 243 ------- 244 None 245 246 Raises 247 ------ 248 MissingClaimError 249 If some field in the claim (payload) is missing this error is 250 raised. 251 InvalidClaimError 252 If some field in the claim (payload) doesn't match what's expected 253 this error is raised. 254 """ 255 256 now = time.time() 257 leeway = self.leeway 258 259 # Expires at 260 if "exp" not in payload or not isinstance(payload["exp"], int): 261 raise MissingClaimError( 262 "Expiration Time (exp) claim must be a number present in the" 263 f" {self.get_token_type()}" 264 ) 265 266 exp_time = payload["exp"] + leeway 267 if now > exp_time: 268 raise InvalidClaimError( 269 f"Expiration Time (exp) claim error in the {self.get_token_type()}; " 270 f"current time ({now}) is after expiration time ({exp_time})" 271 ) 272 273 def verify_issued_at_time(self, payload: typing.TypedDict) -> None: 274 """ 275 Verify the issued at time 276 277 Parameters 278 ---------- 279 payload : dict 280 The payload which contains the claims. Claims are statements about 281 an entity (typically, the user) and additional data. 282 283 Returns 284 ------- 285 None 286 287 Raises 288 ------ 289 MissingClaimError 290 If some field in the claim (payload) is missing this error is 291 raised. 292 InvalidClaimError 293 If some field in the claim (payload) doesn't match what's expected 294 this error is raised. 295 """ 296 297 if "iat" not in payload or not isinstance(payload["iat"], int): 298 raise MissingClaimError( 299 f"Issued At (iat) claim must be a number present in the {self.get_token_type()}" 300 ) 301 302 def verify_azp(self, payload: typing.TypedDict) -> None: 303 """ 304 Verify the authorized party 305 306 Parameters 307 ---------- 308 payload : dict 309 The payload which contains the claims. Claims are statements about 310 an entity (typically, the user) and additional data. 311 312 Returns 313 ------- 314 None 315 316 Raises 317 ------ 318 MissingClaimError 319 If some field in the claim (payload) is missing this error is 320 raised. 321 InvalidClaimError 322 If some field in the claim (payload) doesn't match what's expected 323 this error is raised. 324 """ 325 326 if isinstance(payload["aud"], list) and len(payload["aud"]) > 1: 327 if "azp" not in payload or not isinstance(payload["azp"], str): 328 raise MissingClaimError( 329 "Authorized Party (azp) claim must be a string present in the " 330 f"{self.get_token_type()} when Audience (aud) claim has multiple values" 331 ) 332 333 if payload["azp"] != self.audience: 334 raise InvalidClaimError( 335 f"Authorized Party (azp) claim mismatch in the {self.get_token_type()};" 336 f' expected {self.audience}, found {payload["azp"]}' 337 ) 338 339 def verify_org_id(self, payload: typing.TypedDict, organization: str) -> None: 340 """ 341 Verify the organization id 342 343 Parameters 344 ---------- 345 payload : dict 346 The payload which contains the claims. Claims are statements about 347 an entity (typically, the user) and additional data. 348 organization : str 349 The organization that issued the token. 350 351 Returns 352 ------- 353 None 354 355 Raises 356 ------ 357 MissingClaimError 358 If some field in the claim (payload) is missing this error is 359 raised. 360 InvalidClaimError 361 If some field in the claim (payload) doesn't match what's expected 362 this error is raised. 363 """ 364 365 if "org_id" not in payload or not isinstance(payload["org_id"], str): 366 raise MissingClaimError( 367 "Organization (org_id) claim must be a string present in the" 368 f" {self.get_token_type()}" 369 ) 370 371 if payload["org_id"] != organization: 372 raise InvalidClaimError( 373 f"Organization (org_id) claim mismatch in the {self.get_token_type()}; expected " 374 f'{organization}, found {payload["org_id"]}' 375 ) 376 377 def verify_nonce(self, payload: typing.TypedDict, nonce: str): 378 """ 379 Verify the nonce 380 381 Parameters 382 ---------- 383 payload : dict 384 The payload which contains the claims. Claims are statements about 385 an entity (typically, the user) and additional data. 386 nonce : str 387 The valued associated to the Client session and the ID token. 388 389 Returns 390 ------- 391 None 392 393 Raises 394 ------ 395 MissingClaimError 396 If some field in the claim (payload) is missing this error is 397 raised. 398 InvalidClaimError 399 If some field in the claim (payload) doesn't match what's expected 400 this error is raised. 401 """ 402 403 if "nonce" not in payload or not isinstance(payload["nonce"], str): 404 raise MissingClaimError( 405 f"Nonce (nonce) claim must be a string present in the {self.get_token_type()}" 406 ) 407 408 if payload["nonce"] != nonce: 409 raise InvalidClaimError( 410 f"Nonce (nonce) claim mismatch in the {self.get_token_type()}; expected {nonce}, " 411 f'found {payload["nonce"]}' 412 ) 413 414 def verify_auth_time(self, payload: typing.TypedDict, max_age: int): 415 """ 416 Verify the authentication time 417 418 Parameters 419 ---------- 420 payload : dict 421 The payload which contains the claims. Claims are statements about 422 an entity (typically, the user) and additional data. 423 max_age : int 424 The max_age help us to customize when a token should expire. 425 426 Returns 427 ------- 428 None 429 430 Raises 431 ------ 432 MissingClaimError 433 If some field in the claim (payload) is missing this error is 434 raised. 435 InvalidClaimError 436 If some field in the claim (payload) doesn't match what's expected 437 this error is raised. 438 """ 439 440 now = time.time() 441 leeway = self.leeway 442 443 if "auth_time" not in payload or not isinstance(payload["auth_time"], int): 444 raise MissingClaimError( 445 "Authentication Time (auth_time) claim must be a number present in the" 446 f" {self.get_token_type()} when Max Age (max_age) is specified" 447 ) 448 449 auth_valid_until = payload["auth_time"] + max_age + leeway 450 if now > auth_valid_until: 451 raise InvalidClaimError( 452 f"Authentication Time (auth_time) claim in the {self.get_token_type()} indicates" 453 " that too much time has passed since the last end-user authentication. Current" 454 f" time ({now}) is after last auth at ({auth_valid_until})" 455 ) 456 457 458class IdTokenPayload(typing.TypedDict): 459 """ 460 Attributes 461 ---------- 462 iss : str 463 The "iss" (issuer) claim identifies the principal that issued the JWT. 464 `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1>`_. 465 sub : str 466 The "sub" (subject) claim identifies the principal that is the subject 467 of the JWT. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2>`_. 468 aud : str, list of str 469 The "aud" (audience) claim identifies the recipients that the JWT is 470 intended for. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. 471 exp : int 472 The "exp" (expiration time) claim identifies the expiration time on or 473 after which the JWT MUST NOT be accepted for processing. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_. 474 iat : int 475 The "iat" (issued at) claim identifies the time at which the JWT was 476 issued. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6>`_. 477 name : str 478 The "name" claim identifies the full name. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 479 given_name : str 480 The "given_name" claim identifies the given name(s) or first name(s). `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 481 family_name : str 482 The "family_name" claim identifies the surname(s) or last name(s). `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 483 gender : str, {'male', 'female'} 484 The "gender" claim identifies the gender. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 485 birthdate : str 486 The "birthdate" claim identifies the birthday. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 487 email : str 488 The "email" claim identifies the preferred e-mail address. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 489 picture : str 490 The "picture" claim identifies the profile picture URL. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 491 nonce : str, optional 492 The "nonce" claim identifies the valued used to associate a Client 493 session with an ID Token, and to mitigate replay attacks. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 494 auth_time : int, optional 495 The "auth_time" (authentication time) claim identifies the time when the 496 End-User authentication occurred. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 497 """ 498 499 iss: str 500 sub: str 501 aud: typing.Union[str, typing.List[str]] 502 exp: int 503 iat: int 504 name: str 505 given_name: str 506 family_name: str 507 gender: typing.Literal["male", "female"] 508 birthdate: str 509 email: str 510 picture: str 511 nonce: typing.Optional[str] 512 auth_time: typing.Optional[int] 513 514 515class IdTokenVerifier(JwtVerifier): 516 """ 517 Attributes 518 ---------- 519 jwks_uri : str 520 The JSON Web Key Set URI where the public keys are located. 521 issuer : str 522 The client that issued the JWT. 523 audience : str 524 The recipient that the JWT is intended for. 525 526 Methods 527 ------- 528 get_token_type() 529 Return the token type used by the verify methods. 530 531 get_auth0_signing_keys(jwks_uri) 532 Return the public keys located under the given JSON Web Key Set URI. 533 534 verify(auth_token[, nonce, max_age, organization]) 535 Return the payload or raise an error when on of the verifications fails. 536 537 verify_payload(payload[, nonce, max_age, organization]) 538 Raise an error when one of the verification fails. 539 540 Examples 541 -------- 542 Validating ID Token 543 544 >>> from auth0_jwt_validator import IdTokenVerifier 545 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 546 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 547 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 548 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 549 >>> token_verifier.verify("some-id-token") 550 551 Validating ID Token and nonce 552 553 >>> from auth0_jwt_validator import IdTokenVerifier 554 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 555 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 556 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 557 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 558 >>> token_verifier.verify("some-id-token", nonce="some-random-string") 559 560 Validating ID Token and max_age 561 562 >>> from auth0_jwt_validator import IdTokenVerifier 563 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 564 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 565 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 566 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 567 >>> token_verifier.verify("some-id-token", max_age=60 * 60 * 24) 568 569 Validating ID Token and organization 570 571 >>> from auth0_jwt_validator import IdTokenVerifier 572 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 573 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 574 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 575 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 576 >>> token_verifier.verify("some-id-token", organization="some-organization") 577 """ 578 579 def get_token_type(self) -> str: 580 """ 581 Returns 582 ------- 583 token_type : str 584 Return the token type (ID token or access token) used by the verify 585 methods. 586 """ 587 588 return "ID token" 589 590 def verify( 591 self, 592 auth0_token: str, 593 /, 594 nonce: typing.Optional[str] = None, 595 max_age: typing.Optional[int] = None, 596 organization: typing.Optional[str] = None, 597 ) -> IdTokenPayload: 598 """ 599 Parameters 600 ---------- 601 auth0_token : str 602 The ID token. 603 604 Other Parameters 605 ---------------- 606 nonce : str, default None 607 Lorem. 608 max_age : int, default None 609 Lorem. 610 organization : str, default None 611 The organization that issued the token. 612 613 Returns 614 ------- 615 payload: `IdTokenPayload` 616 The payload which contains the claims. Claims are statements about 617 an entity (typically, the user) and additional data. 618 619 Raises 620 ------ 621 MissingClaimError 622 If some field in the claim (payload) is missing this error is 623 raised. 624 InvalidClaimError 625 If some field in the claim (payload) doesn't match what's expected 626 this error is raised. 627 628 See Also 629 -------- 630 IdTokenPayload 631 """ 632 633 payload: IdTokenPayload = super().verify(auth0_token) # type: ignore 634 self.verify_payload(payload, nonce=nonce, max_age=max_age, organization=organization) 635 636 return payload 637 638 def verify_payload( 639 self, 640 payload: IdTokenPayload, 641 /, 642 nonce: typing.Optional[str] = None, 643 max_age: typing.Optional[int] = None, 644 organization: typing.Optional[str] = None, 645 ) -> None: 646 """ 647 Parameters 648 ---------- 649 payload : `IdTokenPayload` 650 The payload which contains the claims. Claims are statements about 651 an entity (typically, the user) and additional data. 652 653 Other Parameters 654 ---------------- 655 nonce : str, default None 656 Lorem. 657 max_age : int, default None 658 Lorem. 659 organization : str, default None 660 The organization that issued the token. 661 662 Returns 663 ------- 664 None 665 666 Raises 667 ------ 668 MissingClaimError 669 If some field in the claim (payload) is missing this error is 670 raised. 671 InvalidClaimError 672 If some field in the claim (payload) doesn't match what's expected 673 this error is raised. 674 675 See Also 676 -------- 677 IdTokenPayload 678 """ 679 680 # Issuer 681 self.verify_issuer(payload) 682 683 # Subject 684 self.verify_subject(payload) 685 686 # Audience 687 self.verify_audience(payload) 688 689 # Expires at 690 self.verify_expiration_time(payload) 691 692 # Issued at 693 self.verify_issued_at_time(payload) 694 695 # Nonce 696 if nonce: 697 self.verify_nonce(payload, nonce) 698 699 # Organization 700 if organization: 701 self.verify_org_id(payload, organization) 702 703 # Authorized party 704 self.verify_azp(payload) 705 706 # Authentication time 707 if max_age: 708 self.verify_auth_time(payload, max_age) 709 710 711class AccessTokenPayload(typing.TypedDict): 712 """ 713 Attributes 714 ---------- 715 iss : str 716 The "iss" (issuer) claim identifies the principal that issued the JWT. 717 `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1>`_. 718 sub : str 719 The "sub" (subject) claim identifies the principal that is the subject 720 of the JWT. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2>`_. 721 aud : str, list of str 722 The "aud" (audience) claim identifies the recipients that the JWT is 723 intended for. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. 724 azp : str 725 The "azp" (authorized party) claim identifies the party to which the ID 726 Token was issued. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 727 exp : int 728 The "exp" (expiration time) claim identifies the expiration time on or 729 after which the JWT MUST NOT be accepted for processing. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_. 730 iat : int 731 The "iat" (issued at) claim identifies the time at which the JWT was 732 issued. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6>`_. 733 scope : str 734 The "scope" claim identifies the space-separated list of scopes 735 associated with the token. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 736 gty : str 737 The "gty" (grant type) claim identifies the grant type used to create 738 the token. 739 permissions : list of str, optional 740 The "permissions" claim identifies the list of permissions associated to 741 the current user. 742 org_id : str, optional 743 The "org_id" (organization id) claim identifies the organization under 744 which the JWT was issued. 745 """ 746 747 iss: str 748 sub: str 749 aud: typing.Union[str, typing.List[str]] 750 azp: str 751 exp: int 752 iat: int 753 scope: str 754 gty: str 755 permissions: typing.Optional[typing.List[str]] 756 org_id: typing.Optional[str] 757 758 759class AccessTokenVerifier(JwtVerifier): 760 """ 761 Attributes 762 ---------- 763 jwks_uri : str 764 The JSON Web Key Set URI where the public keys are located. 765 issuer : str 766 The client that issued the JWT. 767 audience : str 768 The recipient that the JWT is intended for. 769 770 Methods 771 ------- 772 get_token_type() 773 Return the token type used by the verify methods. 774 775 get_auth0_signing_keys(jwks_uri) 776 Return the public keys located under the given JSON Web Key Set URI. 777 778 verify(auth_token[,organization, required_scopes, required_permissions]) 779 Return the payload or raise an error. 780 781 verify_payload(payload[,organization, required_scopes, required_permissions]) 782 Raise an error when one of the verification fails. 783 784 Examples 785 -------- 786 Validating Access Token 787 788 >>> from auth0_jwt_validator import AccessTokenVerifier 789 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 790 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 791 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 792 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 793 >>> token_verifier.verify("some-access-token") 794 795 Validating Access Token and organization 796 797 >>> from auth0_jwt_validator import AccessTokenVerifier 798 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 799 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 800 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 801 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 802 >>> token_verifier.verify("some-access-token", organization="some-organization") 803 804 Validating Access Token and scopes 805 806 >>> from auth0_jwt_validator import AccessTokenVerifier 807 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 808 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 809 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 810 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 811 >>> token_verifier.verify("some-access-token", required_scopes=["profile", "calendar"]) 812 813 Validating Access Token and permissions 814 815 >>> from auth0_jwt_validator import AccessTokenVerifier 816 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 817 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 818 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 819 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 820 >>> token_verifier.verify("some-access-token", required_permissions=["read:user", "delete:user"]) 821 """ 822 823 def get_token_type(self) -> str: 824 """ 825 Returns 826 ------- 827 token_type : str 828 Return the token type (ID token or access token) used by the verify 829 methods. 830 """ 831 832 return "access token" 833 834 def verify( 835 self, 836 auth0_token: str, 837 /, 838 organization: typing.Optional[str] = None, 839 required_scopes: typing.Optional[typing.List[str]] = None, 840 required_permissions: typing.Optional[typing.List[str]] = None, 841 ) -> AccessTokenPayload: 842 """ 843 Parameters 844 ----------- 845 auth_token : str 846 The access token. 847 848 Other Parameters 849 ---------------- 850 organization : str, default None 851 The organization that issued the token. 852 required_scopes : list of str, default None 853 The list of scopes which are required to be in the payload. 854 required_permissions : list of str, default None 855 The list of permissions which are required to be in the payload. 856 857 Returns 858 ------- 859 payload : `AccessTokenPayload` 860 The payload which contains the claims. Claims are statements about 861 an entity (typically, the user) and additional data. 862 863 Raises 864 ------- 865 MissingClaimError 866 If some field in the claim (payload) is missing this error is raised. 867 InvalidClaimError 868 If some field in the claim (payload) doesn't match what's expected 869 this error is raised. 870 871 See Also 872 -------- 873 AccessTokenPayload 874 """ 875 876 payload: AccessTokenPayload = super().verify(auth0_token) # type: ignore 877 self.verify_payload( 878 payload, 879 organization=organization, 880 required_scopes=required_scopes, 881 required_permissions=required_permissions, 882 ) 883 884 return payload 885 886 def verify_payload( 887 self, 888 payload: AccessTokenPayload, 889 /, 890 organization: typing.Optional[str] = None, 891 required_scopes: typing.Optional[typing.List[str]] = None, 892 required_permissions: typing.Optional[typing.List[str]] = None, 893 ) -> None: 894 """ 895 Parameters 896 ---------- 897 payload : `AccessTokenPayload` 898 The payload which contains the claims. Claims are statements about 899 an entity (typically, the user) and additional data. 900 901 Other Parameters 902 ---------------- 903 organization : str, default None 904 The organization that issued the token. 905 required_scopes : list of str, default None 906 The list of scopes which are required to be in the payload. 907 required_permissions : list of str, default None 908 The list of permissions which are required to be in the payload. 909 910 Returns 911 ------- 912 None 913 914 Raises 915 ------ 916 MissingClaimError 917 If some field in the claim (payload) is missing this error is raised. 918 InvalidClaimError 919 If some field in the claim (payload) doesn't match what's expected 920 this error is raised. 921 922 See Also 923 -------- 924 AccessTokenPayload 925 """ 926 927 # Issuer 928 self.verify_issuer(payload) 929 930 # Subject 931 self.verify_subject(payload) 932 933 # Audience 934 self.verify_audience(payload) 935 936 # Expires at 937 self.verify_expiration_time(payload) 938 939 # Issued at 940 self.verify_issued_at_time(payload) 941 942 # Organization 943 if organization: 944 self.verify_org_id(payload, organization) 945 946 if required_scopes: 947 self._verify_scopes(payload, required_scopes) 948 949 if required_permissions: 950 self._verify_permissions(payload, required_permissions) 951 952 def _verify_scopes( 953 self, payload: AccessTokenPayload, /, required_scopes: typing.List[str] 954 ) -> None: 955 if "scope" not in payload or not isinstance(payload["scope"], str): 956 raise MissingClaimError( 957 f"Scope (scope) claim must be a string present in the {self.get_token_type()}" 958 ) 959 960 scopes = payload["scope"].split() 961 has_enough_scopes = set(scopes).issuperset(required_scopes) 962 963 if not has_enough_scopes: 964 raise InvalidClaimError( 965 f"Scope (scope) claim mismatch in the {self.get_token_type()}; expected at least " 966 f"these {', '.join(required_scopes)} but was found these {', '.join(scopes)}" 967 ) 968 969 def _verify_permissions( 970 self, payload: AccessTokenPayload, /, required_permissions: typing.List[str] 971 ) -> None: 972 if "permissions" not in payload or not isinstance(payload["permissions"], list): 973 raise MissingClaimError( 974 "Permissions (permissions) claim must be an array of strings present in the " 975 f"{self.get_token_type()}" 976 ) 977 978 permissions = payload["permissions"] 979 has_enough_scopes = set(permissions).issuperset(required_permissions) 980 981 if not has_enough_scopes: 982 raise InvalidClaimError( 983 f"Permissions (permissions) claim mismatch in the {self.get_token_type()}; " 984 f"expected at least these {', '.join(required_permissions)} but was found these " 985 f"{', '.join(permissions)}" 986 )
View Source
15class JwtVerifier: 16 """ 17 Attributes 18 ---------- 19 jwks_uri : str 20 The JSON Web Key Set URI where the public keys are located. 21 issuer : str 22 The client that issued the JWT. 23 audience : str 24 The recipient that the JWT is intended for. 25 26 Methods 27 ------- 28 get_token_type() 29 Return the token type used by the verify methods. This method raise an 30 error because this method should be implemented in the sub-class. 31 32 verify(auth0_token) 33 Return the payload from an access token or ID token 34 35 get_auth0_signing_keys(jwks_uri) 36 Return the signing keys from a given JSON Web Key Set URI. 37 38 verify_payload(*args, **kwargs) 39 This method raise an error because this method should be implemented in 40 the sub-class. 41 42 Examples 43 -------- 44 Custom Token Verifier 45 46 ```python 47 class CustomTokenVerifier(JwtVerifier): 48 def get_token_type(): 49 return "custom token" 50 51 def verify(self, auth0_token: str) -> dict: 52 payload: dict = super().verify(auth0_token) 53 return payload 54 55 def verify_payload(self, payload: dict) -> None: 56 # validate just the issuer 57 self.verify_issuer(payload) 58 ``` 59 """ 60 61 def __init__( 62 self, 63 jwks_uri: str, 64 issuer: str, 65 audience: str, 66 /, 67 leeway: int = 0, 68 ) -> None: 69 """ 70 Parameters 71 ---------- 72 jwks_uri : str 73 The JSON Web Key Set URI where the public keys are located. 74 issuer : str 75 The client that issued the JWT. 76 audience : str 77 The recipient that the JWT is intended for. 78 """ 79 80 self.jwks_uri = jwks_uri 81 self.issuer = issuer 82 self.audience = audience 83 self.leeway = leeway 84 85 def get_token_type(self) -> None: 86 """ 87 Raises 88 ------ 89 NotImplementedError 90 Raise an error because this method should be implemented in a 91 sub-class. 92 """ 93 94 raise NotImplementedError("get_token_type method should be implementend") 95 96 def get_auth0_signing_keys(self, jwks_uri: str) -> str: # pragma: no cover 97 """ 98 99 Parameters 100 ---------- 101 jwks_uri : str 102 The JSON Web Key Set URI where the public keys are located. 103 104 Returns 105 ------- 106 auth0_signing_keys : str 107 Return the auth0 public keys as JSON. 108 """ 109 110 with urllib.request.urlopen(jwks_uri) as r: 111 return r.read().decode("utf-8") 112 113 def verify(self, auth0_token: str) -> dict: 114 claims = jwt.decode(auth0_token, self.get_auth0_signing_keys(self.jwks_uri)) 115 return claims.copy() 116 117 def verify_payload(self, *args, **kwargs) -> None: 118 """ 119 Raises 120 ------ 121 NotImplementedError 122 Raise an error because this method should be implemented in a 123 sub-class. 124 """ 125 126 raise NotImplementedError("verify_payload method should be implementend") 127 128 def verify_issuer(self, payload: typing.TypedDict) -> None: 129 """ 130 Verify the issuer 131 132 Parameters 133 ---------- 134 payload : dict 135 The payload which contains the claims. Claims are statements about 136 an entity (typically, the user) and additional data. 137 138 Returns 139 ------- 140 None 141 142 Raises 143 ------ 144 MissingClaimError 145 If some field in the claim (payload) is missing this error is 146 raised. 147 InvalidClaimError 148 If some field in the claim (payload) doesn't match what's expected 149 this error is raised. 150 """ 151 152 if "iss" not in payload or not isinstance(payload["iss"], str): 153 raise MissingClaimError( 154 f"Issuer (iss) claim must be a string present in the {self.get_token_type()}" 155 ) 156 if payload["iss"] != self.issuer: 157 raise InvalidClaimError( 158 f"Issuer (iss) claim mismatch in the {self.get_token_type()}; expected" 159 f' {self.issuer}, found {payload["iss"]}' 160 ) 161 162 def verify_subject(self, payload: typing.TypedDict) -> None: 163 """ 164 Verify the subject 165 166 Parameters 167 ---------- 168 payload : dict 169 The payload which contains the claims. Claims are statements about 170 an entity (typically, the user) and additional data. 171 172 Returns 173 ------- 174 None 175 176 Raises 177 ------ 178 MissingClaimError 179 If some field in the claim (payload) is missing this error is 180 raised. 181 InvalidClaimError 182 If some field in the claim (payload) doesn't match what's expected 183 this error is raised. 184 """ 185 186 if "sub" not in payload or not isinstance(payload["sub"], str): 187 raise MissingClaimError( 188 f"Subject (sub) claim must be a string present in the {self.get_token_type()}" 189 ) 190 191 def verify_audience(self, payload: typing.TypedDict) -> None: 192 """ 193 Verify the audience 194 195 Parameters 196 ---------- 197 payload : dict 198 The payload which contains the claims. Claims are statements about 199 an entity (typically, the user) and additional data. 200 201 Returns 202 ------- 203 None 204 205 Raises 206 ------ 207 MissingClaimError 208 If some field in the claim (payload) is missing this error is 209 raised. 210 InvalidClaimError 211 If some field in the claim (payload) doesn't match what's expected 212 this error is raised. 213 """ 214 215 if "aud" not in payload or not isinstance(payload["aud"], (str, list)): 216 raise MissingClaimError( 217 "Audience (aud) claim must be a string or array of strings present in the " 218 f"{self.get_token_type()}" 219 ) 220 221 if isinstance(payload["aud"], list) and self.audience not in payload["aud"]: 222 raise InvalidClaimError( 223 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected " 224 f"{self.audience} but was not one of {', '.join(payload['aud'])}" 225 ) 226 227 elif isinstance(payload["aud"], str) and payload["aud"] != self.audience: 228 raise InvalidClaimError( 229 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected" 230 f' {self.audience} but found {payload["aud"]}' 231 ) 232 233 def verify_expiration_time(self, payload: typing.TypedDict) -> None: 234 """ 235 Verify the expiration time 236 237 Parameters 238 ---------- 239 payload : dict 240 The payload which contains the claims. Claims are statements about 241 an entity (typically, the user) and additional data. 242 243 Returns 244 ------- 245 None 246 247 Raises 248 ------ 249 MissingClaimError 250 If some field in the claim (payload) is missing this error is 251 raised. 252 InvalidClaimError 253 If some field in the claim (payload) doesn't match what's expected 254 this error is raised. 255 """ 256 257 now = time.time() 258 leeway = self.leeway 259 260 # Expires at 261 if "exp" not in payload or not isinstance(payload["exp"], int): 262 raise MissingClaimError( 263 "Expiration Time (exp) claim must be a number present in the" 264 f" {self.get_token_type()}" 265 ) 266 267 exp_time = payload["exp"] + leeway 268 if now > exp_time: 269 raise InvalidClaimError( 270 f"Expiration Time (exp) claim error in the {self.get_token_type()}; " 271 f"current time ({now}) is after expiration time ({exp_time})" 272 ) 273 274 def verify_issued_at_time(self, payload: typing.TypedDict) -> None: 275 """ 276 Verify the issued at time 277 278 Parameters 279 ---------- 280 payload : dict 281 The payload which contains the claims. Claims are statements about 282 an entity (typically, the user) and additional data. 283 284 Returns 285 ------- 286 None 287 288 Raises 289 ------ 290 MissingClaimError 291 If some field in the claim (payload) is missing this error is 292 raised. 293 InvalidClaimError 294 If some field in the claim (payload) doesn't match what's expected 295 this error is raised. 296 """ 297 298 if "iat" not in payload or not isinstance(payload["iat"], int): 299 raise MissingClaimError( 300 f"Issued At (iat) claim must be a number present in the {self.get_token_type()}" 301 ) 302 303 def verify_azp(self, payload: typing.TypedDict) -> None: 304 """ 305 Verify the authorized party 306 307 Parameters 308 ---------- 309 payload : dict 310 The payload which contains the claims. Claims are statements about 311 an entity (typically, the user) and additional data. 312 313 Returns 314 ------- 315 None 316 317 Raises 318 ------ 319 MissingClaimError 320 If some field in the claim (payload) is missing this error is 321 raised. 322 InvalidClaimError 323 If some field in the claim (payload) doesn't match what's expected 324 this error is raised. 325 """ 326 327 if isinstance(payload["aud"], list) and len(payload["aud"]) > 1: 328 if "azp" not in payload or not isinstance(payload["azp"], str): 329 raise MissingClaimError( 330 "Authorized Party (azp) claim must be a string present in the " 331 f"{self.get_token_type()} when Audience (aud) claim has multiple values" 332 ) 333 334 if payload["azp"] != self.audience: 335 raise InvalidClaimError( 336 f"Authorized Party (azp) claim mismatch in the {self.get_token_type()};" 337 f' expected {self.audience}, found {payload["azp"]}' 338 ) 339 340 def verify_org_id(self, payload: typing.TypedDict, organization: str) -> None: 341 """ 342 Verify the organization id 343 344 Parameters 345 ---------- 346 payload : dict 347 The payload which contains the claims. Claims are statements about 348 an entity (typically, the user) and additional data. 349 organization : str 350 The organization that issued the token. 351 352 Returns 353 ------- 354 None 355 356 Raises 357 ------ 358 MissingClaimError 359 If some field in the claim (payload) is missing this error is 360 raised. 361 InvalidClaimError 362 If some field in the claim (payload) doesn't match what's expected 363 this error is raised. 364 """ 365 366 if "org_id" not in payload or not isinstance(payload["org_id"], str): 367 raise MissingClaimError( 368 "Organization (org_id) claim must be a string present in the" 369 f" {self.get_token_type()}" 370 ) 371 372 if payload["org_id"] != organization: 373 raise InvalidClaimError( 374 f"Organization (org_id) claim mismatch in the {self.get_token_type()}; expected " 375 f'{organization}, found {payload["org_id"]}' 376 ) 377 378 def verify_nonce(self, payload: typing.TypedDict, nonce: str): 379 """ 380 Verify the nonce 381 382 Parameters 383 ---------- 384 payload : dict 385 The payload which contains the claims. Claims are statements about 386 an entity (typically, the user) and additional data. 387 nonce : str 388 The valued associated to the Client session and the ID token. 389 390 Returns 391 ------- 392 None 393 394 Raises 395 ------ 396 MissingClaimError 397 If some field in the claim (payload) is missing this error is 398 raised. 399 InvalidClaimError 400 If some field in the claim (payload) doesn't match what's expected 401 this error is raised. 402 """ 403 404 if "nonce" not in payload or not isinstance(payload["nonce"], str): 405 raise MissingClaimError( 406 f"Nonce (nonce) claim must be a string present in the {self.get_token_type()}" 407 ) 408 409 if payload["nonce"] != nonce: 410 raise InvalidClaimError( 411 f"Nonce (nonce) claim mismatch in the {self.get_token_type()}; expected {nonce}, " 412 f'found {payload["nonce"]}' 413 ) 414 415 def verify_auth_time(self, payload: typing.TypedDict, max_age: int): 416 """ 417 Verify the authentication time 418 419 Parameters 420 ---------- 421 payload : dict 422 The payload which contains the claims. Claims are statements about 423 an entity (typically, the user) and additional data. 424 max_age : int 425 The max_age help us to customize when a token should expire. 426 427 Returns 428 ------- 429 None 430 431 Raises 432 ------ 433 MissingClaimError 434 If some field in the claim (payload) is missing this error is 435 raised. 436 InvalidClaimError 437 If some field in the claim (payload) doesn't match what's expected 438 this error is raised. 439 """ 440 441 now = time.time() 442 leeway = self.leeway 443 444 if "auth_time" not in payload or not isinstance(payload["auth_time"], int): 445 raise MissingClaimError( 446 "Authentication Time (auth_time) claim must be a number present in the" 447 f" {self.get_token_type()} when Max Age (max_age) is specified" 448 ) 449 450 auth_valid_until = payload["auth_time"] + max_age + leeway 451 if now > auth_valid_until: 452 raise InvalidClaimError( 453 f"Authentication Time (auth_time) claim in the {self.get_token_type()} indicates" 454 " that too much time has passed since the last end-user authentication. Current" 455 f" time ({now}) is after last auth at ({auth_valid_until})" 456 )
Attributes
- jwks_uri (str): The JSON Web Key Set URI where the public keys are located.
- issuer (str): The client that issued the JWT.
- audience (str): The recipient that the JWT is intended for.
Methods
get_token_type() Return the token type used by the verify methods. This method raise an error because this method should be implemented in the sub-class.
verify(auth0_token) Return the payload from an access token or ID token
get_auth0_signing_keys(jwks_uri) Return the signing keys from a given JSON Web Key Set URI.
verify_payload(args, *kwargs) This method raise an error because this method should be implemented in the sub-class.
Examples
Custom Token Verifier
class CustomTokenVerifier(JwtVerifier):
def get_token_type():
return "custom token"
def verify(self, auth0_token: str) -> dict:
payload: dict = super().verify(auth0_token)
return payload
def verify_payload(self, payload: dict) -> None:
# validate just the issuer
self.verify_issuer(payload)
View Source
61 def __init__( 62 self, 63 jwks_uri: str, 64 issuer: str, 65 audience: str, 66 /, 67 leeway: int = 0, 68 ) -> None: 69 """ 70 Parameters 71 ---------- 72 jwks_uri : str 73 The JSON Web Key Set URI where the public keys are located. 74 issuer : str 75 The client that issued the JWT. 76 audience : str 77 The recipient that the JWT is intended for. 78 """ 79 80 self.jwks_uri = jwks_uri 81 self.issuer = issuer 82 self.audience = audience 83 self.leeway = leeway
Parameters
- jwks_uri (str): The JSON Web Key Set URI where the public keys are located.
- issuer (str): The client that issued the JWT.
- audience (str): The recipient that the JWT is intended for.
View Source
Raises
- NotImplementedError: Raise an error because this method should be implemented in a sub-class.
View Source
96 def get_auth0_signing_keys(self, jwks_uri: str) -> str: # pragma: no cover 97 """ 98 99 Parameters 100 ---------- 101 jwks_uri : str 102 The JSON Web Key Set URI where the public keys are located. 103 104 Returns 105 ------- 106 auth0_signing_keys : str 107 Return the auth0 public keys as JSON. 108 """ 109 110 with urllib.request.urlopen(jwks_uri) as r: 111 return r.read().decode("utf-8")
Parameters
- jwks_uri (str): The JSON Web Key Set URI where the public keys are located.
Returns
- auth0_signing_keys (str): Return the auth0 public keys as JSON.
View Source
Raises
- NotImplementedError: Raise an error because this method should be implemented in a sub-class.
View Source
128 def verify_issuer(self, payload: typing.TypedDict) -> None: 129 """ 130 Verify the issuer 131 132 Parameters 133 ---------- 134 payload : dict 135 The payload which contains the claims. Claims are statements about 136 an entity (typically, the user) and additional data. 137 138 Returns 139 ------- 140 None 141 142 Raises 143 ------ 144 MissingClaimError 145 If some field in the claim (payload) is missing this error is 146 raised. 147 InvalidClaimError 148 If some field in the claim (payload) doesn't match what's expected 149 this error is raised. 150 """ 151 152 if "iss" not in payload or not isinstance(payload["iss"], str): 153 raise MissingClaimError( 154 f"Issuer (iss) claim must be a string present in the {self.get_token_type()}" 155 ) 156 if payload["iss"] != self.issuer: 157 raise InvalidClaimError( 158 f"Issuer (iss) claim mismatch in the {self.get_token_type()}; expected" 159 f' {self.issuer}, found {payload["iss"]}' 160 )
Verify the issuer
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
162 def verify_subject(self, payload: typing.TypedDict) -> None: 163 """ 164 Verify the subject 165 166 Parameters 167 ---------- 168 payload : dict 169 The payload which contains the claims. Claims are statements about 170 an entity (typically, the user) and additional data. 171 172 Returns 173 ------- 174 None 175 176 Raises 177 ------ 178 MissingClaimError 179 If some field in the claim (payload) is missing this error is 180 raised. 181 InvalidClaimError 182 If some field in the claim (payload) doesn't match what's expected 183 this error is raised. 184 """ 185 186 if "sub" not in payload or not isinstance(payload["sub"], str): 187 raise MissingClaimError( 188 f"Subject (sub) claim must be a string present in the {self.get_token_type()}" 189 )
Verify the subject
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
191 def verify_audience(self, payload: typing.TypedDict) -> None: 192 """ 193 Verify the audience 194 195 Parameters 196 ---------- 197 payload : dict 198 The payload which contains the claims. Claims are statements about 199 an entity (typically, the user) and additional data. 200 201 Returns 202 ------- 203 None 204 205 Raises 206 ------ 207 MissingClaimError 208 If some field in the claim (payload) is missing this error is 209 raised. 210 InvalidClaimError 211 If some field in the claim (payload) doesn't match what's expected 212 this error is raised. 213 """ 214 215 if "aud" not in payload or not isinstance(payload["aud"], (str, list)): 216 raise MissingClaimError( 217 "Audience (aud) claim must be a string or array of strings present in the " 218 f"{self.get_token_type()}" 219 ) 220 221 if isinstance(payload["aud"], list) and self.audience not in payload["aud"]: 222 raise InvalidClaimError( 223 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected " 224 f"{self.audience} but was not one of {', '.join(payload['aud'])}" 225 ) 226 227 elif isinstance(payload["aud"], str) and payload["aud"] != self.audience: 228 raise InvalidClaimError( 229 f"Audience (aud) claim mismatch in the {self.get_token_type()}; expected" 230 f' {self.audience} but found {payload["aud"]}' 231 )
Verify the audience
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
233 def verify_expiration_time(self, payload: typing.TypedDict) -> None: 234 """ 235 Verify the expiration time 236 237 Parameters 238 ---------- 239 payload : dict 240 The payload which contains the claims. Claims are statements about 241 an entity (typically, the user) and additional data. 242 243 Returns 244 ------- 245 None 246 247 Raises 248 ------ 249 MissingClaimError 250 If some field in the claim (payload) is missing this error is 251 raised. 252 InvalidClaimError 253 If some field in the claim (payload) doesn't match what's expected 254 this error is raised. 255 """ 256 257 now = time.time() 258 leeway = self.leeway 259 260 # Expires at 261 if "exp" not in payload or not isinstance(payload["exp"], int): 262 raise MissingClaimError( 263 "Expiration Time (exp) claim must be a number present in the" 264 f" {self.get_token_type()}" 265 ) 266 267 exp_time = payload["exp"] + leeway 268 if now > exp_time: 269 raise InvalidClaimError( 270 f"Expiration Time (exp) claim error in the {self.get_token_type()}; " 271 f"current time ({now}) is after expiration time ({exp_time})" 272 )
Verify the expiration time
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
274 def verify_issued_at_time(self, payload: typing.TypedDict) -> None: 275 """ 276 Verify the issued at time 277 278 Parameters 279 ---------- 280 payload : dict 281 The payload which contains the claims. Claims are statements about 282 an entity (typically, the user) and additional data. 283 284 Returns 285 ------- 286 None 287 288 Raises 289 ------ 290 MissingClaimError 291 If some field in the claim (payload) is missing this error is 292 raised. 293 InvalidClaimError 294 If some field in the claim (payload) doesn't match what's expected 295 this error is raised. 296 """ 297 298 if "iat" not in payload or not isinstance(payload["iat"], int): 299 raise MissingClaimError( 300 f"Issued At (iat) claim must be a number present in the {self.get_token_type()}" 301 )
Verify the issued at time
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
303 def verify_azp(self, payload: typing.TypedDict) -> None: 304 """ 305 Verify the authorized party 306 307 Parameters 308 ---------- 309 payload : dict 310 The payload which contains the claims. Claims are statements about 311 an entity (typically, the user) and additional data. 312 313 Returns 314 ------- 315 None 316 317 Raises 318 ------ 319 MissingClaimError 320 If some field in the claim (payload) is missing this error is 321 raised. 322 InvalidClaimError 323 If some field in the claim (payload) doesn't match what's expected 324 this error is raised. 325 """ 326 327 if isinstance(payload["aud"], list) and len(payload["aud"]) > 1: 328 if "azp" not in payload or not isinstance(payload["azp"], str): 329 raise MissingClaimError( 330 "Authorized Party (azp) claim must be a string present in the " 331 f"{self.get_token_type()} when Audience (aud) claim has multiple values" 332 ) 333 334 if payload["azp"] != self.audience: 335 raise InvalidClaimError( 336 f"Authorized Party (azp) claim mismatch in the {self.get_token_type()};" 337 f' expected {self.audience}, found {payload["azp"]}' 338 )
Verify the authorized party
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
340 def verify_org_id(self, payload: typing.TypedDict, organization: str) -> None: 341 """ 342 Verify the organization id 343 344 Parameters 345 ---------- 346 payload : dict 347 The payload which contains the claims. Claims are statements about 348 an entity (typically, the user) and additional data. 349 organization : str 350 The organization that issued the token. 351 352 Returns 353 ------- 354 None 355 356 Raises 357 ------ 358 MissingClaimError 359 If some field in the claim (payload) is missing this error is 360 raised. 361 InvalidClaimError 362 If some field in the claim (payload) doesn't match what's expected 363 this error is raised. 364 """ 365 366 if "org_id" not in payload or not isinstance(payload["org_id"], str): 367 raise MissingClaimError( 368 "Organization (org_id) claim must be a string present in the" 369 f" {self.get_token_type()}" 370 ) 371 372 if payload["org_id"] != organization: 373 raise InvalidClaimError( 374 f"Organization (org_id) claim mismatch in the {self.get_token_type()}; expected " 375 f'{organization}, found {payload["org_id"]}' 376 )
Verify the organization id
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
- organization (str): The organization that issued the token.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
378 def verify_nonce(self, payload: typing.TypedDict, nonce: str): 379 """ 380 Verify the nonce 381 382 Parameters 383 ---------- 384 payload : dict 385 The payload which contains the claims. Claims are statements about 386 an entity (typically, the user) and additional data. 387 nonce : str 388 The valued associated to the Client session and the ID token. 389 390 Returns 391 ------- 392 None 393 394 Raises 395 ------ 396 MissingClaimError 397 If some field in the claim (payload) is missing this error is 398 raised. 399 InvalidClaimError 400 If some field in the claim (payload) doesn't match what's expected 401 this error is raised. 402 """ 403 404 if "nonce" not in payload or not isinstance(payload["nonce"], str): 405 raise MissingClaimError( 406 f"Nonce (nonce) claim must be a string present in the {self.get_token_type()}" 407 ) 408 409 if payload["nonce"] != nonce: 410 raise InvalidClaimError( 411 f"Nonce (nonce) claim mismatch in the {self.get_token_type()}; expected {nonce}, " 412 f'found {payload["nonce"]}' 413 )
Verify the nonce
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
- nonce (str): The valued associated to the Client session and the ID token.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
415 def verify_auth_time(self, payload: typing.TypedDict, max_age: int): 416 """ 417 Verify the authentication time 418 419 Parameters 420 ---------- 421 payload : dict 422 The payload which contains the claims. Claims are statements about 423 an entity (typically, the user) and additional data. 424 max_age : int 425 The max_age help us to customize when a token should expire. 426 427 Returns 428 ------- 429 None 430 431 Raises 432 ------ 433 MissingClaimError 434 If some field in the claim (payload) is missing this error is 435 raised. 436 InvalidClaimError 437 If some field in the claim (payload) doesn't match what's expected 438 this error is raised. 439 """ 440 441 now = time.time() 442 leeway = self.leeway 443 444 if "auth_time" not in payload or not isinstance(payload["auth_time"], int): 445 raise MissingClaimError( 446 "Authentication Time (auth_time) claim must be a number present in the" 447 f" {self.get_token_type()} when Max Age (max_age) is specified" 448 ) 449 450 auth_valid_until = payload["auth_time"] + max_age + leeway 451 if now > auth_valid_until: 452 raise InvalidClaimError( 453 f"Authentication Time (auth_time) claim in the {self.get_token_type()} indicates" 454 " that too much time has passed since the last end-user authentication. Current" 455 f" time ({now}) is after last auth at ({auth_valid_until})" 456 )
Verify the authentication time
Parameters
- payload (dict): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
- max_age (int): The max_age help us to customize when a token should expire.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
View Source
459class IdTokenPayload(typing.TypedDict): 460 """ 461 Attributes 462 ---------- 463 iss : str 464 The "iss" (issuer) claim identifies the principal that issued the JWT. 465 `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1>`_. 466 sub : str 467 The "sub" (subject) claim identifies the principal that is the subject 468 of the JWT. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2>`_. 469 aud : str, list of str 470 The "aud" (audience) claim identifies the recipients that the JWT is 471 intended for. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. 472 exp : int 473 The "exp" (expiration time) claim identifies the expiration time on or 474 after which the JWT MUST NOT be accepted for processing. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_. 475 iat : int 476 The "iat" (issued at) claim identifies the time at which the JWT was 477 issued. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6>`_. 478 name : str 479 The "name" claim identifies the full name. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 480 given_name : str 481 The "given_name" claim identifies the given name(s) or first name(s). `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 482 family_name : str 483 The "family_name" claim identifies the surname(s) or last name(s). `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 484 gender : str, {'male', 'female'} 485 The "gender" claim identifies the gender. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 486 birthdate : str 487 The "birthdate" claim identifies the birthday. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 488 email : str 489 The "email" claim identifies the preferred e-mail address. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 490 picture : str 491 The "picture" claim identifies the profile picture URL. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 492 nonce : str, optional 493 The "nonce" claim identifies the valued used to associate a Client 494 session with an ID Token, and to mitigate replay attacks. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 495 auth_time : int, optional 496 The "auth_time" (authentication time) claim identifies the time when the 497 End-User authentication occurred. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 498 """ 499 500 iss: str 501 sub: str 502 aud: typing.Union[str, typing.List[str]] 503 exp: int 504 iat: int 505 name: str 506 given_name: str 507 family_name: str 508 gender: typing.Literal["male", "female"] 509 birthdate: str 510 email: str 511 picture: str 512 nonce: typing.Optional[str] 513 auth_time: typing.Optional[int]
Attributes
- iss (str): The "iss" (issuer) claim identifies the principal that issued the JWT. Read more.
- sub (str): The "sub" (subject) claim identifies the principal that is the subject of the JWT. Read more.
- aud (str, list of str): The "aud" (audience) claim identifies the recipients that the JWT is intended for. Read more.
- exp (int): The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. Read more.
- iat (int): The "iat" (issued at) claim identifies the time at which the JWT was issued. Read more.
- name (str): The "name" claim identifies the full name. Read more.
- given_name (str): The "given_name" claim identifies the given name(s) or first name(s). Read more.
- family_name (str): The "family_name" claim identifies the surname(s) or last name(s). Read more.
- gender (str, {'male', 'female'}): The "gender" claim identifies the gender. Read more.
- birthdate (str): The "birthdate" claim identifies the birthday. Read more.
- email (str): The "email" claim identifies the preferred e-mail address. Read more.
- picture (str): The "picture" claim identifies the profile picture URL. Read more.
- nonce (str, optional): The "nonce" claim identifies the valued used to associate a Client session with an ID Token, and to mitigate replay attacks. Read more.
- auth_time (int, optional): The "auth_time" (authentication time) claim identifies the time when the End-User authentication occurred. Read more.
Inherited Members
- builtins.dict
- get
- setdefault
- pop
- popitem
- keys
- items
- values
- update
- fromkeys
- clear
- copy
View Source
516class IdTokenVerifier(JwtVerifier): 517 """ 518 Attributes 519 ---------- 520 jwks_uri : str 521 The JSON Web Key Set URI where the public keys are located. 522 issuer : str 523 The client that issued the JWT. 524 audience : str 525 The recipient that the JWT is intended for. 526 527 Methods 528 ------- 529 get_token_type() 530 Return the token type used by the verify methods. 531 532 get_auth0_signing_keys(jwks_uri) 533 Return the public keys located under the given JSON Web Key Set URI. 534 535 verify(auth_token[, nonce, max_age, organization]) 536 Return the payload or raise an error when on of the verifications fails. 537 538 verify_payload(payload[, nonce, max_age, organization]) 539 Raise an error when one of the verification fails. 540 541 Examples 542 -------- 543 Validating ID Token 544 545 >>> from auth0_jwt_validator import IdTokenVerifier 546 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 547 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 548 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 549 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 550 >>> token_verifier.verify("some-id-token") 551 552 Validating ID Token and nonce 553 554 >>> from auth0_jwt_validator import IdTokenVerifier 555 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 556 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 557 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 558 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 559 >>> token_verifier.verify("some-id-token", nonce="some-random-string") 560 561 Validating ID Token and max_age 562 563 >>> from auth0_jwt_validator import IdTokenVerifier 564 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 565 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 566 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 567 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 568 >>> token_verifier.verify("some-id-token", max_age=60 * 60 * 24) 569 570 Validating ID Token and organization 571 572 >>> from auth0_jwt_validator import IdTokenVerifier 573 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 574 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 575 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 576 >>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience) 577 >>> token_verifier.verify("some-id-token", organization="some-organization") 578 """ 579 580 def get_token_type(self) -> str: 581 """ 582 Returns 583 ------- 584 token_type : str 585 Return the token type (ID token or access token) used by the verify 586 methods. 587 """ 588 589 return "ID token" 590 591 def verify( 592 self, 593 auth0_token: str, 594 /, 595 nonce: typing.Optional[str] = None, 596 max_age: typing.Optional[int] = None, 597 organization: typing.Optional[str] = None, 598 ) -> IdTokenPayload: 599 """ 600 Parameters 601 ---------- 602 auth0_token : str 603 The ID token. 604 605 Other Parameters 606 ---------------- 607 nonce : str, default None 608 Lorem. 609 max_age : int, default None 610 Lorem. 611 organization : str, default None 612 The organization that issued the token. 613 614 Returns 615 ------- 616 payload: `IdTokenPayload` 617 The payload which contains the claims. Claims are statements about 618 an entity (typically, the user) and additional data. 619 620 Raises 621 ------ 622 MissingClaimError 623 If some field in the claim (payload) is missing this error is 624 raised. 625 InvalidClaimError 626 If some field in the claim (payload) doesn't match what's expected 627 this error is raised. 628 629 See Also 630 -------- 631 IdTokenPayload 632 """ 633 634 payload: IdTokenPayload = super().verify(auth0_token) # type: ignore 635 self.verify_payload(payload, nonce=nonce, max_age=max_age, organization=organization) 636 637 return payload 638 639 def verify_payload( 640 self, 641 payload: IdTokenPayload, 642 /, 643 nonce: typing.Optional[str] = None, 644 max_age: typing.Optional[int] = None, 645 organization: typing.Optional[str] = None, 646 ) -> None: 647 """ 648 Parameters 649 ---------- 650 payload : `IdTokenPayload` 651 The payload which contains the claims. Claims are statements about 652 an entity (typically, the user) and additional data. 653 654 Other Parameters 655 ---------------- 656 nonce : str, default None 657 Lorem. 658 max_age : int, default None 659 Lorem. 660 organization : str, default None 661 The organization that issued the token. 662 663 Returns 664 ------- 665 None 666 667 Raises 668 ------ 669 MissingClaimError 670 If some field in the claim (payload) is missing this error is 671 raised. 672 InvalidClaimError 673 If some field in the claim (payload) doesn't match what's expected 674 this error is raised. 675 676 See Also 677 -------- 678 IdTokenPayload 679 """ 680 681 # Issuer 682 self.verify_issuer(payload) 683 684 # Subject 685 self.verify_subject(payload) 686 687 # Audience 688 self.verify_audience(payload) 689 690 # Expires at 691 self.verify_expiration_time(payload) 692 693 # Issued at 694 self.verify_issued_at_time(payload) 695 696 # Nonce 697 if nonce: 698 self.verify_nonce(payload, nonce) 699 700 # Organization 701 if organization: 702 self.verify_org_id(payload, organization) 703 704 # Authorized party 705 self.verify_azp(payload) 706 707 # Authentication time 708 if max_age: 709 self.verify_auth_time(payload, max_age)
Attributes
- jwks_uri (str): The JSON Web Key Set URI where the public keys are located.
- issuer (str): The client that issued the JWT.
- audience (str): The recipient that the JWT is intended for.
Methods
get_token_type() Return the token type used by the verify methods.
get_auth0_signing_keys(jwks_uri) Return the public keys located under the given JSON Web Key Set URI.
verify(auth_token[, nonce, max_age, organization]) Return the payload or raise an error when on of the verifications fails.
verify_payload(payload[, nonce, max_age, organization]) Raise an error when one of the verification fails.
Examples
Validating ID Token
>>> from auth0_jwt_validator import IdTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-id-token")
Validating ID Token and nonce
>>> from auth0_jwt_validator import IdTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-id-token", nonce="some-random-string")
Validating ID Token and max_age
>>> from auth0_jwt_validator import IdTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-id-token", max_age=60 * 60 * 24)
Validating ID Token and organization
>>> from auth0_jwt_validator import IdTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = IdTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-id-token", organization="some-organization")
View Source
Returns
- token_type (str): Return the token type (ID token or access token) used by the verify methods.
View Source
591 def verify( 592 self, 593 auth0_token: str, 594 /, 595 nonce: typing.Optional[str] = None, 596 max_age: typing.Optional[int] = None, 597 organization: typing.Optional[str] = None, 598 ) -> IdTokenPayload: 599 """ 600 Parameters 601 ---------- 602 auth0_token : str 603 The ID token. 604 605 Other Parameters 606 ---------------- 607 nonce : str, default None 608 Lorem. 609 max_age : int, default None 610 Lorem. 611 organization : str, default None 612 The organization that issued the token. 613 614 Returns 615 ------- 616 payload: `IdTokenPayload` 617 The payload which contains the claims. Claims are statements about 618 an entity (typically, the user) and additional data. 619 620 Raises 621 ------ 622 MissingClaimError 623 If some field in the claim (payload) is missing this error is 624 raised. 625 InvalidClaimError 626 If some field in the claim (payload) doesn't match what's expected 627 this error is raised. 628 629 See Also 630 -------- 631 IdTokenPayload 632 """ 633 634 payload: IdTokenPayload = super().verify(auth0_token) # type: ignore 635 self.verify_payload(payload, nonce=nonce, max_age=max_age, organization=organization) 636 637 return payload
Parameters
- auth0_token (str): The ID token.
Other Parameters
- nonce (str, default None): Lorem.
- max_age (int, default None): Lorem.
- organization (str, default None): The organization that issued the token.
Returns
- payload (
IdTokenPayload
): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
See Also
View Source
639 def verify_payload( 640 self, 641 payload: IdTokenPayload, 642 /, 643 nonce: typing.Optional[str] = None, 644 max_age: typing.Optional[int] = None, 645 organization: typing.Optional[str] = None, 646 ) -> None: 647 """ 648 Parameters 649 ---------- 650 payload : `IdTokenPayload` 651 The payload which contains the claims. Claims are statements about 652 an entity (typically, the user) and additional data. 653 654 Other Parameters 655 ---------------- 656 nonce : str, default None 657 Lorem. 658 max_age : int, default None 659 Lorem. 660 organization : str, default None 661 The organization that issued the token. 662 663 Returns 664 ------- 665 None 666 667 Raises 668 ------ 669 MissingClaimError 670 If some field in the claim (payload) is missing this error is 671 raised. 672 InvalidClaimError 673 If some field in the claim (payload) doesn't match what's expected 674 this error is raised. 675 676 See Also 677 -------- 678 IdTokenPayload 679 """ 680 681 # Issuer 682 self.verify_issuer(payload) 683 684 # Subject 685 self.verify_subject(payload) 686 687 # Audience 688 self.verify_audience(payload) 689 690 # Expires at 691 self.verify_expiration_time(payload) 692 693 # Issued at 694 self.verify_issued_at_time(payload) 695 696 # Nonce 697 if nonce: 698 self.verify_nonce(payload, nonce) 699 700 # Organization 701 if organization: 702 self.verify_org_id(payload, organization) 703 704 # Authorized party 705 self.verify_azp(payload) 706 707 # Authentication time 708 if max_age: 709 self.verify_auth_time(payload, max_age)
Parameters
- payload (
IdTokenPayload
): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Other Parameters
- nonce (str, default None): Lorem.
- max_age (int, default None): Lorem.
- organization (str, default None): The organization that issued the token.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
See Also
View Source
712class AccessTokenPayload(typing.TypedDict): 713 """ 714 Attributes 715 ---------- 716 iss : str 717 The "iss" (issuer) claim identifies the principal that issued the JWT. 718 `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1>`_. 719 sub : str 720 The "sub" (subject) claim identifies the principal that is the subject 721 of the JWT. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2>`_. 722 aud : str, list of str 723 The "aud" (audience) claim identifies the recipients that the JWT is 724 intended for. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. 725 azp : str 726 The "azp" (authorized party) claim identifies the party to which the ID 727 Token was issued. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 728 exp : int 729 The "exp" (expiration time) claim identifies the expiration time on or 730 after which the JWT MUST NOT be accepted for processing. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_. 731 iat : int 732 The "iat" (issued at) claim identifies the time at which the JWT was 733 issued. `Read more<https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6>`_. 734 scope : str 735 The "scope" claim identifies the space-separated list of scopes 736 associated with the token. `Read more<https://www.iana.org/assignments/jwt/jwt.xhtml>`_. 737 gty : str 738 The "gty" (grant type) claim identifies the grant type used to create 739 the token. 740 permissions : list of str, optional 741 The "permissions" claim identifies the list of permissions associated to 742 the current user. 743 org_id : str, optional 744 The "org_id" (organization id) claim identifies the organization under 745 which the JWT was issued. 746 """ 747 748 iss: str 749 sub: str 750 aud: typing.Union[str, typing.List[str]] 751 azp: str 752 exp: int 753 iat: int 754 scope: str 755 gty: str 756 permissions: typing.Optional[typing.List[str]] 757 org_id: typing.Optional[str]
Attributes
- iss (str): The "iss" (issuer) claim identifies the principal that issued the JWT. Read more.
- sub (str): The "sub" (subject) claim identifies the principal that is the subject of the JWT. Read more.
- aud (str, list of str): The "aud" (audience) claim identifies the recipients that the JWT is intended for. Read more.
- azp (str): The "azp" (authorized party) claim identifies the party to which the ID Token was issued. Read more.
- exp (int): The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. Read more.
- iat (int): The "iat" (issued at) claim identifies the time at which the JWT was issued. Read more.
- scope (str): The "scope" claim identifies the space-separated list of scopes associated with the token. Read more.
- gty (str): The "gty" (grant type) claim identifies the grant type used to create the token.
- permissions (list of str, optional): The "permissions" claim identifies the list of permissions associated to the current user.
- org_id (str, optional): The "org_id" (organization id) claim identifies the organization under which the JWT was issued.
Inherited Members
- builtins.dict
- get
- setdefault
- pop
- popitem
- keys
- items
- values
- update
- fromkeys
- clear
- copy
View Source
760class AccessTokenVerifier(JwtVerifier): 761 """ 762 Attributes 763 ---------- 764 jwks_uri : str 765 The JSON Web Key Set URI where the public keys are located. 766 issuer : str 767 The client that issued the JWT. 768 audience : str 769 The recipient that the JWT is intended for. 770 771 Methods 772 ------- 773 get_token_type() 774 Return the token type used by the verify methods. 775 776 get_auth0_signing_keys(jwks_uri) 777 Return the public keys located under the given JSON Web Key Set URI. 778 779 verify(auth_token[,organization, required_scopes, required_permissions]) 780 Return the payload or raise an error. 781 782 verify_payload(payload[,organization, required_scopes, required_permissions]) 783 Raise an error when one of the verification fails. 784 785 Examples 786 -------- 787 Validating Access Token 788 789 >>> from auth0_jwt_validator import AccessTokenVerifier 790 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 791 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 792 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 793 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 794 >>> token_verifier.verify("some-access-token") 795 796 Validating Access Token and organization 797 798 >>> from auth0_jwt_validator import AccessTokenVerifier 799 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 800 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 801 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 802 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 803 >>> token_verifier.verify("some-access-token", organization="some-organization") 804 805 Validating Access Token and scopes 806 807 >>> from auth0_jwt_validator import AccessTokenVerifier 808 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 809 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 810 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 811 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 812 >>> token_verifier.verify("some-access-token", required_scopes=["profile", "calendar"]) 813 814 Validating Access Token and permissions 815 816 >>> from auth0_jwt_validator import AccessTokenVerifier 817 >>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json" 818 >>> issuer = "https://<auth0-tenant>.us.auth0.com/" 819 >>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/" 820 >>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience) 821 >>> token_verifier.verify("some-access-token", required_permissions=["read:user", "delete:user"]) 822 """ 823 824 def get_token_type(self) -> str: 825 """ 826 Returns 827 ------- 828 token_type : str 829 Return the token type (ID token or access token) used by the verify 830 methods. 831 """ 832 833 return "access token" 834 835 def verify( 836 self, 837 auth0_token: str, 838 /, 839 organization: typing.Optional[str] = None, 840 required_scopes: typing.Optional[typing.List[str]] = None, 841 required_permissions: typing.Optional[typing.List[str]] = None, 842 ) -> AccessTokenPayload: 843 """ 844 Parameters 845 ----------- 846 auth_token : str 847 The access token. 848 849 Other Parameters 850 ---------------- 851 organization : str, default None 852 The organization that issued the token. 853 required_scopes : list of str, default None 854 The list of scopes which are required to be in the payload. 855 required_permissions : list of str, default None 856 The list of permissions which are required to be in the payload. 857 858 Returns 859 ------- 860 payload : `AccessTokenPayload` 861 The payload which contains the claims. Claims are statements about 862 an entity (typically, the user) and additional data. 863 864 Raises 865 ------- 866 MissingClaimError 867 If some field in the claim (payload) is missing this error is raised. 868 InvalidClaimError 869 If some field in the claim (payload) doesn't match what's expected 870 this error is raised. 871 872 See Also 873 -------- 874 AccessTokenPayload 875 """ 876 877 payload: AccessTokenPayload = super().verify(auth0_token) # type: ignore 878 self.verify_payload( 879 payload, 880 organization=organization, 881 required_scopes=required_scopes, 882 required_permissions=required_permissions, 883 ) 884 885 return payload 886 887 def verify_payload( 888 self, 889 payload: AccessTokenPayload, 890 /, 891 organization: typing.Optional[str] = None, 892 required_scopes: typing.Optional[typing.List[str]] = None, 893 required_permissions: typing.Optional[typing.List[str]] = None, 894 ) -> None: 895 """ 896 Parameters 897 ---------- 898 payload : `AccessTokenPayload` 899 The payload which contains the claims. Claims are statements about 900 an entity (typically, the user) and additional data. 901 902 Other Parameters 903 ---------------- 904 organization : str, default None 905 The organization that issued the token. 906 required_scopes : list of str, default None 907 The list of scopes which are required to be in the payload. 908 required_permissions : list of str, default None 909 The list of permissions which are required to be in the payload. 910 911 Returns 912 ------- 913 None 914 915 Raises 916 ------ 917 MissingClaimError 918 If some field in the claim (payload) is missing this error is raised. 919 InvalidClaimError 920 If some field in the claim (payload) doesn't match what's expected 921 this error is raised. 922 923 See Also 924 -------- 925 AccessTokenPayload 926 """ 927 928 # Issuer 929 self.verify_issuer(payload) 930 931 # Subject 932 self.verify_subject(payload) 933 934 # Audience 935 self.verify_audience(payload) 936 937 # Expires at 938 self.verify_expiration_time(payload) 939 940 # Issued at 941 self.verify_issued_at_time(payload) 942 943 # Organization 944 if organization: 945 self.verify_org_id(payload, organization) 946 947 if required_scopes: 948 self._verify_scopes(payload, required_scopes) 949 950 if required_permissions: 951 self._verify_permissions(payload, required_permissions) 952 953 def _verify_scopes( 954 self, payload: AccessTokenPayload, /, required_scopes: typing.List[str] 955 ) -> None: 956 if "scope" not in payload or not isinstance(payload["scope"], str): 957 raise MissingClaimError( 958 f"Scope (scope) claim must be a string present in the {self.get_token_type()}" 959 ) 960 961 scopes = payload["scope"].split() 962 has_enough_scopes = set(scopes).issuperset(required_scopes) 963 964 if not has_enough_scopes: 965 raise InvalidClaimError( 966 f"Scope (scope) claim mismatch in the {self.get_token_type()}; expected at least " 967 f"these {', '.join(required_scopes)} but was found these {', '.join(scopes)}" 968 ) 969 970 def _verify_permissions( 971 self, payload: AccessTokenPayload, /, required_permissions: typing.List[str] 972 ) -> None: 973 if "permissions" not in payload or not isinstance(payload["permissions"], list): 974 raise MissingClaimError( 975 "Permissions (permissions) claim must be an array of strings present in the " 976 f"{self.get_token_type()}" 977 ) 978 979 permissions = payload["permissions"] 980 has_enough_scopes = set(permissions).issuperset(required_permissions) 981 982 if not has_enough_scopes: 983 raise InvalidClaimError( 984 f"Permissions (permissions) claim mismatch in the {self.get_token_type()}; " 985 f"expected at least these {', '.join(required_permissions)} but was found these " 986 f"{', '.join(permissions)}" 987 )
Attributes
- jwks_uri (str): The JSON Web Key Set URI where the public keys are located.
- issuer (str): The client that issued the JWT.
- audience (str): The recipient that the JWT is intended for.
Methods
get_token_type() Return the token type used by the verify methods.
get_auth0_signing_keys(jwks_uri) Return the public keys located under the given JSON Web Key Set URI.
verify(auth_token[,organization, required_scopes, required_permissions]) Return the payload or raise an error.
verify_payload(payload[,organization, required_scopes, required_permissions]) Raise an error when one of the verification fails.
Examples
Validating Access Token
>>> from auth0_jwt_validator import AccessTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-access-token")
Validating Access Token and organization
>>> from auth0_jwt_validator import AccessTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-access-token", organization="some-organization")
Validating Access Token and scopes
>>> from auth0_jwt_validator import AccessTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-access-token", required_scopes=["profile", "calendar"])
Validating Access Token and permissions
>>> from auth0_jwt_validator import AccessTokenVerifier
>>> auth0_jwks_uri = "https://<auth0-tenant>.us.auth0.com/.well-known/jwks.json"
>>> issuer = "https://<auth0-tenant>.us.auth0.com/"
>>> audience = "https://<auth0-tenant>.us.auth0.com/api/v2/"
>>> token_verifier = AccessTokenVerifier(auth0_jwks_uri, issuer, audience)
>>> token_verifier.verify("some-access-token", required_permissions=["read:user", "delete:user"])
View Source
Returns
- token_type (str): Return the token type (ID token or access token) used by the verify methods.
View Source
835 def verify( 836 self, 837 auth0_token: str, 838 /, 839 organization: typing.Optional[str] = None, 840 required_scopes: typing.Optional[typing.List[str]] = None, 841 required_permissions: typing.Optional[typing.List[str]] = None, 842 ) -> AccessTokenPayload: 843 """ 844 Parameters 845 ----------- 846 auth_token : str 847 The access token. 848 849 Other Parameters 850 ---------------- 851 organization : str, default None 852 The organization that issued the token. 853 required_scopes : list of str, default None 854 The list of scopes which are required to be in the payload. 855 required_permissions : list of str, default None 856 The list of permissions which are required to be in the payload. 857 858 Returns 859 ------- 860 payload : `AccessTokenPayload` 861 The payload which contains the claims. Claims are statements about 862 an entity (typically, the user) and additional data. 863 864 Raises 865 ------- 866 MissingClaimError 867 If some field in the claim (payload) is missing this error is raised. 868 InvalidClaimError 869 If some field in the claim (payload) doesn't match what's expected 870 this error is raised. 871 872 See Also 873 -------- 874 AccessTokenPayload 875 """ 876 877 payload: AccessTokenPayload = super().verify(auth0_token) # type: ignore 878 self.verify_payload( 879 payload, 880 organization=organization, 881 required_scopes=required_scopes, 882 required_permissions=required_permissions, 883 ) 884 885 return payload
Parameters
- auth_token (str): The access token.
Other Parameters
- organization (str, default None): The organization that issued the token.
- required_scopes (list of str, default None): The list of scopes which are required to be in the payload.
- required_permissions (list of str, default None): The list of permissions which are required to be in the payload.
Returns
- payload (
AccessTokenPayload
): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.
See Also
View Source
887 def verify_payload( 888 self, 889 payload: AccessTokenPayload, 890 /, 891 organization: typing.Optional[str] = None, 892 required_scopes: typing.Optional[typing.List[str]] = None, 893 required_permissions: typing.Optional[typing.List[str]] = None, 894 ) -> None: 895 """ 896 Parameters 897 ---------- 898 payload : `AccessTokenPayload` 899 The payload which contains the claims. Claims are statements about 900 an entity (typically, the user) and additional data. 901 902 Other Parameters 903 ---------------- 904 organization : str, default None 905 The organization that issued the token. 906 required_scopes : list of str, default None 907 The list of scopes which are required to be in the payload. 908 required_permissions : list of str, default None 909 The list of permissions which are required to be in the payload. 910 911 Returns 912 ------- 913 None 914 915 Raises 916 ------ 917 MissingClaimError 918 If some field in the claim (payload) is missing this error is raised. 919 InvalidClaimError 920 If some field in the claim (payload) doesn't match what's expected 921 this error is raised. 922 923 See Also 924 -------- 925 AccessTokenPayload 926 """ 927 928 # Issuer 929 self.verify_issuer(payload) 930 931 # Subject 932 self.verify_subject(payload) 933 934 # Audience 935 self.verify_audience(payload) 936 937 # Expires at 938 self.verify_expiration_time(payload) 939 940 # Issued at 941 self.verify_issued_at_time(payload) 942 943 # Organization 944 if organization: 945 self.verify_org_id(payload, organization) 946 947 if required_scopes: 948 self._verify_scopes(payload, required_scopes) 949 950 if required_permissions: 951 self._verify_permissions(payload, required_permissions)
Parameters
- payload (
AccessTokenPayload
): The payload which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
Other Parameters
- organization (str, default None): The organization that issued the token.
- required_scopes (list of str, default None): The list of scopes which are required to be in the payload.
- required_permissions (list of str, default None): The list of permissions which are required to be in the payload.
Returns
- None
Raises
- MissingClaimError: If some field in the claim (payload) is missing this error is raised.
- InvalidClaimError: If some field in the claim (payload) doesn't match what's expected this error is raised.