From aa1667ce92c3078995f69c4d139ce8ff7ca915f2 Mon Sep 17 00:00:00 2001 From: pfvatterott Date: Fri, 17 Apr 2026 09:33:02 -0600 Subject: [PATCH 1/3] new-apis-april-2026 --- .gitignore | 3 ++ propelauth_fastapi/__init__.py | 75 +++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 142def7..1d20579 100644 --- a/.gitignore +++ b/.gitignore @@ -394,3 +394,6 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,python,venv,virtualenv,pycharm+all,vim,emacs,macos,windows +pyproject.toml +uv.lock +.python-version diff --git a/propelauth_fastapi/__init__.py b/propelauth_fastapi/__init__.py index 3af3303..ff7ae88 100644 --- a/propelauth_fastapi/__init__.py +++ b/propelauth_fastapi/__init__.py @@ -3,6 +3,8 @@ from fastapi import Depends, HTTPException, Header from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from propelauth_py import ( + SetOidcIdpMetadataRequest, + SocialLoginTokenProvider, TokenVerificationMetadata, configure_logging, init_base_auth, @@ -264,10 +266,11 @@ def create_magic_link( expires_in_hours: Optional[int] = None, create_new_user_if_one_doesnt_exist: Optional[bool] = None, user_signup_query_parameters: Optional[Dict[str, Any]] = None, - expire_after_first_use: Optional[bool] = None + expire_after_first_use: Optional[bool] = None, + requires_interstitial: Optional[bool] = None, ): return self.auth.create_magic_link( - email, redirect_to_url, expires_in_hours, create_new_user_if_one_doesnt_exist, user_signup_query_parameters, expire_after_first_use + email, redirect_to_url, expires_in_hours, create_new_user_if_one_doesnt_exist, user_signup_query_parameters, expire_after_first_use, requires_interstitial ) def create_access_token(self, user_id: str, duration_in_minutes: int, active_org_id: Optional[str] = None): @@ -328,10 +331,13 @@ def update_org_metadata( domain: Optional[str] = None, require_2fa_by: Optional[str] = None, extra_domains: Optional[List[str]] = None, + password_rotation_enabled: Optional[bool] = None, + password_rotation_history_size: Optional[int] = None, + password_rotation_period: Optional[int] = None, ): return self.auth.update_org_metadata( org_id, name, can_setup_saml, metadata, max_users, - can_join_on_email_domain_match, members_must_have_email_domain_match, domain, require_2fa_by, extra_domains + can_join_on_email_domain_match, members_must_have_email_domain_match, domain, require_2fa_by, extra_domains, password_rotation_enabled, password_rotation_history_size, password_rotation_period ) def subscribe_org_to_role_mapping(self, org_id: str, custom_role_mapping_name: str): @@ -410,9 +416,10 @@ def create_api_key( org_id: Optional[str] = None, user_id: Optional[str] = None, expires_at_seconds: Optional[str] = None, - metadata: Optional[Dict[str, Any]] = None + metadata: Optional[Dict[str, Any]] = None, + display_name: Optional[str] = None, ): - return self.auth.create_api_key(org_id, user_id, expires_at_seconds, metadata) + return self.auth.create_api_key(org_id, user_id, expires_at_seconds, metadata, display_name) def update_api_key(self, api_key_id: str, expires_at_seconds: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, set_to_never_expire: Optional[bool] = None): return self.auth.update_api_key(api_key_id, expires_at_seconds, metadata, set_to_never_expire) @@ -434,6 +441,9 @@ def fetch_saml_sp_metadata(self, org_id: str): def set_saml_idp_metadata(self, org_id: str, saml_idp_metadata: SamlIdpMetadata): return self.auth.set_saml_idp_metadata(org_id=org_id, saml_idp_metadata=saml_idp_metadata) + + def set_oidc_idp_metadata(self, request: SetOidcIdpMetadataRequest): + return self.auth.set_oidc_idp_metadata(request) def saml_go_live(self, org_id: str): return self.auth.saml_go_live(org_id) @@ -490,6 +500,7 @@ def import_api_key( user_id: Optional[str] = None, expires_at_seconds: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, + display_name: Optional[str] = None, ): return self.auth.import_api_key( api_key_token, @@ -497,6 +508,7 @@ def import_api_key( user_id, expires_at_seconds, metadata, + display_name, ) def send_sms_mfa_code( @@ -529,6 +541,23 @@ def verify_sms_challenge( def fetch_employee_by_id(self, employee_id: str): return self.auth.fetch_employee_by_id(employee_id) + + def fetch_org_scim_groups(self, org_id: str, user_id: Optional[str] = None, page_size: int = 10, page_number: int = 0): + return self.auth.fetch_org_scim_groups( + org_id, + user_id, + page_size, + page_number + ) + + def fetch_scim_group(self, org_id: str, group_id: str): + return self.auth.fetch_scim_group(org_id, group_id) + + def fetch_user_oauth_tokens(self, user_id: str): + return self.auth.fetch_user_oauth_tokens(user_id) + + def fetch_fresh_token_from_provider(self, user_id: str, provider: SocialLoginTokenProvider): + return self.auth.fetch_fresh_token_from_provider(user_id, provider) class FastAPIAuthAsync(): def __init__( @@ -714,10 +743,11 @@ async def create_magic_link( expires_in_hours: Optional[int] = None, create_new_user_if_one_doesnt_exist: Optional[bool] = None, user_signup_query_parameters: Optional[Dict[str, Any]] = None, - expire_after_first_use: Optional[bool] = None + expire_after_first_use: Optional[bool] = None, + requires_interstitial: Optional[bool] = None, ): return await self.auth.create_magic_link( - email, redirect_to_url, expires_in_hours, create_new_user_if_one_doesnt_exist, user_signup_query_parameters, expire_after_first_use + email, redirect_to_url, expires_in_hours, create_new_user_if_one_doesnt_exist, user_signup_query_parameters, expire_after_first_use, requires_interstitial ) async def create_access_token(self, user_id: str, duration_in_minutes: int, active_org_id: Optional[str] = None): @@ -778,10 +808,13 @@ async def update_org_metadata( domain: Optional[str] = None, require_2fa_by: Optional[str] = None, extra_domains: Optional[List[str]] = None, + password_rotation_enabled: Optional[bool] = None, + password_rotation_history_size: Optional[int] = None, + password_rotation_period: Optional[int] = None, ): return await self.auth.update_org_metadata( org_id, name, can_setup_saml, metadata, max_users, - can_join_on_email_domain_match, members_must_have_email_domain_match, domain, require_2fa_by, extra_domains + can_join_on_email_domain_match, members_must_have_email_domain_match, domain, require_2fa_by, extra_domains, password_rotation_enabled, password_rotation_history_size, password_rotation_period ) async def subscribe_org_to_role_mapping(self, org_id: str, custom_role_mapping_name: str): @@ -860,9 +893,10 @@ async def create_api_key( org_id: Optional[str] = None, user_id: Optional[str] = None, expires_at_seconds: Optional[str] = None, - metadata: Optional[Dict[str, Any]] = None + metadata: Optional[Dict[str, Any]] = None, + display_name: Optional[str] = None, ): - return await self.auth.create_api_key(org_id, user_id, expires_at_seconds, metadata) + return await self.auth.create_api_key(org_id, user_id, expires_at_seconds, metadata, display_name) async def update_api_key(self, api_key_id: str, expires_at_seconds: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, set_to_never_expire: Optional[bool] = None): return await self.auth.update_api_key(api_key_id, expires_at_seconds, metadata, set_to_never_expire) @@ -884,6 +918,9 @@ async def fetch_saml_sp_metadata(self, org_id: str): async def set_saml_idp_metadata(self, org_id: str, saml_idp_metadata: SamlIdpMetadata): return await self.auth.set_saml_idp_metadata(org_id=org_id, saml_idp_metadata=saml_idp_metadata) + + async def set_oidc_idp_metadata(self, request: SetOidcIdpMetadataRequest): + return await self.auth.set_oidc_idp_metadata(request) async def saml_go_live(self, org_id: str): return await self.auth.saml_go_live(org_id) @@ -992,6 +1029,23 @@ async def fetch_employee_by_id(self, employee_id: str): return await self.auth.fetch_employee_by_id( employee_id ) + + async def fetch_org_scim_groups(self, org_id: str, user_id: Optional[str] = None, page_size: int = 10, page_number: int = 0): + return await self.auth.fetch_org_scim_groups( + org_id, + user_id, + page_size, + page_number + ) + + async def fetch_scim_group(self, org_id: str, group_id: str): + return await self.auth.fetch_scim_group(org_id, group_id) + + async def fetch_user_oauth_tokens(self, user_id: str): + return await self.auth.fetch_user_oauth_tokens(user_id) + + async def fetch_fresh_token_from_provider(self, user_id: str, provider: SocialLoginTokenProvider): + return await self.auth.fetch_fresh_token_from_provider(user_id, provider) def init_auth( @@ -1018,4 +1072,3 @@ def init_auth_async( """Fetches metadata required to validate access tokens and returns auth decorators and utilities""" return FastAPIAuthAsync(auth_url=auth_url, integration_api_key=api_key, token_verification_metadata=token_verification_metadata, debug_mode=debug_mode, httpx_client=httpx_client) - From 7607d98af7386a32f4a618440692f2f76c13f13c Mon Sep 17 00:00:00 2001 From: pfvatterott Date: Fri, 17 Apr 2026 13:20:39 -0600 Subject: [PATCH 2/3] fixes --- propelauth_fastapi/__init__.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/propelauth_fastapi/__init__.py b/propelauth_fastapi/__init__.py index ff7ae88..b5b75be 100644 --- a/propelauth_fastapi/__init__.py +++ b/propelauth_fastapi/__init__.py @@ -550,8 +550,19 @@ def fetch_org_scim_groups(self, org_id: str, user_id: Optional[str] = None, page page_number ) - def fetch_scim_group(self, org_id: str, group_id: str): - return self.auth.fetch_scim_group(org_id, group_id) + def fetch_scim_group( + self, + org_id: str, + group_id: str, + members_page_size: int = 10, + members_page_number: int = 0, + ): + return self.auth.fetch_scim_group( + org_id=org_id, + group_id=group_id, + members_page_size=members_page_size, + members_page_number=members_page_number, + ) def fetch_user_oauth_tokens(self, user_id: str): return self.auth.fetch_user_oauth_tokens(user_id) @@ -969,6 +980,7 @@ async def import_api_key( user_id: Optional[str] = None, expires_at_seconds: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, + display_name: Optional[str] = None, ): return await self.auth.import_api_key( api_key_token, @@ -976,6 +988,7 @@ async def import_api_key( user_id, expires_at_seconds, metadata, + display_name ) async def invite_user_to_org_by_user_id( @@ -1038,8 +1051,19 @@ async def fetch_org_scim_groups(self, org_id: str, user_id: Optional[str] = None page_number ) - async def fetch_scim_group(self, org_id: str, group_id: str): - return await self.auth.fetch_scim_group(org_id, group_id) + async def fetch_scim_group( + self, + org_id: str, + group_id: str, + members_page_size: int = 10, + members_page_number: int = 0, + ): + return await self.auth.fetch_scim_group( + org_id=org_id, + group_id=group_id, + members_page_size=members_page_size, + members_page_number=members_page_number, + ) async def fetch_user_oauth_tokens(self, user_id: str): return await self.auth.fetch_user_oauth_tokens(user_id) From 07d294b19d3c2972bcbcab2e6e8f35a526651675 Mon Sep 17 00:00:00 2001 From: pfvatterott Date: Wed, 22 Apr 2026 11:31:00 -0600 Subject: [PATCH 3/3] update .gitignore --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1d20579..42700e4 100644 --- a/.gitignore +++ b/.gitignore @@ -393,7 +393,3 @@ $RECYCLE.BIN/ *.lnk # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,python,venv,virtualenv,pycharm+all,vim,emacs,macos,windows - -pyproject.toml -uv.lock -.python-version