fief_client

Fief client for Python.

 1"Fief client for Python."
 2from fief_client.client import (
 3    Fief,
 4    FiefAccessTokenACRTooLow,
 5    FiefAccessTokenExpired,
 6    FiefAccessTokenInfo,
 7    FiefAccessTokenInvalid,
 8    FiefAccessTokenMissingPermission,
 9    FiefAccessTokenMissingScope,
10    FiefACR,
11    FiefAsync,
12    FiefError,
13    FiefIdTokenInvalid,
14    FiefRequestError,
15    FiefTokenResponse,
16    FiefUserInfo,
17)
18
19__version__ = "0.18.6"
20
21__all__ = [
22    "Fief",
23    "FiefACR",
24    "FiefAsync",
25    "FiefTokenResponse",
26    "FiefAccessTokenInfo",
27    "FiefUserInfo",
28    "FiefError",
29    "FiefAccessTokenACRTooLow",
30    "FiefAccessTokenExpired",
31    "FiefAccessTokenMissingPermission",
32    "FiefAccessTokenMissingScope",
33    "FiefAccessTokenInvalid",
34    "FiefIdTokenInvalid",
35    "FiefRequestError",
36    "crypto",
37    "pkce",
38    "integrations",
39]
class Fief(fief_client.client.BaseFief):
496class Fief(BaseFief):
497    """Sync Fief authentication client."""
498
499    def __init__(
500        self,
501        base_url: str,
502        client_id: str,
503        client_secret: Optional[str] = None,
504        *,
505        encryption_key: Optional[str] = None,
506        host: Optional[str] = None,
507        verify: VerifyTypes = True,
508        cert: Optional[CertTypes] = None,
509    ) -> None:
510        super().__init__(
511            base_url,
512            client_id,
513            client_secret,
514            encryption_key=encryption_key,
515            host=host,
516            verify=verify,
517            cert=cert,
518        )
519
520    def auth_url(
521        self,
522        redirect_uri: str,
523        *,
524        state: Optional[str] = None,
525        scope: Optional[List[str]] = None,
526        code_challenge: Optional[str] = None,
527        code_challenge_method: Optional[str] = None,
528        lang: Optional[str] = None,
529        extras_params: Optional[Mapping[str, str]] = None,
530    ) -> str:
531        """
532        Return an authorization URL.
533
534        :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
535        :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
536        :param scope: Optional list of scopes to ask for.
537        :param code_challenge: Optional code challenge for
538        [PKCE process](https://docs.fief.dev/going-further/pkce/).
539        :param code_challenge_method: Method used to hash the PKCE code challenge.
540        :param lang: Optional parameter to set the user locale.
541        Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`.
542        If not provided, the user locale is determined by their browser settings.
543        :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/).
544
545        **Example:**
546
547        ```py
548        auth_url = fief.auth_url("http://localhost:8000/callback", scope=["openid"])
549        ```
550        """
551        openid_configuration = self._get_openid_configuration()
552        return self._auth_url(
553            openid_configuration,
554            redirect_uri,
555            state=state,
556            scope=scope,
557            code_challenge=code_challenge,
558            code_challenge_method=code_challenge_method,
559            lang=lang,
560            extras_params=extras_params,
561        )
562
563    def auth_callback(
564        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
565    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
566        """
567        Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code.
568
569        :param code: The authorization code.
570        :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL.
571        :param code_verifier:  The raw
572        [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization.
573
574        **Example:**
575
576        ```py
577        tokens, userinfo = fief.auth_callback("CODE", "http://localhost:8000/callback")
578        ```
579        """
580        token_response = self._auth_exchange_token(
581            code, redirect_uri, code_verifier=code_verifier
582        )
583        jwks = self._get_jwks()
584        userinfo = self._decode_id_token(
585            token_response["id_token"],
586            jwks,
587            code=code,
588            access_token=token_response.get("access_token"),
589        )
590        return token_response, userinfo
591
592    def auth_refresh_token(
593        self, refresh_token: str, *, scope: Optional[List[str]] = None
594    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
595        """
596        Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token
597
598        :param refresh_token: A valid refresh token.
599        :param scope: Optional list of scopes to ask for.
600        If not provided, the access token will share the same list of scopes as requested the first time.
601        Otherwise, it should be a subset of the original list of scopes.
602
603        **Example:**
604
605        ```py
606        tokens, userinfo = fief.auth_refresh_token("REFRESH_TOKEN")
607        ```
608        """
609        token_endpoint = self._get_endpoint_url(
610            self._get_openid_configuration(), "token_endpoint"
611        )
612        with self._get_httpx_client() as client:
613            request = self._get_auth_refresh_token_request(
614                client,
615                endpoint=token_endpoint,
616                refresh_token=refresh_token,
617                scope=scope,
618            )
619            response = client.send(request)
620
621            self._handle_request_error(response)
622
623            token_response = response.json()
624        jwks = self._get_jwks()
625        userinfo = self._decode_id_token(
626            token_response["id_token"],
627            jwks,
628            access_token=token_response.get("access_token"),
629        )
630        return token_response, userinfo
631
632    def validate_access_token(
633        self,
634        access_token: str,
635        *,
636        required_scope: Optional[List[str]] = None,
637        required_acr: Optional[FiefACR] = None,
638        required_permissions: Optional[List[str]] = None,
639    ) -> FiefAccessTokenInfo:
640        """
641        Check if an access token is valid and optionally that it has a required list of scopes,
642        or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/).
643        Returns a `FiefAccessTokenInfo`.
644
645        :param access_token: The access token to validate.
646        :param required_scope: Optional list of scopes to check for.
647        :param required_acr: Optional minimum ACR level required.
648        Read more: https://docs.fief.dev/going-further/acr/
649        :param required_permissions: Optional list of permissions to check for.
650
651        **Example: Validate access token with required scopes**
652
653        ```py
654        try:
655            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
656        except FiefAccessTokenInvalid:
657            print("Invalid access token")
658        except FiefAccessTokenExpired:
659            print("Expired access token")
660        except FiefAccessTokenMissingScope:
661            print("Missing required scope")
662
663        print(access_token_info)
664        ```
665
666        **Example: Validate access token with minimum ACR level**
667
668        ```py
669        try:
670            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
671        except FiefAccessTokenInvalid:
672            print("Invalid access token")
673        except FiefAccessTokenExpired:
674            print("Expired access token")
675        except FiefAccessTokenACRTooLow:
676            print("ACR too low")
677
678        print(access_token_info)
679        ```
680
681        **Example: Validate access token with required permissions**
682
683        ```py
684        try:
685            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
686        except FiefAccessTokenInvalid:
687            print("Invalid access token")
688        except FiefAccessTokenExpired:
689            print("Expired access token")
690        except FiefAccessTokenMissingPermission:
691            print("Missing required permission")
692
693        print(access_token_info)
694        ```
695        """
696        jwks = self._get_jwks()
697        return self._validate_access_token(
698            access_token,
699            jwks,
700            required_scope=required_scope,
701            required_acr=required_acr,
702            required_permissions=required_permissions,
703        )
704
705    def userinfo(self, access_token: str) -> FiefUserInfo:
706        """
707        Return fresh `FiefUserInfo` from the Fief API using a valid access token.
708
709        :param access_token: A valid access token.
710
711        **Example:**
712
713        ```py
714        userinfo = fief.userinfo("ACCESS_TOKEN")
715        ```
716        """
717        userinfo_endpoint = self._get_endpoint_url(
718            self._get_openid_configuration(), "userinfo_endpoint"
719        )
720        with self._get_httpx_client() as client:
721            request = self._get_userinfo_request(
722                client, endpoint=userinfo_endpoint, access_token=access_token
723            )
724            response = client.send(request)
725
726            self._handle_request_error(response)
727
728            return response.json()
729
730    def update_profile(self, access_token: str, data: Dict[str, Any]) -> FiefUserInfo:
731        """
732        Update user information with the Fief API using a valid access token.
733
734        :param access_token: A valid access token.
735        :param data: A dictionary containing the data to update.
736
737        **Example: Update user field**
738
739        To update [user field](https://docs.fief.dev/getting-started/user-fields/) values, you need to nest them into a `fields` dictionary, indexed by their slug.
740
741        ```py
742        userinfo = fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
743        ```
744        """
745        update_profile_endpoint = f"{self.base_url}/api/profile"
746
747        with self._get_httpx_client() as client:
748            request = self._get_update_profile_request(
749                client,
750                endpoint=update_profile_endpoint,
751                access_token=access_token,
752                data=data,
753            )
754            response = client.send(request)
755
756            self._handle_request_error(response)
757
758            return response.json()
759
760    def change_password(self, access_token: str, new_password: str) -> FiefUserInfo:
761        """
762        Change the user password with the Fief API using a valid access token.
763
764        **An access token with an ACR of at least level 1 is required.**
765
766        :param access_token: A valid access token.
767        :param new_password: The new password.
768
769        **Example**
770
771        ```py
772        userinfo = fief.change_password("ACCESS_TOKEN", "herminetincture")
773        ```
774        """
775        change_password_profile_endpoint = f"{self.base_url}/api/password"
776
777        with self._get_httpx_client() as client:
778            request = self._get_change_password_request(
779                client,
780                endpoint=change_password_profile_endpoint,
781                access_token=access_token,
782                new_password=new_password,
783            )
784            response = client.send(request)
785
786            self._handle_request_error(response)
787
788            return response.json()
789
790    def email_change(self, access_token: str, email: str) -> FiefUserInfo:
791        """
792        Request an email change with the Fief API using a valid access token.
793
794        The user will receive a verification code on this new email address.
795        It shall be used with the method `email_verify` to complete the modification.
796
797        **An access token with an ACR of at least level 1 is required.**
798
799        :param access_token: A valid access token.
800        :param email: The new email address.
801
802        **Example**
803
804        ```py
805        userinfo = fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
806        ```
807        """
808        email_change_endpoint = f"{self.base_url}/api/email/change"
809
810        with self._get_httpx_client() as client:
811            request = self._get_email_change_request(
812                client,
813                endpoint=email_change_endpoint,
814                access_token=access_token,
815                email=email,
816            )
817            response = client.send(request)
818
819            self._handle_request_error(response)
820
821            return response.json()
822
823    def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
824        """
825        Verify the user email with the Fief API using a valid access token and verification code.
826
827        **An access token with an ACR of at least level 1 is required.**
828
829        :param access_token: A valid access token.
830        :param code: The verification code received by email.
831
832        **Example**
833
834        ```py
835        userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
836        ```
837        """
838        email_verify_endpoint = f"{self.base_url}/api/email/verify"
839
840        with self._get_httpx_client() as client:
841            request = self._get_email_verify_request(
842                client,
843                endpoint=email_verify_endpoint,
844                access_token=access_token,
845                code=code,
846            )
847            response = client.send(request)
848
849            self._handle_request_error(response)
850
851            return response.json()
852
853    def logout_url(self, redirect_uri: str) -> str:
854        """
855        Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.
856
857        **You're still responsible for clearing your own session mechanism if any.**
858
859        :param redirect_uri: A valid URL where the user will be redirected after the logout process.
860
861        **Example:**
862
863        ```py
864        logout_url = fief.logout_url("http://localhost:8000")
865        ```
866        """
867        params = {"redirect_uri": redirect_uri}
868        return f"{self.base_url}/logout?{urlencode(params)}"
869
870    @contextlib.contextmanager
871    def _get_httpx_client(self):
872        headers = {}
873        if self.host is not None:
874            headers["Host"] = self.host
875
876        with httpx.Client(
877            base_url=self.base_url, headers=headers, verify=self.verify, cert=self.cert
878        ) as client:
879            yield client
880
881    def _get_openid_configuration(self) -> Dict[str, Any]:
882        if self._openid_configuration is not None:
883            return self._openid_configuration
884
885        with self._get_httpx_client() as client:
886            request = self._get_openid_configuration_request(client)
887            response = client.send(request)
888            json = response.json()
889            self._openid_configuration = json
890            return json
891
892    def _get_jwks(self) -> jwk.JWKSet:
893        if self._jwks is not None:
894            return self._jwks
895
896        jwks_uri = self._get_endpoint_url(self._get_openid_configuration(), "jwks_uri")
897        with self._get_httpx_client() as client:
898            response = client.get(jwks_uri)
899            self._jwks = jwk.JWKSet.from_json(response.text)
900            return self._jwks
901
902    def _auth_exchange_token(
903        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
904    ) -> FiefTokenResponse:
905        token_endpoint = self._get_endpoint_url(
906            self._get_openid_configuration(), "token_endpoint"
907        )
908        with self._get_httpx_client() as client:
909            request = self._get_auth_exchange_token_request(
910                client,
911                endpoint=token_endpoint,
912                code=code,
913                redirect_uri=redirect_uri,
914                code_verifier=code_verifier,
915            )
916            response = client.send(request)
917
918            self._handle_request_error(response)
919
920            return response.json()

Sync Fief authentication client.

Fief( base_url: str, client_id: str, client_secret: Optional[str] = None, *, encryption_key: Optional[str] = None, host: Optional[str] = None, verify: Union[str, bool, ssl.SSLContext] = True, cert: Union[str, Tuple[str, Optional[str]], Tuple[str, Optional[str], Optional[str]], NoneType] = None)
499    def __init__(
500        self,
501        base_url: str,
502        client_id: str,
503        client_secret: Optional[str] = None,
504        *,
505        encryption_key: Optional[str] = None,
506        host: Optional[str] = None,
507        verify: VerifyTypes = True,
508        cert: Optional[CertTypes] = None,
509    ) -> None:
510        super().__init__(
511            base_url,
512            client_id,
513            client_secret,
514            encryption_key=encryption_key,
515            host=host,
516            verify=verify,
517            cert=cert,
518        )

Initialize the client.

Parameters
  • base_url: Base URL of your Fief tenant.
  • client_id: ID of your Fief client.
  • client_secret: Secret of your Fief client. If you're implementing a desktop app, it's not recommended to use it, since it can be easily found by the end-user in the source code. The recommended way is to use a Public client.
  • encryption_key: Encryption key of your Fief client. Necessary only if ID Token encryption is enabled.
  • **verify: Corresponds to the verify parameter of HTTPX. Useful to customize SSL connection handling.
  • **cert: Corresponds to the cert parameter of HTTPX. Useful to customize SSL connection handling.
def auth_url( self, redirect_uri: str, *, state: Optional[str] = None, scope: Optional[List[str]] = None, code_challenge: Optional[str] = None, code_challenge_method: Optional[str] = None, lang: Optional[str] = None, extras_params: Optional[Mapping[str, str]] = None) -> str:
520    def auth_url(
521        self,
522        redirect_uri: str,
523        *,
524        state: Optional[str] = None,
525        scope: Optional[List[str]] = None,
526        code_challenge: Optional[str] = None,
527        code_challenge_method: Optional[str] = None,
528        lang: Optional[str] = None,
529        extras_params: Optional[Mapping[str, str]] = None,
530    ) -> str:
531        """
532        Return an authorization URL.
533
534        :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
535        :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
536        :param scope: Optional list of scopes to ask for.
537        :param code_challenge: Optional code challenge for
538        [PKCE process](https://docs.fief.dev/going-further/pkce/).
539        :param code_challenge_method: Method used to hash the PKCE code challenge.
540        :param lang: Optional parameter to set the user locale.
541        Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`.
542        If not provided, the user locale is determined by their browser settings.
543        :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/).
544
545        **Example:**
546
547        ```py
548        auth_url = fief.auth_url("http://localhost:8000/callback", scope=["openid"])
549        ```
550        """
551        openid_configuration = self._get_openid_configuration()
552        return self._auth_url(
553            openid_configuration,
554            redirect_uri,
555            state=state,
556            scope=scope,
557            code_challenge=code_challenge,
558            code_challenge_method=code_challenge_method,
559            lang=lang,
560            extras_params=extras_params,
561        )

Return an authorization URL.

Parameters
  • redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
  • state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
  • scope: Optional list of scopes to ask for.
  • code_challenge: Optional code challenge for PKCE process.
  • code_challenge_method: Method used to hash the PKCE code challenge.
  • lang: Optional parameter to set the user locale. Should be a valid RFC 3066 language identifier, like fr or pt-PT. If not provided, the user locale is determined by their browser settings.
  • **extras_params: Optional dictionary containing specific parameters.

Example:

auth_url = fief.auth_url("http://localhost:8000/callback", scope=["openid"])
def auth_callback( self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None) -> Tuple[FiefTokenResponse, FiefUserInfo]:
563    def auth_callback(
564        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
565    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
566        """
567        Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code.
568
569        :param code: The authorization code.
570        :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL.
571        :param code_verifier:  The raw
572        [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization.
573
574        **Example:**
575
576        ```py
577        tokens, userinfo = fief.auth_callback("CODE", "http://localhost:8000/callback")
578        ```
579        """
580        token_response = self._auth_exchange_token(
581            code, redirect_uri, code_verifier=code_verifier
582        )
583        jwks = self._get_jwks()
584        userinfo = self._decode_id_token(
585            token_response["id_token"],
586            jwks,
587            code=code,
588            access_token=token_response.get("access_token"),
589        )
590        return token_response, userinfo

Return a FiefTokenResponse and FiefUserInfo in exchange of an authorization code.

Parameters
  • code: The authorization code.
  • redirect_uri: The exact same redirect_uri you passed to the authorization URL.
  • code_verifier: The raw PKCE code used to generate the code challenge during authorization.

Example:

tokens, userinfo = fief.auth_callback("CODE", "http://localhost:8000/callback")
def auth_refresh_token( self, refresh_token: str, *, scope: Optional[List[str]] = None) -> Tuple[FiefTokenResponse, FiefUserInfo]:
592    def auth_refresh_token(
593        self, refresh_token: str, *, scope: Optional[List[str]] = None
594    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
595        """
596        Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token
597
598        :param refresh_token: A valid refresh token.
599        :param scope: Optional list of scopes to ask for.
600        If not provided, the access token will share the same list of scopes as requested the first time.
601        Otherwise, it should be a subset of the original list of scopes.
602
603        **Example:**
604
605        ```py
606        tokens, userinfo = fief.auth_refresh_token("REFRESH_TOKEN")
607        ```
608        """
609        token_endpoint = self._get_endpoint_url(
610            self._get_openid_configuration(), "token_endpoint"
611        )
612        with self._get_httpx_client() as client:
613            request = self._get_auth_refresh_token_request(
614                client,
615                endpoint=token_endpoint,
616                refresh_token=refresh_token,
617                scope=scope,
618            )
619            response = client.send(request)
620
621            self._handle_request_error(response)
622
623            token_response = response.json()
624        jwks = self._get_jwks()
625        userinfo = self._decode_id_token(
626            token_response["id_token"],
627            jwks,
628            access_token=token_response.get("access_token"),
629        )
630        return token_response, userinfo

Return fresh FiefTokenResponse and FiefUserInfo in exchange of a refresh token

Parameters
  • refresh_token: A valid refresh token.
  • scope: Optional list of scopes to ask for. If not provided, the access token will share the same list of scopes as requested the first time. Otherwise, it should be a subset of the original list of scopes.

Example:

tokens, userinfo = fief.auth_refresh_token("REFRESH_TOKEN")
def validate_access_token( self, access_token: str, *, required_scope: Optional[List[str]] = None, required_acr: Optional[FiefACR] = None, required_permissions: Optional[List[str]] = None) -> FiefAccessTokenInfo:
632    def validate_access_token(
633        self,
634        access_token: str,
635        *,
636        required_scope: Optional[List[str]] = None,
637        required_acr: Optional[FiefACR] = None,
638        required_permissions: Optional[List[str]] = None,
639    ) -> FiefAccessTokenInfo:
640        """
641        Check if an access token is valid and optionally that it has a required list of scopes,
642        or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/).
643        Returns a `FiefAccessTokenInfo`.
644
645        :param access_token: The access token to validate.
646        :param required_scope: Optional list of scopes to check for.
647        :param required_acr: Optional minimum ACR level required.
648        Read more: https://docs.fief.dev/going-further/acr/
649        :param required_permissions: Optional list of permissions to check for.
650
651        **Example: Validate access token with required scopes**
652
653        ```py
654        try:
655            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
656        except FiefAccessTokenInvalid:
657            print("Invalid access token")
658        except FiefAccessTokenExpired:
659            print("Expired access token")
660        except FiefAccessTokenMissingScope:
661            print("Missing required scope")
662
663        print(access_token_info)
664        ```
665
666        **Example: Validate access token with minimum ACR level**
667
668        ```py
669        try:
670            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
671        except FiefAccessTokenInvalid:
672            print("Invalid access token")
673        except FiefAccessTokenExpired:
674            print("Expired access token")
675        except FiefAccessTokenACRTooLow:
676            print("ACR too low")
677
678        print(access_token_info)
679        ```
680
681        **Example: Validate access token with required permissions**
682
683        ```py
684        try:
685            access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
686        except FiefAccessTokenInvalid:
687            print("Invalid access token")
688        except FiefAccessTokenExpired:
689            print("Expired access token")
690        except FiefAccessTokenMissingPermission:
691            print("Missing required permission")
692
693        print(access_token_info)
694        ```
695        """
696        jwks = self._get_jwks()
697        return self._validate_access_token(
698            access_token,
699            jwks,
700            required_scope=required_scope,
701            required_acr=required_acr,
702            required_permissions=required_permissions,
703        )

Check if an access token is valid and optionally that it has a required list of scopes, or a required list of permissions. Returns a FiefAccessTokenInfo.

Parameters
  • access_token: The access token to validate.
  • required_scope: Optional list of scopes to check for.
  • required_acr: Optional minimum ACR level required. Read more: https://docs.fief.dev/going-further/acr/
  • required_permissions: Optional list of permissions to check for.

Example: Validate access token with required scopes

try:
    access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenMissingScope:
    print("Missing required scope")

print(access_token_info)

Example: Validate access token with minimum ACR level

try:
    access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenACRTooLow:
    print("ACR too low")

print(access_token_info)

Example: Validate access token with required permissions

try:
    access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenMissingPermission:
    print("Missing required permission")

print(access_token_info)
def userinfo(self, access_token: str) -> FiefUserInfo:
705    def userinfo(self, access_token: str) -> FiefUserInfo:
706        """
707        Return fresh `FiefUserInfo` from the Fief API using a valid access token.
708
709        :param access_token: A valid access token.
710
711        **Example:**
712
713        ```py
714        userinfo = fief.userinfo("ACCESS_TOKEN")
715        ```
716        """
717        userinfo_endpoint = self._get_endpoint_url(
718            self._get_openid_configuration(), "userinfo_endpoint"
719        )
720        with self._get_httpx_client() as client:
721            request = self._get_userinfo_request(
722                client, endpoint=userinfo_endpoint, access_token=access_token
723            )
724            response = client.send(request)
725
726            self._handle_request_error(response)
727
728            return response.json()

Return fresh FiefUserInfo from the Fief API using a valid access token.

Parameters
  • access_token: A valid access token.

Example:

userinfo = fief.userinfo("ACCESS_TOKEN")
def update_profile( self, access_token: str, data: Dict[str, Any]) -> FiefUserInfo:
730    def update_profile(self, access_token: str, data: Dict[str, Any]) -> FiefUserInfo:
731        """
732        Update user information with the Fief API using a valid access token.
733
734        :param access_token: A valid access token.
735        :param data: A dictionary containing the data to update.
736
737        **Example: Update user field**
738
739        To update [user field](https://docs.fief.dev/getting-started/user-fields/) values, you need to nest them into a `fields` dictionary, indexed by their slug.
740
741        ```py
742        userinfo = fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
743        ```
744        """
745        update_profile_endpoint = f"{self.base_url}/api/profile"
746
747        with self._get_httpx_client() as client:
748            request = self._get_update_profile_request(
749                client,
750                endpoint=update_profile_endpoint,
751                access_token=access_token,
752                data=data,
753            )
754            response = client.send(request)
755
756            self._handle_request_error(response)
757
758            return response.json()

Update user information with the Fief API using a valid access token.

Parameters
  • access_token: A valid access token.
  • data: A dictionary containing the data to update.

Example: Update user field

To update user field values, you need to nest them into a fields dictionary, indexed by their slug.

userinfo = fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
def change_password( self, access_token: str, new_password: str) -> FiefUserInfo:
760    def change_password(self, access_token: str, new_password: str) -> FiefUserInfo:
761        """
762        Change the user password with the Fief API using a valid access token.
763
764        **An access token with an ACR of at least level 1 is required.**
765
766        :param access_token: A valid access token.
767        :param new_password: The new password.
768
769        **Example**
770
771        ```py
772        userinfo = fief.change_password("ACCESS_TOKEN", "herminetincture")
773        ```
774        """
775        change_password_profile_endpoint = f"{self.base_url}/api/password"
776
777        with self._get_httpx_client() as client:
778            request = self._get_change_password_request(
779                client,
780                endpoint=change_password_profile_endpoint,
781                access_token=access_token,
782                new_password=new_password,
783            )
784            response = client.send(request)
785
786            self._handle_request_error(response)
787
788            return response.json()

Change the user password with the Fief API using a valid access token.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • new_password: The new password.

Example

userinfo = fief.change_password("ACCESS_TOKEN", "herminetincture")
def email_change(self, access_token: str, email: str) -> FiefUserInfo:
790    def email_change(self, access_token: str, email: str) -> FiefUserInfo:
791        """
792        Request an email change with the Fief API using a valid access token.
793
794        The user will receive a verification code on this new email address.
795        It shall be used with the method `email_verify` to complete the modification.
796
797        **An access token with an ACR of at least level 1 is required.**
798
799        :param access_token: A valid access token.
800        :param email: The new email address.
801
802        **Example**
803
804        ```py
805        userinfo = fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
806        ```
807        """
808        email_change_endpoint = f"{self.base_url}/api/email/change"
809
810        with self._get_httpx_client() as client:
811            request = self._get_email_change_request(
812                client,
813                endpoint=email_change_endpoint,
814                access_token=access_token,
815                email=email,
816            )
817            response = client.send(request)
818
819            self._handle_request_error(response)
820
821            return response.json()

Request an email change with the Fief API using a valid access token.

The user will receive a verification code on this new email address. It shall be used with the method email_verify to complete the modification.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • email: The new email address.

Example

userinfo = fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
823    def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
824        """
825        Verify the user email with the Fief API using a valid access token and verification code.
826
827        **An access token with an ACR of at least level 1 is required.**
828
829        :param access_token: A valid access token.
830        :param code: The verification code received by email.
831
832        **Example**
833
834        ```py
835        userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
836        ```
837        """
838        email_verify_endpoint = f"{self.base_url}/api/email/verify"
839
840        with self._get_httpx_client() as client:
841            request = self._get_email_verify_request(
842                client,
843                endpoint=email_verify_endpoint,
844                access_token=access_token,
845                code=code,
846            )
847            response = client.send(request)
848
849            self._handle_request_error(response)
850
851            return response.json()

Verify the user email with the Fief API using a valid access token and verification code.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • code: The verification code received by email.

Example

userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
def logout_url(self, redirect_uri: str) -> str:
853    def logout_url(self, redirect_uri: str) -> str:
854        """
855        Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.
856
857        **You're still responsible for clearing your own session mechanism if any.**
858
859        :param redirect_uri: A valid URL where the user will be redirected after the logout process.
860
861        **Example:**
862
863        ```py
864        logout_url = fief.logout_url("http://localhost:8000")
865        ```
866        """
867        params = {"redirect_uri": redirect_uri}
868        return f"{self.base_url}/logout?{urlencode(params)}"

Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.

You're still responsible for clearing your own session mechanism if any.

Parameters
  • redirect_uri: A valid URL where the user will be redirected after the logout process.

Example:

logout_url = fief.logout_url("http://localhost:8000")
Inherited Members
fief_client.client.BaseFief
base_url
client_id
client_secret
encryption_key
host
verify
cert
class FiefACR(builtins.str, enum.Enum):
18class FiefACR(str, Enum):
19    """
20    List of defined Authentication Context Class Reference.
21    """
22
23    LEVEL_ZERO = "0"
24    """Level 0. No authentication was performed, a previous session was used."""
25    LEVEL_ONE = "1"
26    """Level 1. Password authentication was performed."""
27
28    def __lt__(self, other: object) -> bool:
29        return self._compare(other, True, True)
30
31    def __le__(self, other: object) -> bool:
32        return self._compare(other, False, True)
33
34    def __gt__(self, other: object) -> bool:
35        return self._compare(other, True, False)
36
37    def __ge__(self, other: object) -> bool:
38        return self._compare(other, False, False)
39
40    def _compare(self, other: object, strict: bool, asc: bool) -> bool:
41        if not isinstance(other, FiefACR):
42            return NotImplemented  # pragma: no cover
43
44        if self == other:
45            return not strict
46
47        for elem in FiefACR:
48            if self == elem:
49                return asc
50            elif other == elem:
51                return not asc
52        raise RuntimeError()  # pragma: no cover

List of defined Authentication Context Class Reference.

LEVEL_ZERO = <FiefACR.LEVEL_ZERO: '0'>

Level 0. No authentication was performed, a previous session was used.

LEVEL_ONE = <FiefACR.LEVEL_ONE: '1'>

Level 1. Password authentication was performed.

Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class FiefAsync(fief_client.client.BaseFief):
 923class FiefAsync(BaseFief):
 924    """Async Fief authentication client."""
 925
 926    def __init__(
 927        self,
 928        base_url: str,
 929        client_id: str,
 930        client_secret: Optional[str] = None,
 931        *,
 932        encryption_key: Optional[str] = None,
 933        host: Optional[str] = None,
 934        verify: VerifyTypes = True,
 935        cert: Optional[CertTypes] = None,
 936    ) -> None:
 937        super().__init__(
 938            base_url,
 939            client_id,
 940            client_secret,
 941            encryption_key=encryption_key,
 942            host=host,
 943            verify=verify,
 944            cert=cert,
 945        )
 946
 947    async def auth_url(
 948        self,
 949        redirect_uri: str,
 950        *,
 951        state: Optional[str] = None,
 952        scope: Optional[List[str]] = None,
 953        code_challenge: Optional[str] = None,
 954        code_challenge_method: Optional[str] = None,
 955        lang: Optional[str] = None,
 956        extras_params: Optional[Mapping[str, str]] = None,
 957    ) -> str:
 958        """
 959        Return an authorization URL.
 960
 961        :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
 962        :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
 963        :param scope: Optional list of scopes to ask for.
 964        :param code_challenge: Optional code challenge for
 965        [PKCE process](https://docs.fief.dev/going-further/pkce/).
 966        :param code_challenge_method: Method used to hash the PKCE code challenge.
 967        :param lang: Optional parameter to set the user locale.
 968        Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`.
 969        If not provided, the user locale is determined by their browser settings.
 970        :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/).
 971
 972        **Example:**
 973
 974        ```py
 975        auth_url = await fief.auth_url("http://localhost:8000/callback", scope=["openid"])
 976        ```
 977        """
 978        openid_configuration = await self._get_openid_configuration()
 979        return self._auth_url(
 980            openid_configuration,
 981            redirect_uri,
 982            state=state,
 983            scope=scope,
 984            code_challenge=code_challenge,
 985            code_challenge_method=code_challenge_method,
 986            lang=lang,
 987            extras_params=extras_params,
 988        )
 989
 990    async def auth_callback(
 991        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
 992    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
 993        """
 994        Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code.
 995
 996        :param code: The authorization code.
 997        :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL.
 998        :param code_verifier:  The raw
 999        [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization.
1000
1001        **Example:**
1002
1003        ```py
1004        tokens, userinfo = await fief.auth_callback("CODE", "http://localhost:8000/callback")
1005        ```
1006        """
1007        token_response = await self._auth_exchange_token(
1008            code, redirect_uri, code_verifier=code_verifier
1009        )
1010        jwks = await self._get_jwks()
1011        userinfo = self._decode_id_token(
1012            token_response["id_token"],
1013            jwks,
1014            code=code,
1015            access_token=token_response.get("access_token"),
1016        )
1017        return token_response, userinfo
1018
1019    async def auth_refresh_token(
1020        self, refresh_token: str, *, scope: Optional[List[str]] = None
1021    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
1022        """
1023        Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token
1024
1025        :param refresh_token: A valid refresh token.
1026        :param scope: Optional list of scopes to ask for.
1027        If not provided, the access token will share the same list of scopes as requested the first time.
1028        Otherwise, it should be a subset of the original list of scopes.
1029
1030        **Example:**
1031
1032        ```py
1033        tokens, userinfo = await fief.auth_refresh_token("REFRESH_TOKEN")
1034        ```
1035        """
1036        token_endpoint = self._get_endpoint_url(
1037            await self._get_openid_configuration(), "token_endpoint"
1038        )
1039        async with self._get_httpx_client() as client:
1040            request = self._get_auth_refresh_token_request(
1041                client,
1042                endpoint=token_endpoint,
1043                refresh_token=refresh_token,
1044                scope=scope,
1045            )
1046            response = await client.send(request)
1047
1048            self._handle_request_error(response)
1049
1050            token_response = response.json()
1051
1052        jwks = await self._get_jwks()
1053        userinfo = self._decode_id_token(
1054            token_response["id_token"],
1055            jwks,
1056            access_token=token_response.get("access_token"),
1057        )
1058        return token_response, userinfo
1059
1060    async def validate_access_token(
1061        self,
1062        access_token: str,
1063        *,
1064        required_scope: Optional[List[str]] = None,
1065        required_acr: Optional[FiefACR] = None,
1066        required_permissions: Optional[List[str]] = None,
1067    ) -> FiefAccessTokenInfo:
1068        """
1069        Check if an access token is valid and optionally that it has a required list of scopes,
1070        or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/).
1071        Returns a `FiefAccessTokenInfo`.
1072
1073        :param access_token: The access token to validate.
1074        :param required_scope: Optional list of scopes to check for.
1075        :param required_acr: Optional minimum ACR level required.
1076        Read more: https://docs.fief.dev/going-further/acr/
1077        :param required_permissions: Optional list of permissions to check for.
1078
1079        **Example: Validate access token with required scopes**
1080
1081        ```py
1082        try:
1083            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
1084        except FiefAccessTokenInvalid:
1085            print("Invalid access token")
1086        except FiefAccessTokenExpired:
1087            print("Expired access token")
1088        except FiefAccessTokenMissingScope:
1089            print("Missing required scope")
1090
1091        print(access_token_info)
1092        ```
1093
1094        **Example: Validate access token with minimum ACR level**
1095
1096        ```py
1097        try:
1098            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
1099        except FiefAccessTokenInvalid:
1100            print("Invalid access token")
1101        except FiefAccessTokenExpired:
1102            print("Expired access token")
1103        except FiefAccessTokenACRTooLow:
1104            print("ACR too low")
1105
1106        print(access_token_info)
1107        ```
1108
1109        **Example: Validate access token with required permissions**
1110
1111        ```py
1112        try:
1113            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
1114        except FiefAccessTokenInvalid:
1115            print("Invalid access token")
1116        except FiefAccessTokenExpired:
1117            print("Expired access token")
1118        except FiefAccessTokenMissingPermission:
1119            print("Missing required permission")
1120
1121        print(access_token_info)
1122        ```
1123        """
1124        jwks = await self._get_jwks()
1125        return self._validate_access_token(
1126            access_token,
1127            jwks,
1128            required_scope=required_scope,
1129            required_acr=required_acr,
1130            required_permissions=required_permissions,
1131        )
1132
1133    async def userinfo(self, access_token: str) -> FiefUserInfo:
1134        """
1135        Return fresh `FiefUserInfo` from the Fief API using a valid access token.
1136
1137        :param access_token: A valid access token.
1138
1139        **Example:**
1140
1141        ```py
1142        userinfo = await fief.userinfo("ACCESS_TOKEN")
1143        ```
1144        """
1145        userinfo_endpoint = self._get_endpoint_url(
1146            await self._get_openid_configuration(), "userinfo_endpoint"
1147        )
1148        async with self._get_httpx_client() as client:
1149            request = self._get_userinfo_request(
1150                client, endpoint=userinfo_endpoint, access_token=access_token
1151            )
1152            response = await client.send(request)
1153
1154            self._handle_request_error(response)
1155
1156            return response.json()
1157
1158    async def update_profile(
1159        self, access_token: str, data: Dict[str, Any]
1160    ) -> FiefUserInfo:
1161        """
1162        Update user information with the Fief API using a valid access token.
1163
1164        :param access_token: A valid access token.
1165        :param data: A dictionary containing the data to update.
1166
1167        **Example: Update user field**
1168
1169        To update [user field](https://docs.fief.dev/getting-started/user-fields/) values, you need to nest them into a `fields` dictionary, indexed by their slug.
1170
1171        ```py
1172        userinfo = await fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
1173        ```
1174        """
1175        update_profile_endpoint = f"{self.base_url}/api/profile"
1176
1177        async with self._get_httpx_client() as client:
1178            request = self._get_update_profile_request(
1179                client,
1180                endpoint=update_profile_endpoint,
1181                access_token=access_token,
1182                data=data,
1183            )
1184            response = await client.send(request)
1185
1186            self._handle_request_error(response)
1187
1188            return response.json()
1189
1190    async def change_password(
1191        self, access_token: str, new_password: str
1192    ) -> FiefUserInfo:
1193        """
1194        Change the user password with the Fief API using a valid access token.
1195
1196        **An access token with an ACR of at least level 1 is required.**
1197
1198        :param access_token: A valid access token.
1199        :param new_password: The new password.
1200
1201        **Example**
1202
1203        ```py
1204        userinfo = await fief.change_password("ACCESS_TOKEN", "herminetincture")
1205        ```
1206        """
1207        change_password_profile_endpoint = f"{self.base_url}/api/password"
1208
1209        async with self._get_httpx_client() as client:
1210            request = self._get_change_password_request(
1211                client,
1212                endpoint=change_password_profile_endpoint,
1213                access_token=access_token,
1214                new_password=new_password,
1215            )
1216            response = await client.send(request)
1217
1218            self._handle_request_error(response)
1219
1220            return response.json()
1221
1222    async def email_change(self, access_token: str, email: str) -> FiefUserInfo:
1223        """
1224        Request an email change with the Fief API using a valid access token.
1225
1226        The user will receive a verification code on this new email address.
1227        It shall be used with the method `email_verify` to complete the modification.
1228
1229        **An access token with an ACR of at least level 1 is required.**
1230
1231        :param access_token: A valid access token.
1232        :param email: The new email address.
1233
1234        **Example**
1235
1236        ```py
1237        userinfo = await fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
1238        ```
1239        """
1240        email_change_endpoint = f"{self.base_url}/api/email/change"
1241
1242        async with self._get_httpx_client() as client:
1243            request = self._get_email_change_request(
1244                client,
1245                endpoint=email_change_endpoint,
1246                access_token=access_token,
1247                email=email,
1248            )
1249            response = await client.send(request)
1250
1251            self._handle_request_error(response)
1252
1253            return response.json()
1254
1255    async def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
1256        """
1257        Verify the user email with the Fief API using a valid access token and verification code.
1258
1259        **An access token with an ACR of at least level 1 is required.**
1260
1261        :param access_token: A valid access token.
1262        :param code: The verification code received by email.
1263
1264        **Example**
1265
1266        ```py
1267        userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
1268        ```
1269        """
1270        email_verify_endpoint = f"{self.base_url}/api/email/verify"
1271
1272        async with self._get_httpx_client() as client:
1273            request = self._get_email_verify_request(
1274                client,
1275                endpoint=email_verify_endpoint,
1276                access_token=access_token,
1277                code=code,
1278            )
1279            response = await client.send(request)
1280
1281            self._handle_request_error(response)
1282
1283            return response.json()
1284
1285    async def logout_url(self, redirect_uri: str) -> str:
1286        """
1287        Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.
1288
1289        **You're still responsible for clearing your own session mechanism if any.**
1290
1291        :param redirect_uri: A valid URL where the user will be redirected after the logout process:
1292
1293        **Example:**
1294
1295        ```py
1296        logout_url = await fief.logout_url("http://localhost:8000")
1297        ```
1298        """
1299        params = {"redirect_uri": redirect_uri}
1300        return f"{self.base_url}/logout?{urlencode(params)}"
1301
1302    @contextlib.asynccontextmanager
1303    async def _get_httpx_client(self):
1304        headers = {}
1305        if self.host is not None:
1306            headers["Host"] = self.host
1307
1308        async with httpx.AsyncClient(
1309            base_url=self.base_url, headers=headers, verify=self.verify, cert=self.cert
1310        ) as client:
1311            yield client
1312
1313    async def _get_openid_configuration(self) -> Dict[str, Any]:
1314        if self._openid_configuration is not None:
1315            return self._openid_configuration
1316
1317        async with self._get_httpx_client() as client:
1318            request = self._get_openid_configuration_request(client)
1319            response = await client.send(request)
1320            json = response.json()
1321            self._openid_configuration = json
1322            return json
1323
1324    async def _get_jwks(self) -> jwk.JWKSet:
1325        if self._jwks is not None:
1326            return self._jwks
1327
1328        jwks_uri = self._get_endpoint_url(
1329            await self._get_openid_configuration(), "jwks_uri"
1330        )
1331        async with self._get_httpx_client() as client:
1332            response = await client.get(jwks_uri)
1333            self._jwks = jwk.JWKSet.from_json(response.text)
1334            return self._jwks
1335
1336    async def _auth_exchange_token(
1337        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
1338    ) -> FiefTokenResponse:
1339        token_endpoint = self._get_endpoint_url(
1340            await self._get_openid_configuration(), "token_endpoint"
1341        )
1342        async with self._get_httpx_client() as client:
1343            request = self._get_auth_exchange_token_request(
1344                client,
1345                endpoint=token_endpoint,
1346                code=code,
1347                redirect_uri=redirect_uri,
1348                code_verifier=code_verifier,
1349            )
1350            response = await client.send(request)
1351
1352            self._handle_request_error(response)
1353
1354            return response.json()

Async Fief authentication client.

FiefAsync( base_url: str, client_id: str, client_secret: Optional[str] = None, *, encryption_key: Optional[str] = None, host: Optional[str] = None, verify: Union[str, bool, ssl.SSLContext] = True, cert: Union[str, Tuple[str, Optional[str]], Tuple[str, Optional[str], Optional[str]], NoneType] = None)
926    def __init__(
927        self,
928        base_url: str,
929        client_id: str,
930        client_secret: Optional[str] = None,
931        *,
932        encryption_key: Optional[str] = None,
933        host: Optional[str] = None,
934        verify: VerifyTypes = True,
935        cert: Optional[CertTypes] = None,
936    ) -> None:
937        super().__init__(
938            base_url,
939            client_id,
940            client_secret,
941            encryption_key=encryption_key,
942            host=host,
943            verify=verify,
944            cert=cert,
945        )

Initialize the client.

Parameters
  • base_url: Base URL of your Fief tenant.
  • client_id: ID of your Fief client.
  • client_secret: Secret of your Fief client. If you're implementing a desktop app, it's not recommended to use it, since it can be easily found by the end-user in the source code. The recommended way is to use a Public client.
  • encryption_key: Encryption key of your Fief client. Necessary only if ID Token encryption is enabled.
  • **verify: Corresponds to the verify parameter of HTTPX. Useful to customize SSL connection handling.
  • **cert: Corresponds to the cert parameter of HTTPX. Useful to customize SSL connection handling.
async def auth_url( self, redirect_uri: str, *, state: Optional[str] = None, scope: Optional[List[str]] = None, code_challenge: Optional[str] = None, code_challenge_method: Optional[str] = None, lang: Optional[str] = None, extras_params: Optional[Mapping[str, str]] = None) -> str:
947    async def auth_url(
948        self,
949        redirect_uri: str,
950        *,
951        state: Optional[str] = None,
952        scope: Optional[List[str]] = None,
953        code_challenge: Optional[str] = None,
954        code_challenge_method: Optional[str] = None,
955        lang: Optional[str] = None,
956        extras_params: Optional[Mapping[str, str]] = None,
957    ) -> str:
958        """
959        Return an authorization URL.
960
961        :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
962        :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
963        :param scope: Optional list of scopes to ask for.
964        :param code_challenge: Optional code challenge for
965        [PKCE process](https://docs.fief.dev/going-further/pkce/).
966        :param code_challenge_method: Method used to hash the PKCE code challenge.
967        :param lang: Optional parameter to set the user locale.
968        Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`.
969        If not provided, the user locale is determined by their browser settings.
970        :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/).
971
972        **Example:**
973
974        ```py
975        auth_url = await fief.auth_url("http://localhost:8000/callback", scope=["openid"])
976        ```
977        """
978        openid_configuration = await self._get_openid_configuration()
979        return self._auth_url(
980            openid_configuration,
981            redirect_uri,
982            state=state,
983            scope=scope,
984            code_challenge=code_challenge,
985            code_challenge_method=code_challenge_method,
986            lang=lang,
987            extras_params=extras_params,
988        )

Return an authorization URL.

Parameters
  • redirect_uri: Your callback URI where the user will be redirected after Fief authentication.
  • state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information.
  • scope: Optional list of scopes to ask for.
  • code_challenge: Optional code challenge for PKCE process.
  • code_challenge_method: Method used to hash the PKCE code challenge.
  • lang: Optional parameter to set the user locale. Should be a valid RFC 3066 language identifier, like fr or pt-PT. If not provided, the user locale is determined by their browser settings.
  • **extras_params: Optional dictionary containing specific parameters.

Example:

auth_url = await fief.auth_url("http://localhost:8000/callback", scope=["openid"])
async def auth_callback( self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None) -> Tuple[FiefTokenResponse, FiefUserInfo]:
 990    async def auth_callback(
 991        self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None
 992    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
 993        """
 994        Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code.
 995
 996        :param code: The authorization code.
 997        :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL.
 998        :param code_verifier:  The raw
 999        [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization.
1000
1001        **Example:**
1002
1003        ```py
1004        tokens, userinfo = await fief.auth_callback("CODE", "http://localhost:8000/callback")
1005        ```
1006        """
1007        token_response = await self._auth_exchange_token(
1008            code, redirect_uri, code_verifier=code_verifier
1009        )
1010        jwks = await self._get_jwks()
1011        userinfo = self._decode_id_token(
1012            token_response["id_token"],
1013            jwks,
1014            code=code,
1015            access_token=token_response.get("access_token"),
1016        )
1017        return token_response, userinfo

Return a FiefTokenResponse and FiefUserInfo in exchange of an authorization code.

Parameters
  • code: The authorization code.
  • redirect_uri: The exact same redirect_uri you passed to the authorization URL.
  • code_verifier: The raw PKCE code used to generate the code challenge during authorization.

Example:

tokens, userinfo = await fief.auth_callback("CODE", "http://localhost:8000/callback")
async def auth_refresh_token( self, refresh_token: str, *, scope: Optional[List[str]] = None) -> Tuple[FiefTokenResponse, FiefUserInfo]:
1019    async def auth_refresh_token(
1020        self, refresh_token: str, *, scope: Optional[List[str]] = None
1021    ) -> Tuple[FiefTokenResponse, FiefUserInfo]:
1022        """
1023        Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token
1024
1025        :param refresh_token: A valid refresh token.
1026        :param scope: Optional list of scopes to ask for.
1027        If not provided, the access token will share the same list of scopes as requested the first time.
1028        Otherwise, it should be a subset of the original list of scopes.
1029
1030        **Example:**
1031
1032        ```py
1033        tokens, userinfo = await fief.auth_refresh_token("REFRESH_TOKEN")
1034        ```
1035        """
1036        token_endpoint = self._get_endpoint_url(
1037            await self._get_openid_configuration(), "token_endpoint"
1038        )
1039        async with self._get_httpx_client() as client:
1040            request = self._get_auth_refresh_token_request(
1041                client,
1042                endpoint=token_endpoint,
1043                refresh_token=refresh_token,
1044                scope=scope,
1045            )
1046            response = await client.send(request)
1047
1048            self._handle_request_error(response)
1049
1050            token_response = response.json()
1051
1052        jwks = await self._get_jwks()
1053        userinfo = self._decode_id_token(
1054            token_response["id_token"],
1055            jwks,
1056            access_token=token_response.get("access_token"),
1057        )
1058        return token_response, userinfo

Return fresh FiefTokenResponse and FiefUserInfo in exchange of a refresh token

Parameters
  • refresh_token: A valid refresh token.
  • scope: Optional list of scopes to ask for. If not provided, the access token will share the same list of scopes as requested the first time. Otherwise, it should be a subset of the original list of scopes.

Example:

tokens, userinfo = await fief.auth_refresh_token("REFRESH_TOKEN")
async def validate_access_token( self, access_token: str, *, required_scope: Optional[List[str]] = None, required_acr: Optional[FiefACR] = None, required_permissions: Optional[List[str]] = None) -> FiefAccessTokenInfo:
1060    async def validate_access_token(
1061        self,
1062        access_token: str,
1063        *,
1064        required_scope: Optional[List[str]] = None,
1065        required_acr: Optional[FiefACR] = None,
1066        required_permissions: Optional[List[str]] = None,
1067    ) -> FiefAccessTokenInfo:
1068        """
1069        Check if an access token is valid and optionally that it has a required list of scopes,
1070        or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/).
1071        Returns a `FiefAccessTokenInfo`.
1072
1073        :param access_token: The access token to validate.
1074        :param required_scope: Optional list of scopes to check for.
1075        :param required_acr: Optional minimum ACR level required.
1076        Read more: https://docs.fief.dev/going-further/acr/
1077        :param required_permissions: Optional list of permissions to check for.
1078
1079        **Example: Validate access token with required scopes**
1080
1081        ```py
1082        try:
1083            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
1084        except FiefAccessTokenInvalid:
1085            print("Invalid access token")
1086        except FiefAccessTokenExpired:
1087            print("Expired access token")
1088        except FiefAccessTokenMissingScope:
1089            print("Missing required scope")
1090
1091        print(access_token_info)
1092        ```
1093
1094        **Example: Validate access token with minimum ACR level**
1095
1096        ```py
1097        try:
1098            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
1099        except FiefAccessTokenInvalid:
1100            print("Invalid access token")
1101        except FiefAccessTokenExpired:
1102            print("Expired access token")
1103        except FiefAccessTokenACRTooLow:
1104            print("ACR too low")
1105
1106        print(access_token_info)
1107        ```
1108
1109        **Example: Validate access token with required permissions**
1110
1111        ```py
1112        try:
1113            access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
1114        except FiefAccessTokenInvalid:
1115            print("Invalid access token")
1116        except FiefAccessTokenExpired:
1117            print("Expired access token")
1118        except FiefAccessTokenMissingPermission:
1119            print("Missing required permission")
1120
1121        print(access_token_info)
1122        ```
1123        """
1124        jwks = await self._get_jwks()
1125        return self._validate_access_token(
1126            access_token,
1127            jwks,
1128            required_scope=required_scope,
1129            required_acr=required_acr,
1130            required_permissions=required_permissions,
1131        )

Check if an access token is valid and optionally that it has a required list of scopes, or a required list of permissions. Returns a FiefAccessTokenInfo.

Parameters
  • access_token: The access token to validate.
  • required_scope: Optional list of scopes to check for.
  • required_acr: Optional minimum ACR level required. Read more: https://docs.fief.dev/going-further/acr/
  • required_permissions: Optional list of permissions to check for.

Example: Validate access token with required scopes

try:
    access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"])
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenMissingScope:
    print("Missing required scope")

print(access_token_info)

Example: Validate access token with minimum ACR level

try:
    access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE)
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenACRTooLow:
    print("ACR too low")

print(access_token_info)

Example: Validate access token with required permissions

try:
    access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"])
except FiefAccessTokenInvalid:
    print("Invalid access token")
except FiefAccessTokenExpired:
    print("Expired access token")
except FiefAccessTokenMissingPermission:
    print("Missing required permission")

print(access_token_info)
async def userinfo(self, access_token: str) -> FiefUserInfo:
1133    async def userinfo(self, access_token: str) -> FiefUserInfo:
1134        """
1135        Return fresh `FiefUserInfo` from the Fief API using a valid access token.
1136
1137        :param access_token: A valid access token.
1138
1139        **Example:**
1140
1141        ```py
1142        userinfo = await fief.userinfo("ACCESS_TOKEN")
1143        ```
1144        """
1145        userinfo_endpoint = self._get_endpoint_url(
1146            await self._get_openid_configuration(), "userinfo_endpoint"
1147        )
1148        async with self._get_httpx_client() as client:
1149            request = self._get_userinfo_request(
1150                client, endpoint=userinfo_endpoint, access_token=access_token
1151            )
1152            response = await client.send(request)
1153
1154            self._handle_request_error(response)
1155
1156            return response.json()

Return fresh FiefUserInfo from the Fief API using a valid access token.

Parameters
  • access_token: A valid access token.

Example:

userinfo = await fief.userinfo("ACCESS_TOKEN")
async def update_profile( self, access_token: str, data: Dict[str, Any]) -> FiefUserInfo:
1158    async def update_profile(
1159        self, access_token: str, data: Dict[str, Any]
1160    ) -> FiefUserInfo:
1161        """
1162        Update user information with the Fief API using a valid access token.
1163
1164        :param access_token: A valid access token.
1165        :param data: A dictionary containing the data to update.
1166
1167        **Example: Update user field**
1168
1169        To update [user field](https://docs.fief.dev/getting-started/user-fields/) values, you need to nest them into a `fields` dictionary, indexed by their slug.
1170
1171        ```py
1172        userinfo = await fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
1173        ```
1174        """
1175        update_profile_endpoint = f"{self.base_url}/api/profile"
1176
1177        async with self._get_httpx_client() as client:
1178            request = self._get_update_profile_request(
1179                client,
1180                endpoint=update_profile_endpoint,
1181                access_token=access_token,
1182                data=data,
1183            )
1184            response = await client.send(request)
1185
1186            self._handle_request_error(response)
1187
1188            return response.json()

Update user information with the Fief API using a valid access token.

Parameters
  • access_token: A valid access token.
  • data: A dictionary containing the data to update.

Example: Update user field

To update user field values, you need to nest them into a fields dictionary, indexed by their slug.

userinfo = await fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } })
async def change_password( self, access_token: str, new_password: str) -> FiefUserInfo:
1190    async def change_password(
1191        self, access_token: str, new_password: str
1192    ) -> FiefUserInfo:
1193        """
1194        Change the user password with the Fief API using a valid access token.
1195
1196        **An access token with an ACR of at least level 1 is required.**
1197
1198        :param access_token: A valid access token.
1199        :param new_password: The new password.
1200
1201        **Example**
1202
1203        ```py
1204        userinfo = await fief.change_password("ACCESS_TOKEN", "herminetincture")
1205        ```
1206        """
1207        change_password_profile_endpoint = f"{self.base_url}/api/password"
1208
1209        async with self._get_httpx_client() as client:
1210            request = self._get_change_password_request(
1211                client,
1212                endpoint=change_password_profile_endpoint,
1213                access_token=access_token,
1214                new_password=new_password,
1215            )
1216            response = await client.send(request)
1217
1218            self._handle_request_error(response)
1219
1220            return response.json()

Change the user password with the Fief API using a valid access token.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • new_password: The new password.

Example

userinfo = await fief.change_password("ACCESS_TOKEN", "herminetincture")
async def email_change(self, access_token: str, email: str) -> FiefUserInfo:
1222    async def email_change(self, access_token: str, email: str) -> FiefUserInfo:
1223        """
1224        Request an email change with the Fief API using a valid access token.
1225
1226        The user will receive a verification code on this new email address.
1227        It shall be used with the method `email_verify` to complete the modification.
1228
1229        **An access token with an ACR of at least level 1 is required.**
1230
1231        :param access_token: A valid access token.
1232        :param email: The new email address.
1233
1234        **Example**
1235
1236        ```py
1237        userinfo = await fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
1238        ```
1239        """
1240        email_change_endpoint = f"{self.base_url}/api/email/change"
1241
1242        async with self._get_httpx_client() as client:
1243            request = self._get_email_change_request(
1244                client,
1245                endpoint=email_change_endpoint,
1246                access_token=access_token,
1247                email=email,
1248            )
1249            response = await client.send(request)
1250
1251            self._handle_request_error(response)
1252
1253            return response.json()

Request an email change with the Fief API using a valid access token.

The user will receive a verification code on this new email address. It shall be used with the method email_verify to complete the modification.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • email: The new email address.

Example

userinfo = await fief.email_change("ACCESS_TOKEN", "anne@nantes.city")
async def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
1255    async def email_verify(self, access_token: str, code: str) -> FiefUserInfo:
1256        """
1257        Verify the user email with the Fief API using a valid access token and verification code.
1258
1259        **An access token with an ACR of at least level 1 is required.**
1260
1261        :param access_token: A valid access token.
1262        :param code: The verification code received by email.
1263
1264        **Example**
1265
1266        ```py
1267        userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
1268        ```
1269        """
1270        email_verify_endpoint = f"{self.base_url}/api/email/verify"
1271
1272        async with self._get_httpx_client() as client:
1273            request = self._get_email_verify_request(
1274                client,
1275                endpoint=email_verify_endpoint,
1276                access_token=access_token,
1277                code=code,
1278            )
1279            response = await client.send(request)
1280
1281            self._handle_request_error(response)
1282
1283            return response.json()

Verify the user email with the Fief API using a valid access token and verification code.

An access token with an ACR of at least level 1 is required.

Parameters
  • access_token: A valid access token.
  • code: The verification code received by email.

Example

userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE")
async def logout_url(self, redirect_uri: str) -> str:
1285    async def logout_url(self, redirect_uri: str) -> str:
1286        """
1287        Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.
1288
1289        **You're still responsible for clearing your own session mechanism if any.**
1290
1291        :param redirect_uri: A valid URL where the user will be redirected after the logout process:
1292
1293        **Example:**
1294
1295        ```py
1296        logout_url = await fief.logout_url("http://localhost:8000")
1297        ```
1298        """
1299        params = {"redirect_uri": redirect_uri}
1300        return f"{self.base_url}/logout?{urlencode(params)}"

Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side.

You're still responsible for clearing your own session mechanism if any.

Parameters
  • redirect_uri: A valid URL where the user will be redirected after the logout process:

Example:

logout_url = await fief.logout_url("http://localhost:8000")
Inherited Members
fief_client.client.BaseFief
base_url
client_id
client_secret
encryption_key
host
verify
cert
class FiefTokenResponse(typing.TypedDict):
55class FiefTokenResponse(TypedDict):
56    """
57    Typed dictionary containing the tokens and related information returned by Fief after a successful authentication.
58    """
59
60    access_token: str
61    """Access token you can use to call the Fief API."""
62    id_token: str
63    """ID token containing user information."""
64    token_type: str
65    """Type of token, usually `bearer`."""
66    expires_in: int
67    """Number of seconds after which the tokens will expire."""
68    refresh_token: Optional[str]
69    """Token provided only if scope `offline_access` was granted. Allows you to retrieve fresh tokens using the `Fief.auth_refresh_token` method."""

Typed dictionary containing the tokens and related information returned by Fief after a successful authentication.

access_token: str

Access token you can use to call the Fief API.

id_token: str

ID token containing user information.

token_type: str

Type of token, usually bearer.

expires_in: int

Number of seconds after which the tokens will expire.

refresh_token: Optional[str]

Token provided only if scope offline_access was granted. Allows you to retrieve fresh tokens using the Fief.auth_refresh_token method.

class FiefAccessTokenInfo(typing.TypedDict):
72class FiefAccessTokenInfo(TypedDict):
73    """
74    Typed dictionary containing information about the access token.
75
76    **Example:**
77
78    ```json
79    {
80        "id": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e",
81        "scope": ["openid", "required_scope"],
82        "acr": "1",
83        "permissions": ["castles:read", "castles:create", "castles:update", "castles:delete"],
84        "access_token": "ACCESS_TOKEN",
85    }
86    ```
87    """
88
89    id: uuid.UUID
90    """ID of the user."""
91    scope: List[str]
92    """List of granted scopes for this access token."""
93    acr: FiefACR
94    """Level of Authentication Context class Reference."""
95    permissions: List[str]
96    """List of [granted permissions](https://docs.fief.dev/getting-started/access-control/) for this user."""
97    access_token: str
98    """Access token you can use to call the Fief API."""

Typed dictionary containing information about the access token.

Example:

{
    "id": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e",
    "scope": ["openid", "required_scope"],
    "acr": "1",
    "permissions": ["castles:read", "castles:create", "castles:update", "castles:delete"],
    "access_token": "ACCESS_TOKEN",
}
id: uuid.UUID

ID of the user.

scope: List[str]

List of granted scopes for this access token.

acr: FiefACR

Level of Authentication Context class Reference.

permissions: List[str]

List of granted permissions for this user.

access_token: str

Access token you can use to call the Fief API.

class FiefUserInfo(typing.TypedDict):
101class FiefUserInfo(TypedDict):
102    """
103    Dictionary containing user information.
104
105    **Example:**
106
107    ```json
108    {
109        "sub": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e",
110        "email": "anne@bretagne.duchy",
111        "tenant_id": "c91ecb7f-359c-4244-8385-51ecd6c0d06b",
112        "fields": {
113            "first_name": "Anne",
114            "last_name": "De Bretagne"
115        }
116    }
117    ```
118    """
119
120    sub: str
121    """
122    ID of the user.
123    """
124    email: str
125    """
126    Email address of the user.
127    """
128    tenant_id: str
129    """
130    ID of the [tenant](https://docs.fief.dev/getting-started/tenants/) associated to the user.
131    """
132    fields: Dict[str, Any]
133    """
134    [User fields](https://docs.fief.dev/getting-started/user-fields/) values for this user, indexed by their slug.
135    """

Dictionary containing user information.

Example:

{
    "sub": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e",
    "email": "anne@bretagne.duchy",
    "tenant_id": "c91ecb7f-359c-4244-8385-51ecd6c0d06b",
    "fields": {
        "first_name": "Anne",
        "last_name": "De Bretagne"
    }
}
sub: str

ID of the user.

email: str

Email address of the user.

tenant_id: str

ID of the tenant associated to the user.

fields: Dict[str, Any]

User fields values for this user, indexed by their slug.

class FiefError(builtins.Exception):
138class FiefError(Exception):
139    """Base Fief client error."""

Base Fief client error.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefAccessTokenACRTooLow(fief_client.FiefError):
164class FiefAccessTokenACRTooLow(FiefError):
165    """The access token doesn't meet the minimum ACR level."""

The access token doesn't meet the minimum ACR level.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefAccessTokenExpired(fief_client.FiefError):
156class FiefAccessTokenExpired(FiefError):
157    """The access token is expired."""

The access token is expired.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefAccessTokenMissingPermission(fief_client.FiefError):
168class FiefAccessTokenMissingPermission(FiefError):
169    """The access token is missing a required permission."""

The access token is missing a required permission.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefAccessTokenMissingScope(fief_client.FiefError):
160class FiefAccessTokenMissingScope(FiefError):
161    """The access token is missing a required scope."""

The access token is missing a required scope.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefAccessTokenInvalid(fief_client.FiefError):
152class FiefAccessTokenInvalid(FiefError):
153    """The access token is invalid."""

The access token is invalid.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefIdTokenInvalid(fief_client.FiefError):
172class FiefIdTokenInvalid(FiefError):
173    """The ID token is invalid."""

The ID token is invalid.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class FiefRequestError(fief_client.FiefError):
142class FiefRequestError(FiefError):
143    """The request to Fief server resulted in an error."""
144
145    def __init__(self, status_code: int, detail: str) -> None:
146        self.status_code = status_code
147        self.detail = detail
148        self.message = f"[{status_code}] - {detail}"
149        super().__init__(self.message)

The request to Fief server resulted in an error.

FiefRequestError(status_code: int, detail: str)
145    def __init__(self, status_code: int, detail: str) -> None:
146        self.status_code = status_code
147        self.detail = detail
148        self.message = f"[{status_code}] - {detail}"
149        super().__init__(self.message)
status_code
detail
message
Inherited Members
builtins.BaseException
with_traceback
args