fief_client
Fief client for Python.
1"Fief client for Python." 2 3from fief_client.client import ( 4 Fief, 5 FiefAccessTokenACRTooLow, 6 FiefAccessTokenExpired, 7 FiefAccessTokenInfo, 8 FiefAccessTokenInvalid, 9 FiefAccessTokenMissingPermission, 10 FiefAccessTokenMissingScope, 11 FiefACR, 12 FiefAsync, 13 FiefError, 14 FiefIdTokenInvalid, 15 FiefRequestError, 16 FiefTokenResponse, 17 FiefUserInfo, 18) 19 20__version__ = "0.20.0" 21 22__all__ = [ 23 "Fief", 24 "FiefACR", 25 "FiefAsync", 26 "FiefTokenResponse", 27 "FiefAccessTokenInfo", 28 "FiefUserInfo", 29 "FiefError", 30 "FiefAccessTokenACRTooLow", 31 "FiefAccessTokenExpired", 32 "FiefAccessTokenMissingPermission", 33 "FiefAccessTokenMissingScope", 34 "FiefAccessTokenInvalid", 35 "FiefIdTokenInvalid", 36 "FiefRequestError", 37 "crypto", 38 "pkce", 39 "integrations", 40]
498class Fief(BaseFief): 499 """Sync Fief authentication client.""" 500 501 def __init__( 502 self, 503 base_url: str, 504 client_id: str, 505 client_secret: Optional[str] = None, 506 *, 507 encryption_key: Optional[str] = None, 508 host: Optional[str] = None, 509 verify: VerifyTypes = True, 510 cert: Optional[CertTypes] = None, 511 ) -> None: 512 super().__init__( 513 base_url, 514 client_id, 515 client_secret, 516 encryption_key=encryption_key, 517 host=host, 518 verify=verify, 519 cert=cert, 520 ) 521 522 def auth_url( 523 self, 524 redirect_uri: str, 525 *, 526 state: Optional[str] = None, 527 scope: Optional[list[str]] = None, 528 code_challenge: Optional[str] = None, 529 code_challenge_method: Optional[str] = None, 530 lang: Optional[str] = None, 531 extras_params: Optional[Mapping[str, str]] = None, 532 ) -> str: 533 """ 534 Return an authorization URL. 535 536 :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication. 537 :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information. 538 :param scope: Optional list of scopes to ask for. 539 :param code_challenge: Optional code challenge for 540 [PKCE process](https://docs.fief.dev/going-further/pkce/). 541 :param code_challenge_method: Method used to hash the PKCE code challenge. 542 :param lang: Optional parameter to set the user locale. 543 Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`. 544 If not provided, the user locale is determined by their browser settings. 545 :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/). 546 547 **Example:** 548 549 ```py 550 auth_url = fief.auth_url("http://localhost:8000/callback", scope=["openid"]) 551 ``` 552 """ 553 openid_configuration = self._get_openid_configuration() 554 return self._auth_url( 555 openid_configuration, 556 redirect_uri, 557 state=state, 558 scope=scope, 559 code_challenge=code_challenge, 560 code_challenge_method=code_challenge_method, 561 lang=lang, 562 extras_params=extras_params, 563 ) 564 565 def auth_callback( 566 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 567 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 568 """ 569 Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code. 570 571 :param code: The authorization code. 572 :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL. 573 :param code_verifier: The raw 574 [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization. 575 576 **Example:** 577 578 ```py 579 tokens, userinfo = fief.auth_callback("CODE", "http://localhost:8000/callback") 580 ``` 581 """ 582 token_response = self._auth_exchange_token( 583 code, redirect_uri, code_verifier=code_verifier 584 ) 585 jwks = self._get_jwks() 586 userinfo = self._decode_id_token( 587 token_response["id_token"], 588 jwks, 589 code=code, 590 access_token=token_response.get("access_token"), 591 ) 592 return token_response, userinfo 593 594 def auth_refresh_token( 595 self, refresh_token: str, *, scope: Optional[list[str]] = None 596 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 597 """ 598 Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token 599 600 :param refresh_token: A valid refresh token. 601 :param scope: Optional list of scopes to ask for. 602 If not provided, the access token will share the same list of scopes as requested the first time. 603 Otherwise, it should be a subset of the original list of scopes. 604 605 **Example:** 606 607 ```py 608 tokens, userinfo = fief.auth_refresh_token("REFRESH_TOKEN") 609 ``` 610 """ 611 token_endpoint = self._get_endpoint_url( 612 self._get_openid_configuration(), "token_endpoint" 613 ) 614 with self._get_httpx_client() as client: 615 request = self._get_auth_refresh_token_request( 616 client, 617 endpoint=token_endpoint, 618 refresh_token=refresh_token, 619 scope=scope, 620 ) 621 response = client.send(request) 622 623 self._handle_request_error(response) 624 625 token_response = response.json() 626 jwks = self._get_jwks() 627 userinfo = self._decode_id_token( 628 token_response["id_token"], 629 jwks, 630 access_token=token_response.get("access_token"), 631 ) 632 return token_response, userinfo 633 634 def validate_access_token( 635 self, 636 access_token: str, 637 *, 638 required_scope: Optional[list[str]] = None, 639 required_acr: Optional[FiefACR] = None, 640 required_permissions: Optional[list[str]] = None, 641 ) -> FiefAccessTokenInfo: 642 """ 643 Check if an access token is valid and optionally that it has a required list of scopes, 644 or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/). 645 Returns a `FiefAccessTokenInfo`. 646 647 :param access_token: The access token to validate. 648 :param required_scope: Optional list of scopes to check for. 649 :param required_acr: Optional minimum ACR level required. 650 Read more: https://docs.fief.dev/going-further/acr/ 651 :param required_permissions: Optional list of permissions to check for. 652 653 **Example: Validate access token with required scopes** 654 655 ```py 656 try: 657 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"]) 658 except FiefAccessTokenInvalid: 659 print("Invalid access token") 660 except FiefAccessTokenExpired: 661 print("Expired access token") 662 except FiefAccessTokenMissingScope: 663 print("Missing required scope") 664 665 print(access_token_info) 666 ``` 667 668 **Example: Validate access token with minimum ACR level** 669 670 ```py 671 try: 672 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE) 673 except FiefAccessTokenInvalid: 674 print("Invalid access token") 675 except FiefAccessTokenExpired: 676 print("Expired access token") 677 except FiefAccessTokenACRTooLow: 678 print("ACR too low") 679 680 print(access_token_info) 681 ``` 682 683 **Example: Validate access token with required permissions** 684 685 ```py 686 try: 687 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"]) 688 except FiefAccessTokenInvalid: 689 print("Invalid access token") 690 except FiefAccessTokenExpired: 691 print("Expired access token") 692 except FiefAccessTokenMissingPermission: 693 print("Missing required permission") 694 695 print(access_token_info) 696 ``` 697 """ 698 jwks = self._get_jwks() 699 return self._validate_access_token( 700 access_token, 701 jwks, 702 required_scope=required_scope, 703 required_acr=required_acr, 704 required_permissions=required_permissions, 705 ) 706 707 def userinfo(self, access_token: str) -> FiefUserInfo: 708 """ 709 Return fresh `FiefUserInfo` from the Fief API using a valid access token. 710 711 :param access_token: A valid access token. 712 713 **Example:** 714 715 ```py 716 userinfo = fief.userinfo("ACCESS_TOKEN") 717 ``` 718 """ 719 userinfo_endpoint = self._get_endpoint_url( 720 self._get_openid_configuration(), "userinfo_endpoint" 721 ) 722 with self._get_httpx_client() as client: 723 request = self._get_userinfo_request( 724 client, endpoint=userinfo_endpoint, access_token=access_token 725 ) 726 response = client.send(request) 727 728 self._handle_request_error(response) 729 730 return response.json() 731 732 def update_profile(self, access_token: str, data: dict[str, Any]) -> FiefUserInfo: 733 """ 734 Update user information with the Fief API using a valid access token. 735 736 :param access_token: A valid access token. 737 :param data: A dictionary containing the data to update. 738 739 **Example: Update user field** 740 741 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. 742 743 ```py 744 userinfo = fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } }) 745 ``` 746 """ 747 update_profile_endpoint = f"{self.base_url}/api/profile" 748 749 with self._get_httpx_client() as client: 750 request = self._get_update_profile_request( 751 client, 752 endpoint=update_profile_endpoint, 753 access_token=access_token, 754 data=data, 755 ) 756 response = client.send(request) 757 758 self._handle_request_error(response) 759 760 return response.json() 761 762 def change_password(self, access_token: str, new_password: str) -> FiefUserInfo: 763 """ 764 Change the user password with the Fief API using a valid access token. 765 766 **An access token with an ACR of at least level 1 is required.** 767 768 :param access_token: A valid access token. 769 :param new_password: The new password. 770 771 **Example** 772 773 ```py 774 userinfo = fief.change_password("ACCESS_TOKEN", "herminetincture") 775 ``` 776 """ 777 change_password_profile_endpoint = f"{self.base_url}/api/password" 778 779 with self._get_httpx_client() as client: 780 request = self._get_change_password_request( 781 client, 782 endpoint=change_password_profile_endpoint, 783 access_token=access_token, 784 new_password=new_password, 785 ) 786 response = client.send(request) 787 788 self._handle_request_error(response) 789 790 return response.json() 791 792 def email_change(self, access_token: str, email: str) -> FiefUserInfo: 793 """ 794 Request an email change with the Fief API using a valid access token. 795 796 The user will receive a verification code on this new email address. 797 It shall be used with the method `email_verify` to complete the modification. 798 799 **An access token with an ACR of at least level 1 is required.** 800 801 :param access_token: A valid access token. 802 :param email: The new email address. 803 804 **Example** 805 806 ```py 807 userinfo = fief.email_change("ACCESS_TOKEN", "anne@nantes.city") 808 ``` 809 """ 810 email_change_endpoint = f"{self.base_url}/api/email/change" 811 812 with self._get_httpx_client() as client: 813 request = self._get_email_change_request( 814 client, 815 endpoint=email_change_endpoint, 816 access_token=access_token, 817 email=email, 818 ) 819 response = client.send(request) 820 821 self._handle_request_error(response) 822 823 return response.json() 824 825 def email_verify(self, access_token: str, code: str) -> FiefUserInfo: 826 """ 827 Verify the user email with the Fief API using a valid access token and verification code. 828 829 **An access token with an ACR of at least level 1 is required.** 830 831 :param access_token: A valid access token. 832 :param code: The verification code received by email. 833 834 **Example** 835 836 ```py 837 userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE") 838 ``` 839 """ 840 email_verify_endpoint = f"{self.base_url}/api/email/verify" 841 842 with self._get_httpx_client() as client: 843 request = self._get_email_verify_request( 844 client, 845 endpoint=email_verify_endpoint, 846 access_token=access_token, 847 code=code, 848 ) 849 response = client.send(request) 850 851 self._handle_request_error(response) 852 853 return response.json() 854 855 def logout_url(self, redirect_uri: str) -> str: 856 """ 857 Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side. 858 859 **You're still responsible for clearing your own session mechanism if any.** 860 861 :param redirect_uri: A valid URL where the user will be redirected after the logout process. 862 863 **Example:** 864 865 ```py 866 logout_url = fief.logout_url("http://localhost:8000") 867 ``` 868 """ 869 params = {"redirect_uri": redirect_uri} 870 return f"{self.base_url}/logout?{urlencode(params)}" 871 872 @contextlib.contextmanager 873 def _get_httpx_client(self): 874 headers = {} 875 if self.host is not None: 876 headers["Host"] = self.host 877 878 with httpx.Client( 879 base_url=self.base_url, headers=headers, verify=self.verify, cert=self.cert 880 ) as client: 881 yield client 882 883 def _get_openid_configuration(self) -> dict[str, Any]: 884 if self._openid_configuration is not None: 885 return self._openid_configuration 886 887 with self._get_httpx_client() as client: 888 request = self._get_openid_configuration_request(client) 889 response = client.send(request) 890 json = response.json() 891 self._openid_configuration = json 892 return json 893 894 def _get_jwks(self) -> jwk.JWKSet: 895 if self._jwks is not None: 896 return self._jwks 897 898 jwks_uri = self._get_endpoint_url(self._get_openid_configuration(), "jwks_uri") 899 with self._get_httpx_client() as client: 900 response = client.get(jwks_uri) 901 self._jwks = jwk.JWKSet.from_json(response.text) 902 return self._jwks 903 904 def _auth_exchange_token( 905 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 906 ) -> FiefTokenResponse: 907 token_endpoint = self._get_endpoint_url( 908 self._get_openid_configuration(), "token_endpoint" 909 ) 910 with self._get_httpx_client() as client: 911 request = self._get_auth_exchange_token_request( 912 client, 913 endpoint=token_endpoint, 914 code=code, 915 redirect_uri=redirect_uri, 916 code_verifier=code_verifier, 917 ) 918 response = client.send(request) 919 920 self._handle_request_error(response) 921 922 return response.json()
Sync Fief authentication client.
501 def __init__( 502 self, 503 base_url: str, 504 client_id: str, 505 client_secret: Optional[str] = None, 506 *, 507 encryption_key: Optional[str] = None, 508 host: Optional[str] = None, 509 verify: VerifyTypes = True, 510 cert: Optional[CertTypes] = None, 511 ) -> None: 512 super().__init__( 513 base_url, 514 client_id, 515 client_secret, 516 encryption_key=encryption_key, 517 host=host, 518 verify=verify, 519 cert=cert, 520 )
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.
522 def auth_url( 523 self, 524 redirect_uri: str, 525 *, 526 state: Optional[str] = None, 527 scope: Optional[list[str]] = None, 528 code_challenge: Optional[str] = None, 529 code_challenge_method: Optional[str] = None, 530 lang: Optional[str] = None, 531 extras_params: Optional[Mapping[str, str]] = None, 532 ) -> str: 533 """ 534 Return an authorization URL. 535 536 :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication. 537 :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information. 538 :param scope: Optional list of scopes to ask for. 539 :param code_challenge: Optional code challenge for 540 [PKCE process](https://docs.fief.dev/going-further/pkce/). 541 :param code_challenge_method: Method used to hash the PKCE code challenge. 542 :param lang: Optional parameter to set the user locale. 543 Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`. 544 If not provided, the user locale is determined by their browser settings. 545 :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/). 546 547 **Example:** 548 549 ```py 550 auth_url = fief.auth_url("http://localhost:8000/callback", scope=["openid"]) 551 ``` 552 """ 553 openid_configuration = self._get_openid_configuration() 554 return self._auth_url( 555 openid_configuration, 556 redirect_uri, 557 state=state, 558 scope=scope, 559 code_challenge=code_challenge, 560 code_challenge_method=code_challenge_method, 561 lang=lang, 562 extras_params=extras_params, 563 )
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
orpt-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"])
565 def auth_callback( 566 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 567 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 568 """ 569 Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code. 570 571 :param code: The authorization code. 572 :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL. 573 :param code_verifier: The raw 574 [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization. 575 576 **Example:** 577 578 ```py 579 tokens, userinfo = fief.auth_callback("CODE", "http://localhost:8000/callback") 580 ``` 581 """ 582 token_response = self._auth_exchange_token( 583 code, redirect_uri, code_verifier=code_verifier 584 ) 585 jwks = self._get_jwks() 586 userinfo = self._decode_id_token( 587 token_response["id_token"], 588 jwks, 589 code=code, 590 access_token=token_response.get("access_token"), 591 ) 592 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")
594 def auth_refresh_token( 595 self, refresh_token: str, *, scope: Optional[list[str]] = None 596 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 597 """ 598 Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token 599 600 :param refresh_token: A valid refresh token. 601 :param scope: Optional list of scopes to ask for. 602 If not provided, the access token will share the same list of scopes as requested the first time. 603 Otherwise, it should be a subset of the original list of scopes. 604 605 **Example:** 606 607 ```py 608 tokens, userinfo = fief.auth_refresh_token("REFRESH_TOKEN") 609 ``` 610 """ 611 token_endpoint = self._get_endpoint_url( 612 self._get_openid_configuration(), "token_endpoint" 613 ) 614 with self._get_httpx_client() as client: 615 request = self._get_auth_refresh_token_request( 616 client, 617 endpoint=token_endpoint, 618 refresh_token=refresh_token, 619 scope=scope, 620 ) 621 response = client.send(request) 622 623 self._handle_request_error(response) 624 625 token_response = response.json() 626 jwks = self._get_jwks() 627 userinfo = self._decode_id_token( 628 token_response["id_token"], 629 jwks, 630 access_token=token_response.get("access_token"), 631 ) 632 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")
634 def validate_access_token( 635 self, 636 access_token: str, 637 *, 638 required_scope: Optional[list[str]] = None, 639 required_acr: Optional[FiefACR] = None, 640 required_permissions: Optional[list[str]] = None, 641 ) -> FiefAccessTokenInfo: 642 """ 643 Check if an access token is valid and optionally that it has a required list of scopes, 644 or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/). 645 Returns a `FiefAccessTokenInfo`. 646 647 :param access_token: The access token to validate. 648 :param required_scope: Optional list of scopes to check for. 649 :param required_acr: Optional minimum ACR level required. 650 Read more: https://docs.fief.dev/going-further/acr/ 651 :param required_permissions: Optional list of permissions to check for. 652 653 **Example: Validate access token with required scopes** 654 655 ```py 656 try: 657 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"]) 658 except FiefAccessTokenInvalid: 659 print("Invalid access token") 660 except FiefAccessTokenExpired: 661 print("Expired access token") 662 except FiefAccessTokenMissingScope: 663 print("Missing required scope") 664 665 print(access_token_info) 666 ``` 667 668 **Example: Validate access token with minimum ACR level** 669 670 ```py 671 try: 672 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE) 673 except FiefAccessTokenInvalid: 674 print("Invalid access token") 675 except FiefAccessTokenExpired: 676 print("Expired access token") 677 except FiefAccessTokenACRTooLow: 678 print("ACR too low") 679 680 print(access_token_info) 681 ``` 682 683 **Example: Validate access token with required permissions** 684 685 ```py 686 try: 687 access_token_info = fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"]) 688 except FiefAccessTokenInvalid: 689 print("Invalid access token") 690 except FiefAccessTokenExpired: 691 print("Expired access token") 692 except FiefAccessTokenMissingPermission: 693 print("Missing required permission") 694 695 print(access_token_info) 696 ``` 697 """ 698 jwks = self._get_jwks() 699 return self._validate_access_token( 700 access_token, 701 jwks, 702 required_scope=required_scope, 703 required_acr=required_acr, 704 required_permissions=required_permissions, 705 )
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)
707 def userinfo(self, access_token: str) -> FiefUserInfo: 708 """ 709 Return fresh `FiefUserInfo` from the Fief API using a valid access token. 710 711 :param access_token: A valid access token. 712 713 **Example:** 714 715 ```py 716 userinfo = fief.userinfo("ACCESS_TOKEN") 717 ``` 718 """ 719 userinfo_endpoint = self._get_endpoint_url( 720 self._get_openid_configuration(), "userinfo_endpoint" 721 ) 722 with self._get_httpx_client() as client: 723 request = self._get_userinfo_request( 724 client, endpoint=userinfo_endpoint, access_token=access_token 725 ) 726 response = client.send(request) 727 728 self._handle_request_error(response) 729 730 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")
732 def update_profile(self, access_token: str, data: dict[str, Any]) -> FiefUserInfo: 733 """ 734 Update user information with the Fief API using a valid access token. 735 736 :param access_token: A valid access token. 737 :param data: A dictionary containing the data to update. 738 739 **Example: Update user field** 740 741 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. 742 743 ```py 744 userinfo = fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } }) 745 ``` 746 """ 747 update_profile_endpoint = f"{self.base_url}/api/profile" 748 749 with self._get_httpx_client() as client: 750 request = self._get_update_profile_request( 751 client, 752 endpoint=update_profile_endpoint, 753 access_token=access_token, 754 data=data, 755 ) 756 response = client.send(request) 757 758 self._handle_request_error(response) 759 760 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" } })
762 def change_password(self, access_token: str, new_password: str) -> FiefUserInfo: 763 """ 764 Change the user password with the Fief API using a valid access token. 765 766 **An access token with an ACR of at least level 1 is required.** 767 768 :param access_token: A valid access token. 769 :param new_password: The new password. 770 771 **Example** 772 773 ```py 774 userinfo = fief.change_password("ACCESS_TOKEN", "herminetincture") 775 ``` 776 """ 777 change_password_profile_endpoint = f"{self.base_url}/api/password" 778 779 with self._get_httpx_client() as client: 780 request = self._get_change_password_request( 781 client, 782 endpoint=change_password_profile_endpoint, 783 access_token=access_token, 784 new_password=new_password, 785 ) 786 response = client.send(request) 787 788 self._handle_request_error(response) 789 790 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")
792 def email_change(self, access_token: str, email: str) -> FiefUserInfo: 793 """ 794 Request an email change with the Fief API using a valid access token. 795 796 The user will receive a verification code on this new email address. 797 It shall be used with the method `email_verify` to complete the modification. 798 799 **An access token with an ACR of at least level 1 is required.** 800 801 :param access_token: A valid access token. 802 :param email: The new email address. 803 804 **Example** 805 806 ```py 807 userinfo = fief.email_change("ACCESS_TOKEN", "anne@nantes.city") 808 ``` 809 """ 810 email_change_endpoint = f"{self.base_url}/api/email/change" 811 812 with self._get_httpx_client() as client: 813 request = self._get_email_change_request( 814 client, 815 endpoint=email_change_endpoint, 816 access_token=access_token, 817 email=email, 818 ) 819 response = client.send(request) 820 821 self._handle_request_error(response) 822 823 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")
825 def email_verify(self, access_token: str, code: str) -> FiefUserInfo: 826 """ 827 Verify the user email with the Fief API using a valid access token and verification code. 828 829 **An access token with an ACR of at least level 1 is required.** 830 831 :param access_token: A valid access token. 832 :param code: The verification code received by email. 833 834 **Example** 835 836 ```py 837 userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE") 838 ``` 839 """ 840 email_verify_endpoint = f"{self.base_url}/api/email/verify" 841 842 with self._get_httpx_client() as client: 843 request = self._get_email_verify_request( 844 client, 845 endpoint=email_verify_endpoint, 846 access_token=access_token, 847 code=code, 848 ) 849 response = client.send(request) 850 851 self._handle_request_error(response) 852 853 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")
855 def logout_url(self, redirect_uri: str) -> str: 856 """ 857 Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side. 858 859 **You're still responsible for clearing your own session mechanism if any.** 860 861 :param redirect_uri: A valid URL where the user will be redirected after the logout process. 862 863 **Example:** 864 865 ```py 866 logout_url = fief.logout_url("http://localhost:8000") 867 ``` 868 """ 869 params = {"redirect_uri": redirect_uri} 870 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")
19class FiefACR(str, Enum): 20 """ 21 List of defined Authentication Context Class Reference. 22 """ 23 24 LEVEL_ZERO = "0" 25 """Level 0. No authentication was performed, a previous session was used.""" 26 LEVEL_ONE = "1" 27 """Level 1. Password authentication was performed.""" 28 29 def __lt__(self, other: object) -> bool: 30 return self._compare(other, True, True) 31 32 def __le__(self, other: object) -> bool: 33 return self._compare(other, False, True) 34 35 def __gt__(self, other: object) -> bool: 36 return self._compare(other, True, False) 37 38 def __ge__(self, other: object) -> bool: 39 return self._compare(other, False, False) 40 41 def _compare(self, other: object, strict: bool, asc: bool) -> bool: 42 if not isinstance(other, FiefACR): 43 return NotImplemented # pragma: no cover 44 45 if self == other: 46 return not strict 47 48 for elem in FiefACR: 49 if self == elem: 50 return asc 51 elif other == elem: 52 return not asc 53 raise RuntimeError() # pragma: no cover
List of defined Authentication Context Class Reference.
Level 0. No authentication was performed, a previous session was used.
925class FiefAsync(BaseFief): 926 """Async Fief authentication client.""" 927 928 def __init__( 929 self, 930 base_url: str, 931 client_id: str, 932 client_secret: Optional[str] = None, 933 *, 934 encryption_key: Optional[str] = None, 935 host: Optional[str] = None, 936 verify: VerifyTypes = True, 937 cert: Optional[CertTypes] = None, 938 ) -> None: 939 super().__init__( 940 base_url, 941 client_id, 942 client_secret, 943 encryption_key=encryption_key, 944 host=host, 945 verify=verify, 946 cert=cert, 947 ) 948 949 async def auth_url( 950 self, 951 redirect_uri: str, 952 *, 953 state: Optional[str] = None, 954 scope: Optional[list[str]] = None, 955 code_challenge: Optional[str] = None, 956 code_challenge_method: Optional[str] = None, 957 lang: Optional[str] = None, 958 extras_params: Optional[Mapping[str, str]] = None, 959 ) -> str: 960 """ 961 Return an authorization URL. 962 963 :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication. 964 :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information. 965 :param scope: Optional list of scopes to ask for. 966 :param code_challenge: Optional code challenge for 967 [PKCE process](https://docs.fief.dev/going-further/pkce/). 968 :param code_challenge_method: Method used to hash the PKCE code challenge. 969 :param lang: Optional parameter to set the user locale. 970 Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`. 971 If not provided, the user locale is determined by their browser settings. 972 :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/). 973 974 **Example:** 975 976 ```py 977 auth_url = await fief.auth_url("http://localhost:8000/callback", scope=["openid"]) 978 ``` 979 """ 980 openid_configuration = await self._get_openid_configuration() 981 return self._auth_url( 982 openid_configuration, 983 redirect_uri, 984 state=state, 985 scope=scope, 986 code_challenge=code_challenge, 987 code_challenge_method=code_challenge_method, 988 lang=lang, 989 extras_params=extras_params, 990 ) 991 992 async def auth_callback( 993 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 994 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 995 """ 996 Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code. 997 998 :param code: The authorization code. 999 :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL. 1000 :param code_verifier: The raw 1001 [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization. 1002 1003 **Example:** 1004 1005 ```py 1006 tokens, userinfo = await fief.auth_callback("CODE", "http://localhost:8000/callback") 1007 ``` 1008 """ 1009 token_response = await self._auth_exchange_token( 1010 code, redirect_uri, code_verifier=code_verifier 1011 ) 1012 jwks = await self._get_jwks() 1013 userinfo = self._decode_id_token( 1014 token_response["id_token"], 1015 jwks, 1016 code=code, 1017 access_token=token_response.get("access_token"), 1018 ) 1019 return token_response, userinfo 1020 1021 async def auth_refresh_token( 1022 self, refresh_token: str, *, scope: Optional[list[str]] = None 1023 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 1024 """ 1025 Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token 1026 1027 :param refresh_token: A valid refresh token. 1028 :param scope: Optional list of scopes to ask for. 1029 If not provided, the access token will share the same list of scopes as requested the first time. 1030 Otherwise, it should be a subset of the original list of scopes. 1031 1032 **Example:** 1033 1034 ```py 1035 tokens, userinfo = await fief.auth_refresh_token("REFRESH_TOKEN") 1036 ``` 1037 """ 1038 token_endpoint = self._get_endpoint_url( 1039 await self._get_openid_configuration(), "token_endpoint" 1040 ) 1041 async with self._get_httpx_client() as client: 1042 request = self._get_auth_refresh_token_request( 1043 client, 1044 endpoint=token_endpoint, 1045 refresh_token=refresh_token, 1046 scope=scope, 1047 ) 1048 response = await client.send(request) 1049 1050 self._handle_request_error(response) 1051 1052 token_response = response.json() 1053 1054 jwks = await self._get_jwks() 1055 userinfo = self._decode_id_token( 1056 token_response["id_token"], 1057 jwks, 1058 access_token=token_response.get("access_token"), 1059 ) 1060 return token_response, userinfo 1061 1062 async def validate_access_token( 1063 self, 1064 access_token: str, 1065 *, 1066 required_scope: Optional[list[str]] = None, 1067 required_acr: Optional[FiefACR] = None, 1068 required_permissions: Optional[list[str]] = None, 1069 ) -> FiefAccessTokenInfo: 1070 """ 1071 Check if an access token is valid and optionally that it has a required list of scopes, 1072 or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/). 1073 Returns a `FiefAccessTokenInfo`. 1074 1075 :param access_token: The access token to validate. 1076 :param required_scope: Optional list of scopes to check for. 1077 :param required_acr: Optional minimum ACR level required. 1078 Read more: https://docs.fief.dev/going-further/acr/ 1079 :param required_permissions: Optional list of permissions to check for. 1080 1081 **Example: Validate access token with required scopes** 1082 1083 ```py 1084 try: 1085 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"]) 1086 except FiefAccessTokenInvalid: 1087 print("Invalid access token") 1088 except FiefAccessTokenExpired: 1089 print("Expired access token") 1090 except FiefAccessTokenMissingScope: 1091 print("Missing required scope") 1092 1093 print(access_token_info) 1094 ``` 1095 1096 **Example: Validate access token with minimum ACR level** 1097 1098 ```py 1099 try: 1100 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE) 1101 except FiefAccessTokenInvalid: 1102 print("Invalid access token") 1103 except FiefAccessTokenExpired: 1104 print("Expired access token") 1105 except FiefAccessTokenACRTooLow: 1106 print("ACR too low") 1107 1108 print(access_token_info) 1109 ``` 1110 1111 **Example: Validate access token with required permissions** 1112 1113 ```py 1114 try: 1115 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"]) 1116 except FiefAccessTokenInvalid: 1117 print("Invalid access token") 1118 except FiefAccessTokenExpired: 1119 print("Expired access token") 1120 except FiefAccessTokenMissingPermission: 1121 print("Missing required permission") 1122 1123 print(access_token_info) 1124 ``` 1125 """ 1126 jwks = await self._get_jwks() 1127 return self._validate_access_token( 1128 access_token, 1129 jwks, 1130 required_scope=required_scope, 1131 required_acr=required_acr, 1132 required_permissions=required_permissions, 1133 ) 1134 1135 async def userinfo(self, access_token: str) -> FiefUserInfo: 1136 """ 1137 Return fresh `FiefUserInfo` from the Fief API using a valid access token. 1138 1139 :param access_token: A valid access token. 1140 1141 **Example:** 1142 1143 ```py 1144 userinfo = await fief.userinfo("ACCESS_TOKEN") 1145 ``` 1146 """ 1147 userinfo_endpoint = self._get_endpoint_url( 1148 await self._get_openid_configuration(), "userinfo_endpoint" 1149 ) 1150 async with self._get_httpx_client() as client: 1151 request = self._get_userinfo_request( 1152 client, endpoint=userinfo_endpoint, access_token=access_token 1153 ) 1154 response = await client.send(request) 1155 1156 self._handle_request_error(response) 1157 1158 return response.json() 1159 1160 async def update_profile( 1161 self, access_token: str, data: dict[str, Any] 1162 ) -> FiefUserInfo: 1163 """ 1164 Update user information with the Fief API using a valid access token. 1165 1166 :param access_token: A valid access token. 1167 :param data: A dictionary containing the data to update. 1168 1169 **Example: Update user field** 1170 1171 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. 1172 1173 ```py 1174 userinfo = await fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } }) 1175 ``` 1176 """ 1177 update_profile_endpoint = f"{self.base_url}/api/profile" 1178 1179 async with self._get_httpx_client() as client: 1180 request = self._get_update_profile_request( 1181 client, 1182 endpoint=update_profile_endpoint, 1183 access_token=access_token, 1184 data=data, 1185 ) 1186 response = await client.send(request) 1187 1188 self._handle_request_error(response) 1189 1190 return response.json() 1191 1192 async def change_password( 1193 self, access_token: str, new_password: str 1194 ) -> FiefUserInfo: 1195 """ 1196 Change the user password with the Fief API using a valid access token. 1197 1198 **An access token with an ACR of at least level 1 is required.** 1199 1200 :param access_token: A valid access token. 1201 :param new_password: The new password. 1202 1203 **Example** 1204 1205 ```py 1206 userinfo = await fief.change_password("ACCESS_TOKEN", "herminetincture") 1207 ``` 1208 """ 1209 change_password_profile_endpoint = f"{self.base_url}/api/password" 1210 1211 async with self._get_httpx_client() as client: 1212 request = self._get_change_password_request( 1213 client, 1214 endpoint=change_password_profile_endpoint, 1215 access_token=access_token, 1216 new_password=new_password, 1217 ) 1218 response = await client.send(request) 1219 1220 self._handle_request_error(response) 1221 1222 return response.json() 1223 1224 async def email_change(self, access_token: str, email: str) -> FiefUserInfo: 1225 """ 1226 Request an email change with the Fief API using a valid access token. 1227 1228 The user will receive a verification code on this new email address. 1229 It shall be used with the method `email_verify` to complete the modification. 1230 1231 **An access token with an ACR of at least level 1 is required.** 1232 1233 :param access_token: A valid access token. 1234 :param email: The new email address. 1235 1236 **Example** 1237 1238 ```py 1239 userinfo = await fief.email_change("ACCESS_TOKEN", "anne@nantes.city") 1240 ``` 1241 """ 1242 email_change_endpoint = f"{self.base_url}/api/email/change" 1243 1244 async with self._get_httpx_client() as client: 1245 request = self._get_email_change_request( 1246 client, 1247 endpoint=email_change_endpoint, 1248 access_token=access_token, 1249 email=email, 1250 ) 1251 response = await client.send(request) 1252 1253 self._handle_request_error(response) 1254 1255 return response.json() 1256 1257 async def email_verify(self, access_token: str, code: str) -> FiefUserInfo: 1258 """ 1259 Verify the user email with the Fief API using a valid access token and verification code. 1260 1261 **An access token with an ACR of at least level 1 is required.** 1262 1263 :param access_token: A valid access token. 1264 :param code: The verification code received by email. 1265 1266 **Example** 1267 1268 ```py 1269 userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE") 1270 ``` 1271 """ 1272 email_verify_endpoint = f"{self.base_url}/api/email/verify" 1273 1274 async with self._get_httpx_client() as client: 1275 request = self._get_email_verify_request( 1276 client, 1277 endpoint=email_verify_endpoint, 1278 access_token=access_token, 1279 code=code, 1280 ) 1281 response = await client.send(request) 1282 1283 self._handle_request_error(response) 1284 1285 return response.json() 1286 1287 async def logout_url(self, redirect_uri: str) -> str: 1288 """ 1289 Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side. 1290 1291 **You're still responsible for clearing your own session mechanism if any.** 1292 1293 :param redirect_uri: A valid URL where the user will be redirected after the logout process: 1294 1295 **Example:** 1296 1297 ```py 1298 logout_url = await fief.logout_url("http://localhost:8000") 1299 ``` 1300 """ 1301 params = {"redirect_uri": redirect_uri} 1302 return f"{self.base_url}/logout?{urlencode(params)}" 1303 1304 @contextlib.asynccontextmanager 1305 async def _get_httpx_client(self): 1306 headers = {} 1307 if self.host is not None: 1308 headers["Host"] = self.host 1309 1310 async with httpx.AsyncClient( 1311 base_url=self.base_url, headers=headers, verify=self.verify, cert=self.cert 1312 ) as client: 1313 yield client 1314 1315 async def _get_openid_configuration(self) -> dict[str, Any]: 1316 if self._openid_configuration is not None: 1317 return self._openid_configuration 1318 1319 async with self._get_httpx_client() as client: 1320 request = self._get_openid_configuration_request(client) 1321 response = await client.send(request) 1322 json = response.json() 1323 self._openid_configuration = json 1324 return json 1325 1326 async def _get_jwks(self) -> jwk.JWKSet: 1327 if self._jwks is not None: 1328 return self._jwks 1329 1330 jwks_uri = self._get_endpoint_url( 1331 await self._get_openid_configuration(), "jwks_uri" 1332 ) 1333 async with self._get_httpx_client() as client: 1334 response = await client.get(jwks_uri) 1335 self._jwks = jwk.JWKSet.from_json(response.text) 1336 return self._jwks 1337 1338 async def _auth_exchange_token( 1339 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 1340 ) -> FiefTokenResponse: 1341 token_endpoint = self._get_endpoint_url( 1342 await self._get_openid_configuration(), "token_endpoint" 1343 ) 1344 async with self._get_httpx_client() as client: 1345 request = self._get_auth_exchange_token_request( 1346 client, 1347 endpoint=token_endpoint, 1348 code=code, 1349 redirect_uri=redirect_uri, 1350 code_verifier=code_verifier, 1351 ) 1352 response = await client.send(request) 1353 1354 self._handle_request_error(response) 1355 1356 return response.json()
Async Fief authentication client.
928 def __init__( 929 self, 930 base_url: str, 931 client_id: str, 932 client_secret: Optional[str] = None, 933 *, 934 encryption_key: Optional[str] = None, 935 host: Optional[str] = None, 936 verify: VerifyTypes = True, 937 cert: Optional[CertTypes] = None, 938 ) -> None: 939 super().__init__( 940 base_url, 941 client_id, 942 client_secret, 943 encryption_key=encryption_key, 944 host=host, 945 verify=verify, 946 cert=cert, 947 )
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.
949 async def auth_url( 950 self, 951 redirect_uri: str, 952 *, 953 state: Optional[str] = None, 954 scope: Optional[list[str]] = None, 955 code_challenge: Optional[str] = None, 956 code_challenge_method: Optional[str] = None, 957 lang: Optional[str] = None, 958 extras_params: Optional[Mapping[str, str]] = None, 959 ) -> str: 960 """ 961 Return an authorization URL. 962 963 :param redirect_uri: Your callback URI where the user will be redirected after Fief authentication. 964 :param state: Optional string that will be returned back in the callback parameters to allow you to retrieve state information. 965 :param scope: Optional list of scopes to ask for. 966 :param code_challenge: Optional code challenge for 967 [PKCE process](https://docs.fief.dev/going-further/pkce/). 968 :param code_challenge_method: Method used to hash the PKCE code challenge. 969 :param lang: Optional parameter to set the user locale. 970 Should be a valid [RFC 3066](https://www.rfc-editor.org/rfc/rfc3066) language identifier, like `fr` or `pt-PT`. 971 If not provided, the user locale is determined by their browser settings. 972 :param extras_params: Optional dictionary containing [specific parameters](https://docs.fief.dev/going-further/authorize-url/). 973 974 **Example:** 975 976 ```py 977 auth_url = await fief.auth_url("http://localhost:8000/callback", scope=["openid"]) 978 ``` 979 """ 980 openid_configuration = await self._get_openid_configuration() 981 return self._auth_url( 982 openid_configuration, 983 redirect_uri, 984 state=state, 985 scope=scope, 986 code_challenge=code_challenge, 987 code_challenge_method=code_challenge_method, 988 lang=lang, 989 extras_params=extras_params, 990 )
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
orpt-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"])
992 async def auth_callback( 993 self, code: str, redirect_uri: str, *, code_verifier: Optional[str] = None 994 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 995 """ 996 Return a `FiefTokenResponse` and `FiefUserInfo` in exchange of an authorization code. 997 998 :param code: The authorization code. 999 :param redirect_uri: The exact same `redirect_uri` you passed to the authorization URL. 1000 :param code_verifier: The raw 1001 [PKCE](https://docs.fief.dev/going-further/pkce/) code used to generate the code challenge during authorization. 1002 1003 **Example:** 1004 1005 ```py 1006 tokens, userinfo = await fief.auth_callback("CODE", "http://localhost:8000/callback") 1007 ``` 1008 """ 1009 token_response = await self._auth_exchange_token( 1010 code, redirect_uri, code_verifier=code_verifier 1011 ) 1012 jwks = await self._get_jwks() 1013 userinfo = self._decode_id_token( 1014 token_response["id_token"], 1015 jwks, 1016 code=code, 1017 access_token=token_response.get("access_token"), 1018 ) 1019 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")
1021 async def auth_refresh_token( 1022 self, refresh_token: str, *, scope: Optional[list[str]] = None 1023 ) -> tuple[FiefTokenResponse, FiefUserInfo]: 1024 """ 1025 Return fresh `FiefTokenResponse` and `FiefUserInfo` in exchange of a refresh token 1026 1027 :param refresh_token: A valid refresh token. 1028 :param scope: Optional list of scopes to ask for. 1029 If not provided, the access token will share the same list of scopes as requested the first time. 1030 Otherwise, it should be a subset of the original list of scopes. 1031 1032 **Example:** 1033 1034 ```py 1035 tokens, userinfo = await fief.auth_refresh_token("REFRESH_TOKEN") 1036 ``` 1037 """ 1038 token_endpoint = self._get_endpoint_url( 1039 await self._get_openid_configuration(), "token_endpoint" 1040 ) 1041 async with self._get_httpx_client() as client: 1042 request = self._get_auth_refresh_token_request( 1043 client, 1044 endpoint=token_endpoint, 1045 refresh_token=refresh_token, 1046 scope=scope, 1047 ) 1048 response = await client.send(request) 1049 1050 self._handle_request_error(response) 1051 1052 token_response = response.json() 1053 1054 jwks = await self._get_jwks() 1055 userinfo = self._decode_id_token( 1056 token_response["id_token"], 1057 jwks, 1058 access_token=token_response.get("access_token"), 1059 ) 1060 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")
1062 async def validate_access_token( 1063 self, 1064 access_token: str, 1065 *, 1066 required_scope: Optional[list[str]] = None, 1067 required_acr: Optional[FiefACR] = None, 1068 required_permissions: Optional[list[str]] = None, 1069 ) -> FiefAccessTokenInfo: 1070 """ 1071 Check if an access token is valid and optionally that it has a required list of scopes, 1072 or a required list of [permissions](https://docs.fief.dev/getting-started/access-control/). 1073 Returns a `FiefAccessTokenInfo`. 1074 1075 :param access_token: The access token to validate. 1076 :param required_scope: Optional list of scopes to check for. 1077 :param required_acr: Optional minimum ACR level required. 1078 Read more: https://docs.fief.dev/going-further/acr/ 1079 :param required_permissions: Optional list of permissions to check for. 1080 1081 **Example: Validate access token with required scopes** 1082 1083 ```py 1084 try: 1085 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_scope=["required_scope"]) 1086 except FiefAccessTokenInvalid: 1087 print("Invalid access token") 1088 except FiefAccessTokenExpired: 1089 print("Expired access token") 1090 except FiefAccessTokenMissingScope: 1091 print("Missing required scope") 1092 1093 print(access_token_info) 1094 ``` 1095 1096 **Example: Validate access token with minimum ACR level** 1097 1098 ```py 1099 try: 1100 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_acr=FiefACR.LEVEL_ONE) 1101 except FiefAccessTokenInvalid: 1102 print("Invalid access token") 1103 except FiefAccessTokenExpired: 1104 print("Expired access token") 1105 except FiefAccessTokenACRTooLow: 1106 print("ACR too low") 1107 1108 print(access_token_info) 1109 ``` 1110 1111 **Example: Validate access token with required permissions** 1112 1113 ```py 1114 try: 1115 access_token_info = await fief.validate_access_token("ACCESS_TOKEN", required_permissions=["castles:create", "castles:read"]) 1116 except FiefAccessTokenInvalid: 1117 print("Invalid access token") 1118 except FiefAccessTokenExpired: 1119 print("Expired access token") 1120 except FiefAccessTokenMissingPermission: 1121 print("Missing required permission") 1122 1123 print(access_token_info) 1124 ``` 1125 """ 1126 jwks = await self._get_jwks() 1127 return self._validate_access_token( 1128 access_token, 1129 jwks, 1130 required_scope=required_scope, 1131 required_acr=required_acr, 1132 required_permissions=required_permissions, 1133 )
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)
1135 async def userinfo(self, access_token: str) -> FiefUserInfo: 1136 """ 1137 Return fresh `FiefUserInfo` from the Fief API using a valid access token. 1138 1139 :param access_token: A valid access token. 1140 1141 **Example:** 1142 1143 ```py 1144 userinfo = await fief.userinfo("ACCESS_TOKEN") 1145 ``` 1146 """ 1147 userinfo_endpoint = self._get_endpoint_url( 1148 await self._get_openid_configuration(), "userinfo_endpoint" 1149 ) 1150 async with self._get_httpx_client() as client: 1151 request = self._get_userinfo_request( 1152 client, endpoint=userinfo_endpoint, access_token=access_token 1153 ) 1154 response = await client.send(request) 1155 1156 self._handle_request_error(response) 1157 1158 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")
1160 async def update_profile( 1161 self, access_token: str, data: dict[str, Any] 1162 ) -> FiefUserInfo: 1163 """ 1164 Update user information with the Fief API using a valid access token. 1165 1166 :param access_token: A valid access token. 1167 :param data: A dictionary containing the data to update. 1168 1169 **Example: Update user field** 1170 1171 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. 1172 1173 ```py 1174 userinfo = await fief.update_profile("ACCESS_TOKEN", { "fields": { "first_name": "Anne" } }) 1175 ``` 1176 """ 1177 update_profile_endpoint = f"{self.base_url}/api/profile" 1178 1179 async with self._get_httpx_client() as client: 1180 request = self._get_update_profile_request( 1181 client, 1182 endpoint=update_profile_endpoint, 1183 access_token=access_token, 1184 data=data, 1185 ) 1186 response = await client.send(request) 1187 1188 self._handle_request_error(response) 1189 1190 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" } })
1192 async def change_password( 1193 self, access_token: str, new_password: str 1194 ) -> FiefUserInfo: 1195 """ 1196 Change the user password with the Fief API using a valid access token. 1197 1198 **An access token with an ACR of at least level 1 is required.** 1199 1200 :param access_token: A valid access token. 1201 :param new_password: The new password. 1202 1203 **Example** 1204 1205 ```py 1206 userinfo = await fief.change_password("ACCESS_TOKEN", "herminetincture") 1207 ``` 1208 """ 1209 change_password_profile_endpoint = f"{self.base_url}/api/password" 1210 1211 async with self._get_httpx_client() as client: 1212 request = self._get_change_password_request( 1213 client, 1214 endpoint=change_password_profile_endpoint, 1215 access_token=access_token, 1216 new_password=new_password, 1217 ) 1218 response = await client.send(request) 1219 1220 self._handle_request_error(response) 1221 1222 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")
1224 async def email_change(self, access_token: str, email: str) -> FiefUserInfo: 1225 """ 1226 Request an email change with the Fief API using a valid access token. 1227 1228 The user will receive a verification code on this new email address. 1229 It shall be used with the method `email_verify` to complete the modification. 1230 1231 **An access token with an ACR of at least level 1 is required.** 1232 1233 :param access_token: A valid access token. 1234 :param email: The new email address. 1235 1236 **Example** 1237 1238 ```py 1239 userinfo = await fief.email_change("ACCESS_TOKEN", "anne@nantes.city") 1240 ``` 1241 """ 1242 email_change_endpoint = f"{self.base_url}/api/email/change" 1243 1244 async with self._get_httpx_client() as client: 1245 request = self._get_email_change_request( 1246 client, 1247 endpoint=email_change_endpoint, 1248 access_token=access_token, 1249 email=email, 1250 ) 1251 response = await client.send(request) 1252 1253 self._handle_request_error(response) 1254 1255 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")
1257 async def email_verify(self, access_token: str, code: str) -> FiefUserInfo: 1258 """ 1259 Verify the user email with the Fief API using a valid access token and verification code. 1260 1261 **An access token with an ACR of at least level 1 is required.** 1262 1263 :param access_token: A valid access token. 1264 :param code: The verification code received by email. 1265 1266 **Example** 1267 1268 ```py 1269 userinfo = fief.email_verify("ACCESS_TOKEN", "ABCDE") 1270 ``` 1271 """ 1272 email_verify_endpoint = f"{self.base_url}/api/email/verify" 1273 1274 async with self._get_httpx_client() as client: 1275 request = self._get_email_verify_request( 1276 client, 1277 endpoint=email_verify_endpoint, 1278 access_token=access_token, 1279 code=code, 1280 ) 1281 response = await client.send(request) 1282 1283 self._handle_request_error(response) 1284 1285 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")
1287 async def logout_url(self, redirect_uri: str) -> str: 1288 """ 1289 Returns a logout URL. If you redirect the user to this page, Fief will clear the session stored on its side. 1290 1291 **You're still responsible for clearing your own session mechanism if any.** 1292 1293 :param redirect_uri: A valid URL where the user will be redirected after the logout process: 1294 1295 **Example:** 1296 1297 ```py 1298 logout_url = await fief.logout_url("http://localhost:8000") 1299 ``` 1300 """ 1301 params = {"redirect_uri": redirect_uri} 1302 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")
56class FiefTokenResponse(TypedDict): 57 """ 58 Typed dictionary containing the tokens and related information returned by Fief after a successful authentication. 59 """ 60 61 access_token: str 62 """Access token you can use to call the Fief API.""" 63 id_token: str 64 """ID token containing user information.""" 65 token_type: str 66 """Type of token, usually `bearer`.""" 67 expires_in: int 68 """Number of seconds after which the tokens will expire.""" 69 refresh_token: Optional[str] 70 """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.
Token provided only if scope offline_access
was granted. Allows you to retrieve fresh tokens using the Fief.auth_refresh_token
method.
73class FiefAccessTokenInfo(TypedDict): 74 """ 75 Typed dictionary containing information about the access token. 76 77 **Example:** 78 79 ```json 80 { 81 "id": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e", 82 "scope": ["openid", "required_scope"], 83 "acr": "1", 84 "permissions": ["castles:read", "castles:create", "castles:update", "castles:delete"], 85 "access_token": "ACCESS_TOKEN", 86 } 87 ``` 88 """ 89 90 id: uuid.UUID 91 """ID of the user.""" 92 scope: list[str] 93 """List of granted scopes for this access token.""" 94 acr: FiefACR 95 """Level of Authentication Context class Reference.""" 96 permissions: list[str] 97 """List of [granted permissions](https://docs.fief.dev/getting-started/access-control/) for this user.""" 98 access_token: str 99 """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",
}
102class FiefUserInfo(TypedDict): 103 """ 104 Dictionary containing user information. 105 106 **Example:** 107 108 ```json 109 { 110 "sub": "aeeb8bfa-e8f4-4724-9427-c3d5af66190e", 111 "email": "anne@bretagne.duchy", 112 "tenant_id": "c91ecb7f-359c-4244-8385-51ecd6c0d06b", 113 "fields": { 114 "first_name": "Anne", 115 "last_name": "De Bretagne" 116 } 117 } 118 ``` 119 """ 120 121 sub: str 122 """ 123 ID of the user. 124 """ 125 email: str 126 """ 127 Email address of the user. 128 """ 129 tenant_id: str 130 """ 131 ID of the [tenant](https://docs.fief.dev/getting-started/tenants/) associated to the user. 132 """ 133 fields: dict[str, Any] 134 """ 135 [User fields](https://docs.fief.dev/getting-started/user-fields/) values for this user, indexed by their slug. 136 """
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"
}
}
Base Fief client error.
165class FiefAccessTokenACRTooLow(FiefError): 166 """The access token doesn't meet the minimum ACR level."""
The access token doesn't meet the minimum ACR level.
The access token is expired.
169class FiefAccessTokenMissingPermission(FiefError): 170 """The access token is missing a required permission."""
The access token is missing a required permission.
161class FiefAccessTokenMissingScope(FiefError): 162 """The access token is missing a required scope."""
The access token is missing a required scope.
The access token is invalid.
The ID token is invalid.
143class FiefRequestError(FiefError): 144 """The request to Fief server resulted in an error.""" 145 146 def __init__(self, status_code: int, detail: str) -> None: 147 self.status_code = status_code 148 self.detail = detail 149 self.message = f"[{status_code}] - {detail}" 150 super().__init__(self.message)
The request to Fief server resulted in an error.