From 3984ad3a69a973262ae307aa8c6ea68a830d4fb1 Mon Sep 17 00:00:00 2001 From: Stytch Codegen Bot Date: Mon, 3 Nov 2025 17:22:40 +0000 Subject: [PATCH] Support SHA-512 password migration --- stytch/b2b/api/idp_oauth.py | 8 +++ stytch/b2b/api/organizations.py | 4 +- stytch/b2b/api/organizations_members.py | 4 +- stytch/b2b/api/passwords.py | 25 +++++++-- stytch/b2b/models/discovery_organizations.py | 48 ++++++++-------- stytch/b2b/models/passwords.py | 1 + stytch/b2b/models/scim.py | 7 --- stytch/b2b/models/sso.py | 58 -------------------- stytch/consumer/api/idp_oauth.py | 8 +++ stytch/consumer/api/passwords.py | 25 +++++++-- stytch/consumer/api/users.py | 4 +- stytch/consumer/models/passwords.py | 12 ++++ stytch/version.py | 2 +- 13 files changed, 102 insertions(+), 104 deletions(-) diff --git a/stytch/b2b/api/idp_oauth.py b/stytch/b2b/api/idp_oauth.py index 5d00d544..5a8ae553 100644 --- a/stytch/b2b/api/idp_oauth.py +++ b/stytch/b2b/api/idp_oauth.py @@ -168,6 +168,7 @@ def authorize( state: Optional[str] = None, nonce: Optional[str] = None, code_challenge: Optional[str] = None, + resources: Optional[List[str]] = None, ) -> AuthorizeResponse: """Completes a request for authorization of a Connected App to access a Member's account. @@ -201,6 +202,7 @@ def authorize( - state: An opaque value used to maintain state between the request and callback. - nonce: A string used to associate a client session with an ID token to mitigate replay attacks. - code_challenge: A base64url encoded challenge derived from the code verifier for PKCE flows. + - resources: (no documentation yet) """ # noqa headers: Dict[str, str] = {} data: Dict[str, Any] = { @@ -226,6 +228,8 @@ def authorize( data["nonce"] = nonce if code_challenge is not None: data["code_challenge"] = code_challenge + if resources is not None: + data["resources"] = resources url = self.api_base.url_for("/v1/b2b/idp/oauth/authorize", data) res = self.sync_client.post(url, data, headers) @@ -246,6 +250,7 @@ async def authorize_async( state: Optional[str] = None, nonce: Optional[str] = None, code_challenge: Optional[str] = None, + resources: Optional[List[str]] = None, ) -> AuthorizeResponse: """Completes a request for authorization of a Connected App to access a Member's account. @@ -279,6 +284,7 @@ async def authorize_async( - state: An opaque value used to maintain state between the request and callback. - nonce: A string used to associate a client session with an ID token to mitigate replay attacks. - code_challenge: A base64url encoded challenge derived from the code verifier for PKCE flows. + - resources: (no documentation yet) """ # noqa headers: Dict[str, str] = {} data: Dict[str, Any] = { @@ -304,6 +310,8 @@ async def authorize_async( data["nonce"] = nonce if code_challenge is not None: data["code_challenge"] = code_challenge + if resources is not None: + data["resources"] = resources url = self.api_base.url_for("/v1/b2b/idp/oauth/authorize", data) res = await self.async_client.post(url, data, headers) diff --git a/stytch/b2b/api/organizations.py b/stytch/b2b/api/organizations.py index e4a17778..fd683158 100644 --- a/stytch/b2b/api/organizations.py +++ b/stytch/b2b/api/organizations.py @@ -979,7 +979,7 @@ def search( query: Optional[Union[SearchQuery, Dict[str, Any]]] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/minute. Search across your Organizations. Returns an array of Organization objects. @@ -1008,7 +1008,7 @@ async def search_async( query: Optional[SearchQuery] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/minute. Search across your Organizations. Returns an array of Organization objects. diff --git a/stytch/b2b/api/organizations_members.py b/stytch/b2b/api/organizations_members.py index aa6b28b0..779cb3a9 100644 --- a/stytch/b2b/api/organizations_members.py +++ b/stytch/b2b/api/organizations_members.py @@ -518,7 +518,7 @@ def search( method_options: Optional[SearchRequestOptions] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/minute. Search for Members within specified Organizations. An array with at least one `organization_id` is required. Submitting an empty `query` returns all non-deleted Members within the specified Organizations. @@ -556,7 +556,7 @@ async def search_async( method_options: Optional[SearchRequestOptions] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/minute. Search for Members within specified Organizations. An array with at least one `organization_id` is required. Submitting an empty `query` returns all non-deleted Members within the specified Organizations. diff --git a/stytch/b2b/api/passwords.py b/stytch/b2b/api/passwords.py index a1a6178d..a13b3de2 100644 --- a/stytch/b2b/api/passwords.py +++ b/stytch/b2b/api/passwords.py @@ -25,6 +25,7 @@ PBKDF2Config, ScryptConfig, SHA1Config, + SHA512Config, ) from stytch.core.api_base import ApiBase from stytch.core.http.client import AsyncClient, SyncClient @@ -135,6 +136,7 @@ def migrate( md_5_config: Optional[Union[MD5Config, Dict[str, Any]]] = None, argon_2_config: Optional[Union[Argon2Config, Dict[str, Any]]] = None, sha_1_config: Optional[Union[SHA1Config, Dict[str, Any]]] = None, + sha_512_config: Optional[Union[SHA512Config, Dict[str, Any]]] = None, scrypt_config: Optional[Union[ScryptConfig, Dict[str, Any]]] = None, pbkdf_2_config: Optional[Union[PBKDF2Config, Dict[str, Any]]] = None, name: Optional[str] = None, @@ -151,7 +153,7 @@ def migrate( Adds an existing password to a Member's email that doesn't have a password yet. - We support migrating members from passwords stored with bcrypt, scrypt, argon2, MD-5, SHA-1, and PBKDF2. This endpoint has a rate limit of 100 requests per second. + We support migrating members from passwords stored with bcrypt, scrypt, argon2, MD-5, SHA-1, SHA-512, and PBKDF2. This endpoint has a rate limit of 100 requests per second. The Member's email will be marked as verified when you use this endpoint. @@ -160,11 +162,12 @@ def migrate( Fields: - email_address: The email address of the Member. - hash: The password hash. For a Scrypt or PBKDF2 hash, the hash needs to be a base64 encoded string. - - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, and `pbkdf_2` are supported. + - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, `sha_512`, and `pbkdf_2` are supported. - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience. - md_5_config: Optional parameters for MD-5 hash types. - argon_2_config: Required parameters if the argon2 hex form, as opposed to the encoded form, is supplied. - sha_1_config: Optional parameters for SHA-1 hash types. + - sha_512_config: Optional parameters for SHA-512 hash types. - scrypt_config: Required parameters if the scrypt is not provided in a **PHC encoded form**. - pbkdf_2_config: Required additional parameters for PBKDF2 hash keys. Note that we use the SHA-256 by default, please contact [support@stytch.com](mailto:support@stytch.com) if you use another hashing function. - name: The name of the Member. Each field in the name object is optional. @@ -209,6 +212,12 @@ def migrate( data["sha_1_config"] = ( sha_1_config if isinstance(sha_1_config, dict) else sha_1_config.dict() ) + if sha_512_config is not None: + data["sha_512_config"] = ( + sha_512_config + if isinstance(sha_512_config, dict) + else sha_512_config.dict() + ) if scrypt_config is not None: data["scrypt_config"] = ( scrypt_config @@ -251,6 +260,7 @@ async def migrate_async( md_5_config: Optional[MD5Config] = None, argon_2_config: Optional[Argon2Config] = None, sha_1_config: Optional[SHA1Config] = None, + sha_512_config: Optional[SHA512Config] = None, scrypt_config: Optional[ScryptConfig] = None, pbkdf_2_config: Optional[PBKDF2Config] = None, name: Optional[str] = None, @@ -267,7 +277,7 @@ async def migrate_async( Adds an existing password to a Member's email that doesn't have a password yet. - We support migrating members from passwords stored with bcrypt, scrypt, argon2, MD-5, SHA-1, and PBKDF2. This endpoint has a rate limit of 100 requests per second. + We support migrating members from passwords stored with bcrypt, scrypt, argon2, MD-5, SHA-1, SHA-512, and PBKDF2. This endpoint has a rate limit of 100 requests per second. The Member's email will be marked as verified when you use this endpoint. @@ -276,11 +286,12 @@ async def migrate_async( Fields: - email_address: The email address of the Member. - hash: The password hash. For a Scrypt or PBKDF2 hash, the hash needs to be a base64 encoded string. - - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, and `pbkdf_2` are supported. + - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, `sha_512`, and `pbkdf_2` are supported. - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience. - md_5_config: Optional parameters for MD-5 hash types. - argon_2_config: Required parameters if the argon2 hex form, as opposed to the encoded form, is supplied. - sha_1_config: Optional parameters for SHA-1 hash types. + - sha_512_config: Optional parameters for SHA-512 hash types. - scrypt_config: Required parameters if the scrypt is not provided in a **PHC encoded form**. - pbkdf_2_config: Required additional parameters for PBKDF2 hash keys. Note that we use the SHA-256 by default, please contact [support@stytch.com](mailto:support@stytch.com) if you use another hashing function. - name: The name of the Member. Each field in the name object is optional. @@ -325,6 +336,12 @@ async def migrate_async( data["sha_1_config"] = ( sha_1_config if isinstance(sha_1_config, dict) else sha_1_config.dict() ) + if sha_512_config is not None: + data["sha_512_config"] = ( + sha_512_config + if isinstance(sha_512_config, dict) + else sha_512_config.dict() + ) if scrypt_config is not None: data["scrypt_config"] = ( scrypt_config diff --git a/stytch/b2b/models/discovery_organizations.py b/stytch/b2b/models/discovery_organizations.py index cc36e323..062d1862 100644 --- a/stytch/b2b/models/discovery_organizations.py +++ b/stytch/b2b/models/discovery_organizations.py @@ -29,6 +29,30 @@ class CreateRequestThirdPartyConnectedAppsAllowedType(str, enum.Enum): NOT_ALLOWED = "NOT_ALLOWED" +class ListResponse(ResponseBase): + """Response type for `Organizations.list`. + Fields: + - email_address: The email address. + - discovered_organizations: An array of `discovered_organization` objects tied to the `intermediate_session_token`, `session_token`, or `session_jwt`. See the [Discovered Organization Object](https://stytch.com/docs/b2b/api/discovered-organization-object) for complete details. + + Note that Organizations will only appear here under any of the following conditions: + 1. The end user is already a Member of the Organization. + 2. The end user is invited to the Organization. + 3. The end user can join the Organization because: + + a) The Organization allows JIT provisioning. + + b) The Organizations' allowed domains list contains the Member's email domain. + + c) The Organization has at least one other Member with a verified email address with the same domain as the end user (to prevent phishing attacks). + - organization_id_hint: If the intermediate session token is associated with a specific Organization, that Organization ID will be returned here. The Organization ID will be null if the intermediate session token was generated by a email magic link discovery or OAuth discovery flow. If a session token or session JWT is provided, the Organization ID hint will be null. + """ # noqa + + email_address: str + discovered_organizations: List[DiscoveredOrganization] + organization_id_hint: Optional[str] = None + + class CreateResponse(ResponseBase): """Response type for `Organizations.create`. Fields: @@ -56,27 +80,3 @@ class CreateResponse(ResponseBase): mfa_required: Optional[MfaRequired] = None primary_required: Optional[PrimaryRequired] = None member_device: Optional[DeviceInfo] = None - - -class ListResponse(ResponseBase): - """Response type for `Organizations.list`. - Fields: - - email_address: The email address. - - discovered_organizations: An array of `discovered_organization` objects tied to the `intermediate_session_token`, `session_token`, or `session_jwt`. See the [Discovered Organization Object](https://stytch.com/docs/b2b/api/discovered-organization-object) for complete details. - - Note that Organizations will only appear here under any of the following conditions: - 1. The end user is already a Member of the Organization. - 2. The end user is invited to the Organization. - 3. The end user can join the Organization because: - - a) The Organization allows JIT provisioning. - - b) The Organizations' allowed domains list contains the Member's email domain. - - c) The Organization has at least one other Member with a verified email address with the same domain as the end user (to prevent phishing attacks). - - organization_id_hint: If the intermediate session token is associated with a specific Organization, that Organization ID will be returned here. The Organization ID will be null if the intermediate session token was generated by a email magic link discovery or OAuth discovery flow. If a session token or session JWT is provided, the Organization ID hint will be null. - """ # noqa - - email_address: str - discovered_organizations: List[DiscoveredOrganization] - organization_id_hint: Optional[str] = None diff --git a/stytch/b2b/models/passwords.py b/stytch/b2b/models/passwords.py index f56f1c22..c20e0eb1 100644 --- a/stytch/b2b/models/passwords.py +++ b/stytch/b2b/models/passwords.py @@ -31,6 +31,7 @@ class MigrateRequestHashType(str, enum.Enum): ARGON_2I = "argon_2i" ARGON_2ID = "argon_2id" SHA_1 = "sha_1" + SHA_512 = "sha_512" SCRYPT = "scrypt" PHPASS = "phpass" PBKDF_2 = "pbkdf_2" diff --git a/stytch/b2b/models/scim.py b/stytch/b2b/models/scim.py index d964c4ac..413ac7eb 100644 --- a/stytch/b2b/models/scim.py +++ b/stytch/b2b/models/scim.py @@ -104,13 +104,6 @@ class SCIMGroup(pydantic.BaseModel): class SCIMGroupImplicitRoleAssignments(pydantic.BaseModel): - """ - Fields: - - role_id: The ID of the role. - - group_id: The ID of the group. - - group_name: (no documentation yet) - """ # noqa - role_id: str group_id: str group_name: str diff --git a/stytch/b2b/models/sso.py b/stytch/b2b/models/sso.py index 76b08d35..c27232a8 100644 --- a/stytch/b2b/models/sso.py +++ b/stytch/b2b/models/sso.py @@ -32,20 +32,6 @@ class AuthenticateRequestLocale(str, enum.Enum): class ConnectionImplicitRoleAssignment(pydantic.BaseModel): - """ - Fields: - - role_id: The unique identifier of the RBAC Role, provided by the developer and intended to be human-readable. - - Reserved `role_id`s that are predefined by Stytch include: - - * `stytch_member` - * `stytch_admin` - - Check out the [guide on Stytch default Roles](https://stytch.com/docs/b2b/guides/rbac/stytch-default) for a more detailed explanation. - - - """ # noqa - role_id: str @@ -88,21 +74,6 @@ def add_headers(self, headers: Dict[str, str]) -> Dict[str, str]: class GroupImplicitRoleAssignment(pydantic.BaseModel): - """ - Fields: - - role_id: The unique identifier of the RBAC Role, provided by the developer and intended to be human-readable. - - Reserved `role_id`s that are predefined by Stytch include: - - * `stytch_member` - * `stytch_admin` - - Check out the [guide on Stytch default Roles](https://stytch.com/docs/b2b/guides/rbac/stytch-default) for a more detailed explanation. - - - - group: The name of the group that grants the specified role assignment. - """ # noqa - role_id: str group: str @@ -139,39 +110,10 @@ class OIDCConnection(pydantic.BaseModel): class SAMLConnectionImplicitRoleAssignment(pydantic.BaseModel): - """ - Fields: - - role_id: The unique identifier of the RBAC Role, provided by the developer and intended to be human-readable. - - Reserved `role_id`s that are predefined by Stytch include: - - * `stytch_member` - * `stytch_admin` - - Check out the [guide on Stytch default Roles](https://stytch.com/docs/b2b/guides/rbac/stytch-default) for a more detailed explanation. - - - """ # noqa - role_id: str class SAMLGroupImplicitRoleAssignment(pydantic.BaseModel): - """ - Fields: - - role_id: The unique identifier of the RBAC Role, provided by the developer and intended to be human-readable. - - Reserved `role_id`s that are predefined by Stytch include: - - * `stytch_member` - * `stytch_admin` - - Check out the [guide on Stytch default Roles](https://stytch.com/docs/b2b/guides/rbac/stytch-default) for a more detailed explanation. - - - - group: The name of the group that grants the specified role assignment. - """ # noqa - role_id: str group: str diff --git a/stytch/consumer/api/idp_oauth.py b/stytch/consumer/api/idp_oauth.py index 7a71a55e..f91bbb4e 100644 --- a/stytch/consumer/api/idp_oauth.py +++ b/stytch/consumer/api/idp_oauth.py @@ -159,6 +159,7 @@ def authorize( state: Optional[str] = None, nonce: Optional[str] = None, code_challenge: Optional[str] = None, + resources: Optional[List[str]] = None, ) -> AuthorizeResponse: """Completes a request for authorization of a Connected App to access a User's account. @@ -191,6 +192,7 @@ def authorize( - state: An opaque value used to maintain state between the request and callback. - nonce: A string used to associate a client session with an ID token to mitigate replay attacks. - code_challenge: A base64url encoded challenge derived from the code verifier for PKCE flows. + - resources: (no documentation yet) """ # noqa headers: Dict[str, str] = {} data: Dict[str, Any] = { @@ -214,6 +216,8 @@ def authorize( data["nonce"] = nonce if code_challenge is not None: data["code_challenge"] = code_challenge + if resources is not None: + data["resources"] = resources url = self.api_base.url_for("/v1/idp/oauth/authorize", data) res = self.sync_client.post(url, data, headers) @@ -233,6 +237,7 @@ async def authorize_async( state: Optional[str] = None, nonce: Optional[str] = None, code_challenge: Optional[str] = None, + resources: Optional[List[str]] = None, ) -> AuthorizeResponse: """Completes a request for authorization of a Connected App to access a User's account. @@ -265,6 +270,7 @@ async def authorize_async( - state: An opaque value used to maintain state between the request and callback. - nonce: A string used to associate a client session with an ID token to mitigate replay attacks. - code_challenge: A base64url encoded challenge derived from the code verifier for PKCE flows. + - resources: (no documentation yet) """ # noqa headers: Dict[str, str] = {} data: Dict[str, Any] = { @@ -288,6 +294,8 @@ async def authorize_async( data["nonce"] = nonce if code_challenge is not None: data["code_challenge"] = code_challenge + if resources is not None: + data["resources"] = resources url = self.api_base.url_for("/v1/idp/oauth/authorize", data) res = await self.async_client.post(url, data, headers) diff --git a/stytch/consumer/api/passwords.py b/stytch/consumer/api/passwords.py index 5d1d20b1..a4e7c165 100644 --- a/stytch/consumer/api/passwords.py +++ b/stytch/consumer/api/passwords.py @@ -21,6 +21,7 @@ PBKDF2Config, ScryptConfig, SHA1Config, + SHA512Config, StrengthCheckResponse, ) from stytch.consumer.models.users import Name @@ -361,6 +362,7 @@ def migrate( md_5_config: Optional[Union[MD5Config, Dict[str, Any]]] = None, argon_2_config: Optional[Union[Argon2Config, Dict[str, Any]]] = None, sha_1_config: Optional[Union[SHA1Config, Dict[str, Any]]] = None, + sha_512_config: Optional[Union[SHA512Config, Dict[str, Any]]] = None, scrypt_config: Optional[Union[ScryptConfig, Dict[str, Any]]] = None, pbkdf_2_config: Optional[Union[PBKDF2Config, Dict[str, Any]]] = None, trusted_metadata: Optional[Dict[str, Any]] = None, @@ -372,15 +374,16 @@ def migrate( external_id: Optional[str] = None, roles: Optional[List[str]] = None, ) -> MigrateResponse: - """Adds an existing password to a User's email that doesn't have a password yet. We support migrating users from passwords stored with `bcrypt`, `scrypt`, `argon2`, `MD-5`, `SHA-1`, or `PBKDF2`. This endpoint has a rate limit of 100 requests per second. + """Adds an existing password to a User's email that doesn't have a password yet. We support migrating users from passwords stored with `bcrypt`, `scrypt`, `argon2`, `MD-5`, `SHA-1`, `SHA-512`, or `PBKDF2`. This endpoint has a rate limit of 100 requests per second. Fields: - email: The email address of the end user. - hash: The password hash. For a Scrypt or PBKDF2 hash, the hash needs to be a base64 encoded string. - - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, and `pbkdf_2` are supported. + - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, `sha_512`, and `pbkdf_2` are supported. - md_5_config: Optional parameters for MD-5 hash types. - argon_2_config: Required parameters if the argon2 hex form, as opposed to the encoded form, is supplied. - sha_1_config: Optional parameters for SHA-1 hash types. + - sha_512_config: Optional parameters for SHA-512 hash types. - scrypt_config: Required parameters if the scrypt is not provided in a [PHC encoded form](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#phc-string-format). - pbkdf_2_config: Required additional parameters for PBKDF2 hash keys. - trusted_metadata: The `trusted_metadata` field contains an arbitrary JSON object of application-specific data. See the [Metadata](https://stytch.com/docs/api/metadata) reference for complete field behavior details. @@ -416,6 +419,12 @@ def migrate( data["sha_1_config"] = ( sha_1_config if isinstance(sha_1_config, dict) else sha_1_config.dict() ) + if sha_512_config is not None: + data["sha_512_config"] = ( + sha_512_config + if isinstance(sha_512_config, dict) + else sha_512_config.dict() + ) if scrypt_config is not None: data["scrypt_config"] = ( scrypt_config @@ -457,6 +466,7 @@ async def migrate_async( md_5_config: Optional[MD5Config] = None, argon_2_config: Optional[Argon2Config] = None, sha_1_config: Optional[SHA1Config] = None, + sha_512_config: Optional[SHA512Config] = None, scrypt_config: Optional[ScryptConfig] = None, pbkdf_2_config: Optional[PBKDF2Config] = None, trusted_metadata: Optional[Dict[str, Any]] = None, @@ -468,15 +478,16 @@ async def migrate_async( external_id: Optional[str] = None, roles: Optional[List[str]] = None, ) -> MigrateResponse: - """Adds an existing password to a User's email that doesn't have a password yet. We support migrating users from passwords stored with `bcrypt`, `scrypt`, `argon2`, `MD-5`, `SHA-1`, or `PBKDF2`. This endpoint has a rate limit of 100 requests per second. + """Adds an existing password to a User's email that doesn't have a password yet. We support migrating users from passwords stored with `bcrypt`, `scrypt`, `argon2`, `MD-5`, `SHA-1`, `SHA-512`, or `PBKDF2`. This endpoint has a rate limit of 100 requests per second. Fields: - email: The email address of the end user. - hash: The password hash. For a Scrypt or PBKDF2 hash, the hash needs to be a base64 encoded string. - - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, and `pbkdf_2` are supported. + - hash_type: The password hash used. Currently `bcrypt`, `scrypt`, `argon_2i`, `argon_2id`, `md_5`, `sha_1`, `sha_512`, and `pbkdf_2` are supported. - md_5_config: Optional parameters for MD-5 hash types. - argon_2_config: Required parameters if the argon2 hex form, as opposed to the encoded form, is supplied. - sha_1_config: Optional parameters for SHA-1 hash types. + - sha_512_config: Optional parameters for SHA-512 hash types. - scrypt_config: Required parameters if the scrypt is not provided in a [PHC encoded form](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#phc-string-format). - pbkdf_2_config: Required additional parameters for PBKDF2 hash keys. - trusted_metadata: The `trusted_metadata` field contains an arbitrary JSON object of application-specific data. See the [Metadata](https://stytch.com/docs/api/metadata) reference for complete field behavior details. @@ -512,6 +523,12 @@ async def migrate_async( data["sha_1_config"] = ( sha_1_config if isinstance(sha_1_config, dict) else sha_1_config.dict() ) + if sha_512_config is not None: + data["sha_512_config"] = ( + sha_512_config + if isinstance(sha_512_config, dict) + else sha_512_config.dict() + ) if scrypt_config is not None: data["scrypt_config"] = ( scrypt_config diff --git a/stytch/consumer/api/users.py b/stytch/consumer/api/users.py index d7cfcc74..549ea533 100644 --- a/stytch/consumer/api/users.py +++ b/stytch/consumer/api/users.py @@ -198,7 +198,7 @@ def search( query: Optional[Union[SearchUsersQuery, Dict[str, Any]]] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 150 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 150 requests/minute. Search for Users within your Stytch Project. @@ -235,7 +235,7 @@ async def search_async( query: Optional[SearchUsersQuery] = None, ) -> SearchResponse: """ - **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 150 requests/second. + **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 150 requests/minute. Search for Users within your Stytch Project. diff --git a/stytch/consumer/models/passwords.py b/stytch/consumer/models/passwords.py index f8d3fece..6900ef65 100644 --- a/stytch/consumer/models/passwords.py +++ b/stytch/consumer/models/passwords.py @@ -23,6 +23,7 @@ class MigrateRequestHashType(str, enum.Enum): ARGON_2I = "argon_2i" ARGON_2ID = "argon_2id" SHA_1 = "sha_1" + SHA_512 = "sha_512" SCRYPT = "scrypt" PHPASS = "phpass" PBKDF_2 = "pbkdf_2" @@ -114,6 +115,17 @@ class SHA1Config(pydantic.BaseModel): append_salt: str +class SHA512Config(pydantic.BaseModel): + """ + Fields: + - prepend_salt: The salt that should be prepended to the migrated password. + - append_salt: The salt that should be appended to the migrated password. + """ # noqa + + prepend_salt: str + append_salt: str + + class ScryptConfig(pydantic.BaseModel): """ Fields: diff --git a/stytch/version.py b/stytch/version.py index 803336eb..0cd7c66e 100644 --- a/stytch/version.py +++ b/stytch/version.py @@ -1 +1 @@ -__version__ = "13.27.0" +__version__ = "13.28.0"