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            )
#   class JwtVerifier:
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)        
#   JwtVerifier(jwks_uri: str, issuer: str, audience: str, /, leeway: int = 0)
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.
#   def get_token_type(self) -> None:
View Source
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")
Raises
  • NotImplementedError: Raise an error because this method should be implemented in a sub-class.
#   def get_auth0_signing_keys(self, jwks_uri: str) -> str:
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.
#   def verify(self, auth0_token: str) -> dict:
View Source
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()
#   def verify_payload(self, *args, **kwargs) -> None:
View Source
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")
Raises
  • NotImplementedError: Raise an error because this method should be implemented in a sub-class.
#   def verify_issuer(self, payload: <function TypedDict>) -> None:
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.
#   def verify_subject(self, payload: <function TypedDict>) -> None:
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.
#   def verify_audience(self, payload: <function TypedDict>) -> None:
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.
#   def verify_expiration_time(self, payload: <function TypedDict>) -> None:
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.
#   def verify_issued_at_time(self, payload: <function TypedDict>) -> None:
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.
#   def verify_azp(self, payload: <function TypedDict>) -> None:
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.
#   def verify_org_id(self, payload: <function TypedDict>, organization: str) -> None:
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.
#   def verify_nonce(self, payload: <function TypedDict>, nonce: str):
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.
#   def verify_auth_time(self, payload: <function TypedDict>, max_age: int):
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.
#   class IdTokenPayload(typing.TypedDict):
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
#   class IdTokenVerifier(JwtVerifier):
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")
#   def get_token_type(self) -> str:
View Source
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"
Returns
  • token_type (str): Return the token type (ID token or access token) used by the verify methods.
#   def verify( self, auth0_token: str, /, nonce: Optional[str] = None, max_age: Optional[int] = None, organization: Optional[str] = None ) -> auth0_jwt_validator.jwt_verifier.IdTokenPayload:
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

IdTokenPayload

#   def verify_payload( self, payload: auth0_jwt_validator.jwt_verifier.IdTokenPayload, /, nonce: Optional[str] = None, max_age: Optional[int] = None, organization: Optional[str] = None ) -> None:
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

IdTokenPayload

#   class AccessTokenPayload(typing.TypedDict):
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
#   class AccessTokenVerifier(JwtVerifier):
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"])
#   def get_token_type(self) -> str:
View Source
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"
Returns
  • token_type (str): Return the token type (ID token or access token) used by the verify methods.
#   def verify( self, auth0_token: str, /, organization: Optional[str] = None, required_scopes: Optional[List[str]] = None, required_permissions: Optional[List[str]] = None ) -> auth0_jwt_validator.jwt_verifier.AccessTokenPayload:
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

AccessTokenPayload

#   def verify_payload( self, payload: auth0_jwt_validator.jwt_verifier.AccessTokenPayload, /, organization: Optional[str] = None, required_scopes: Optional[List[str]] = None, required_permissions: Optional[List[str]] = None ) -> None:
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.
See Also

AccessTokenPayload