From 2adcab2231cb42c80db3476d9c05606ee523dfbc Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:30:07 +0000 Subject: [PATCH 1/5] feat(user_management)!: SDK surface change: Parameter type changed for "body" on "UserManagement.create_authenticate" --- src/workos/user_management/_resource.py | 177 ++++++++++++++++-- src/workos/user_management/models/__init__.py | 14 ++ tests/test_user_management.py | 88 +++++++-- .../test_user_management_models_round_trip.py | 61 ++++++ 4 files changed, 310 insertions(+), 30 deletions(-) diff --git a/src/workos/user_management/_resource.py b/src/workos/user_management/_resource.py index 1cfb7e27..ed2ea8d6 100644 --- a/src/workos/user_management/_resource.py +++ b/src/workos/user_management/_resource.py @@ -21,19 +21,24 @@ JWTTemplateResponse, JwksResponse, MagicAuth, + MagicAuthSendMagicAuthCodeAndReturnResponse, PasswordReset, PasswordSessionAuthenticateRequest, RedirectUri, RefreshTokenSessionAuthenticateRequest, ResetPasswordResponse, + SendRadarSmsChallengeResponse, SendVerificationEmailResponse, DeviceCodeSessionAuthenticateRequest, EmailVerificationCodeSessionAuthenticateRequest, MagicAuthCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, + RadarEmailChallengeCodeSessionAuthenticateRequest, + RadarSmsChallengeCodeSessionAuthenticateRequest, UserApiKey, UserApiKeyWithValue, + UserCreateResponse, UserIdentitiesGetItem, UserInvite, VerifyEmailResponse, @@ -126,6 +131,8 @@ def create_authenticate( EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, + RadarEmailChallengeCodeSessionAuthenticateRequest, + RadarSmsChallengeCodeSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, Dict[str, Any], ], @@ -136,7 +143,7 @@ def create_authenticate( Authenticate a user with a specified [authentication method](https://workos.com/docs/reference/authkit/authentication). Args: - body: The request body. Accepts: AuthorizationCodeSessionAuthenticateRequest, PasswordSessionAuthenticateRequest, RefreshTokenSessionAuthenticateRequest, MagicAuthCodeSessionAuthenticateRequest, EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, or a plain dict. + body: The request body. Accepts: AuthorizationCodeSessionAuthenticateRequest, PasswordSessionAuthenticateRequest, RefreshTokenSessionAuthenticateRequest, MagicAuthCodeSessionAuthenticateRequest, EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, RadarEmailChallengeCodeSessionAuthenticateRequest, RadarSmsChallengeCodeSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, or a plain dict. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -566,6 +573,55 @@ def create_device( request_options=request_options, ) + def create_radar_challenge( + self, + *, + user_id: str, + pending_authentication_token: str, + phone_number: str, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> SendRadarSmsChallengeResponse: + """Send a Radar SMS challenge + + Sends a one-time verification code over SMS to a user as part of a Radar challenge. Use the returned `verification_id` to authenticate the user with the `urn:workos:oauth:grant-type:radar-sms-challenge:code` grant type. + + Args: + user_id: The ID of the user to send the SMS challenge to. + pending_authentication_token: The pending authentication token from a previous authentication attempt that triggered the Radar challenge. + phone_number: The phone number to send the SMS verification code to. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SendRadarSmsChallengeResponse + + Raises: + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "user_id": user_id, + "pending_authentication_token": pending_authentication_token, + "phone_number": phone_number, + "ip_address": ip_address, + "user_agent": user_agent, + }.items() + if v is not None + } + return self._client.request( + method="post", + path=("user_management", "radar_challenges"), + body=body, + model=SendRadarSmsChallengeResponse, + request_options=request_options, + ) + def get_logout_url( self, *, @@ -914,9 +970,12 @@ def create_user( email_verified: Optional[bool] = None, metadata: Optional[Dict[str, str]] = None, external_id: Optional[str] = None, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + signals_id: Optional[str] = None, password: Optional[Union[PasswordPlaintext, PasswordHashed]] = None, request_options: Optional[RequestOptions] = None, - ) -> User: + ) -> UserCreateResponse: """Create a user Create a new user in the current environment. @@ -929,11 +988,14 @@ def create_user( email_verified: Whether the user's email has been verified. metadata: Object containing metadata key/value pairs associated with the user. external_id: The external ID of the user. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + signals_id: An optional Radar signals ID to correlate client-side signals with this request. password: Identifies the password. One of: PasswordPlaintext, PasswordHashed. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - User + UserCreateResponse Raises: BadRequestError: If the request is malformed (400). @@ -953,6 +1015,9 @@ def create_user( "email_verified": email_verified, "metadata": metadata, "external_id": external_id, + "ip_address": ip_address, + "user_agent": user_agent, + "signals_id": signals_id, }.items() if v is not None } @@ -966,7 +1031,7 @@ def create_user( method="post", path=("user_management", "users"), body=body, - model=User, + model=UserCreateResponse, request_options=request_options, ) @@ -1689,8 +1754,12 @@ def create_magic_auth( *, email: str, invitation_token: Optional[str] = None, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + radar_auth_attempt_id: Optional[str] = None, + signals_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, - ) -> MagicAuth: + ) -> MagicAuthSendMagicAuthCodeAndReturnResponse: """Create a Magic Auth code Creates a one-time authentication code that can be sent to the user's email address. The code expires in 10 minutes. To verify the code, [authenticate the user with Magic Auth](https://workos.com/docs/reference/authkit/authentication/magic-auth). @@ -1698,10 +1767,14 @@ def create_magic_auth( Args: email: The email address to send the magic code to. invitation_token: The invitation token to associate with this magic code. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + radar_auth_attempt_id: The ID of an existing Radar authentication attempt to associate with this request. + signals_id: An optional Radar signals ID to correlate client-side signals with this request. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - MagicAuth + MagicAuthSendMagicAuthCodeAndReturnResponse Raises: BadRequestError: If the request is malformed (400). @@ -1715,6 +1788,10 @@ def create_magic_auth( for k, v in { "email": email, "invitation_token": invitation_token, + "ip_address": ip_address, + "user_agent": user_agent, + "radar_auth_attempt_id": radar_auth_attempt_id, + "signals_id": signals_id, }.items() if v is not None } @@ -1722,7 +1799,7 @@ def create_magic_auth( method="post", path=("user_management", "magic_auth"), body=body, - model=MagicAuth, + model=MagicAuthSendMagicAuthCodeAndReturnResponse, request_options=request_options, ) @@ -2257,6 +2334,8 @@ async def create_authenticate( EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, + RadarEmailChallengeCodeSessionAuthenticateRequest, + RadarSmsChallengeCodeSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, Dict[str, Any], ], @@ -2267,7 +2346,7 @@ async def create_authenticate( Authenticate a user with a specified [authentication method](https://workos.com/docs/reference/authkit/authentication). Args: - body: The request body. Accepts: AuthorizationCodeSessionAuthenticateRequest, PasswordSessionAuthenticateRequest, RefreshTokenSessionAuthenticateRequest, MagicAuthCodeSessionAuthenticateRequest, EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, or a plain dict. + body: The request body. Accepts: AuthorizationCodeSessionAuthenticateRequest, PasswordSessionAuthenticateRequest, RefreshTokenSessionAuthenticateRequest, MagicAuthCodeSessionAuthenticateRequest, EmailVerificationCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, RadarEmailChallengeCodeSessionAuthenticateRequest, RadarSmsChallengeCodeSessionAuthenticateRequest, DeviceCodeSessionAuthenticateRequest, or a plain dict. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -2697,6 +2776,55 @@ async def create_device( request_options=request_options, ) + async def create_radar_challenge( + self, + *, + user_id: str, + pending_authentication_token: str, + phone_number: str, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> SendRadarSmsChallengeResponse: + """Send a Radar SMS challenge + + Sends a one-time verification code over SMS to a user as part of a Radar challenge. Use the returned `verification_id` to authenticate the user with the `urn:workos:oauth:grant-type:radar-sms-challenge:code` grant type. + + Args: + user_id: The ID of the user to send the SMS challenge to. + pending_authentication_token: The pending authentication token from a previous authentication attempt that triggered the Radar challenge. + phone_number: The phone number to send the SMS verification code to. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SendRadarSmsChallengeResponse + + Raises: + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "user_id": user_id, + "pending_authentication_token": pending_authentication_token, + "phone_number": phone_number, + "ip_address": ip_address, + "user_agent": user_agent, + }.items() + if v is not None + } + return await self._client.request( + method="post", + path=("user_management", "radar_challenges"), + body=body, + model=SendRadarSmsChallengeResponse, + request_options=request_options, + ) + def get_logout_url( self, *, @@ -3045,9 +3173,12 @@ async def create_user( email_verified: Optional[bool] = None, metadata: Optional[Dict[str, str]] = None, external_id: Optional[str] = None, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + signals_id: Optional[str] = None, password: Optional[Union[PasswordPlaintext, PasswordHashed]] = None, request_options: Optional[RequestOptions] = None, - ) -> User: + ) -> UserCreateResponse: """Create a user Create a new user in the current environment. @@ -3060,11 +3191,14 @@ async def create_user( email_verified: Whether the user's email has been verified. metadata: Object containing metadata key/value pairs associated with the user. external_id: The external ID of the user. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + signals_id: An optional Radar signals ID to correlate client-side signals with this request. password: Identifies the password. One of: PasswordPlaintext, PasswordHashed. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - User + UserCreateResponse Raises: BadRequestError: If the request is malformed (400). @@ -3084,6 +3218,9 @@ async def create_user( "email_verified": email_verified, "metadata": metadata, "external_id": external_id, + "ip_address": ip_address, + "user_agent": user_agent, + "signals_id": signals_id, }.items() if v is not None } @@ -3097,7 +3234,7 @@ async def create_user( method="post", path=("user_management", "users"), body=body, - model=User, + model=UserCreateResponse, request_options=request_options, ) @@ -3820,8 +3957,12 @@ async def create_magic_auth( *, email: str, invitation_token: Optional[str] = None, + ip_address: Optional[str] = None, + user_agent: Optional[str] = None, + radar_auth_attempt_id: Optional[str] = None, + signals_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, - ) -> MagicAuth: + ) -> MagicAuthSendMagicAuthCodeAndReturnResponse: """Create a Magic Auth code Creates a one-time authentication code that can be sent to the user's email address. The code expires in 10 minutes. To verify the code, [authenticate the user with Magic Auth](https://workos.com/docs/reference/authkit/authentication/magic-auth). @@ -3829,10 +3970,14 @@ async def create_magic_auth( Args: email: The email address to send the magic code to. invitation_token: The invitation token to associate with this magic code. + ip_address: The IP address of the user's request. + user_agent: The user agent string from the user's request. + radar_auth_attempt_id: The ID of an existing Radar authentication attempt to associate with this request. + signals_id: An optional Radar signals ID to correlate client-side signals with this request. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - MagicAuth + MagicAuthSendMagicAuthCodeAndReturnResponse Raises: BadRequestError: If the request is malformed (400). @@ -3846,6 +3991,10 @@ async def create_magic_auth( for k, v in { "email": email, "invitation_token": invitation_token, + "ip_address": ip_address, + "user_agent": user_agent, + "radar_auth_attempt_id": radar_auth_attempt_id, + "signals_id": signals_id, }.items() if v is not None } @@ -3853,7 +4002,7 @@ async def create_magic_auth( method="post", path=("user_management", "magic_auth"), body=body, - model=MagicAuth, + model=MagicAuthSendMagicAuthCodeAndReturnResponse, request_options=request_options, ) diff --git a/src/workos/user_management/models/__init__.py b/src/workos/user_management/models/__init__.py index 894c261c..6d7da6dc 100644 --- a/src/workos/user_management/models/__init__.py +++ b/src/workos/user_management/models/__init__.py @@ -45,6 +45,9 @@ from .jwks_response_keys import JwksResponseKeys as JwksResponseKeys from .jwt_template_response import JWTTemplateResponse as JWTTemplateResponse from .magic_auth import MagicAuth as MagicAuth +from .magic_auth_send_magic_auth_code_and_return_response import ( + MagicAuthSendMagicAuthCodeAndReturnResponse as MagicAuthSendMagicAuthCodeAndReturnResponse, +) from .password_reset import PasswordReset as PasswordReset from .password_session_authenticate_request import ( PasswordSessionAuthenticateRequest as PasswordSessionAuthenticateRequest, @@ -59,6 +62,10 @@ from .reset_password_response import ResetPasswordResponse as ResetPasswordResponse from .revoke_session import RevokeSession as RevokeSession from .send_email_change import SendEmailChange as SendEmailChange +from .send_radar_sms_challenge import SendRadarSmsChallenge as SendRadarSmsChallenge +from .send_radar_sms_challenge_response import ( + SendRadarSmsChallengeResponse as SendRadarSmsChallengeResponse, +) from .send_verification_email_response import ( SendVerificationEmailResponse as SendVerificationEmailResponse, ) @@ -82,6 +89,12 @@ from .organization_selection_session_authenticate_request import ( OrganizationSelectionSessionAuthenticateRequest as OrganizationSelectionSessionAuthenticateRequest, ) +from .radar_email_challenge_code_session_authenticate_request import ( + RadarEmailChallengeCodeSessionAuthenticateRequest as RadarEmailChallengeCodeSessionAuthenticateRequest, +) +from .radar_sms_challenge_code_session_authenticate_request import ( + RadarSmsChallengeCodeSessionAuthenticateRequest as RadarSmsChallengeCodeSessionAuthenticateRequest, +) from workos.common.models.user import User as User from .user_api_key import UserApiKey as UserApiKey from .user_api_key_owner import UserApiKeyOwner as UserApiKeyOwner @@ -89,6 +102,7 @@ from .user_api_key_with_value_owner import ( UserApiKeyWithValueOwner as UserApiKeyWithValueOwner, ) +from .user_create_response import UserCreateResponse as UserCreateResponse from .user_identities_get_item import UserIdentitiesGetItem as UserIdentitiesGetItem from .user_invite import UserInvite as UserInvite from .user_management_authentication_provider import ( diff --git a/tests/test_user_management.py b/tests/test_user_management.py index 57c2e809..5e722725 100644 --- a/tests/test_user_management.py +++ b/tests/test_user_management.py @@ -19,12 +19,15 @@ JWTTemplateResponse, JwksResponse, MagicAuth, + MagicAuthSendMagicAuthCodeAndReturnResponse, PasswordReset, RedirectUri, ResetPasswordResponse, + SendRadarSmsChallengeResponse, SendVerificationEmailResponse, UserApiKey, UserApiKeyWithValue, + UserCreateResponse, UserIdentitiesGetItem, UserInvite, VerifyEmailResponse, @@ -90,6 +93,28 @@ def test_create_device(self, workos, httpx_mock): body = json.loads(request.content) assert body["client_id"] == "test_client_id" + def test_create_radar_challenge(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("send_radar_sms_challenge_response.json"), + ) + result = workos.user_management.create_radar_challenge( + user_id="test_user_id", + pending_authentication_token="test_pending_authentication_token", + phone_number="test_phone_number", + ) + assert isinstance(result, SendRadarSmsChallengeResponse) + assert result.verification_id == "vrf_01HXYZ123456789ABCDEFGHIJ" + assert result.phone_number == "+15555550123" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/radar_challenges") + body = json.loads(request.content) + assert body["user_id"] == "test_user_id" + assert ( + body["pending_authentication_token"] == "test_pending_authentication_token" + ) + assert body["phone_number"] == "test_phone_number" + def test_get_logout_url(self, workos): result = workos.user_management.get_logout_url(session_id="test_session_id") assert isinstance(result, str) @@ -235,14 +260,16 @@ def test_list_users_encodes_query_params(self, workos, httpx_mock): def test_create_user(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("user.json"), + json=load_fixture("user_create_response.json"), ) result = workos.user_management.create_user( email="test_email", password=PasswordPlaintext(password="test_value") ) - assert isinstance(result, User) - assert result.object == "user" - assert result.id == "user_01E4ZCR3C56J083X43JQXF3JK5" + assert isinstance(result, UserCreateResponse) + assert ( + result.radar_auth_attempt_id + == "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" + ) request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/user_management/users") @@ -532,12 +559,16 @@ def test_update_jwt_template(self, workos, httpx_mock): def test_create_magic_auth(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("magic_auth.json"), + json=load_fixture( + "magic_auth_send_magic_auth_code_and_return_response.json" + ), ) result = workos.user_management.create_magic_auth(email="test_email") - assert isinstance(result, MagicAuth) - assert result.object == "magic_auth" - assert result.id == "magic_auth_01HWZBQZY2M3AMQW166Q22K88F" + assert isinstance(result, MagicAuthSendMagicAuthCodeAndReturnResponse) + assert ( + result.radar_auth_attempt_id + == "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" + ) request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/user_management/magic_auth") @@ -906,6 +937,23 @@ async def test_create_device(self, async_workos, httpx_mock): assert request.method == "POST" assert request.url.path.endswith("/user_management/authorize/device") + @pytest.mark.asyncio + async def test_create_radar_challenge(self, async_workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("send_radar_sms_challenge_response.json") + ) + result = await async_workos.user_management.create_radar_challenge( + user_id="test_user_id", + pending_authentication_token="test_pending_authentication_token", + phone_number="test_phone_number", + ) + assert isinstance(result, SendRadarSmsChallengeResponse) + assert result.verification_id == "vrf_01HXYZ123456789ABCDEFGHIJ" + assert result.phone_number == "+15555550123" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/radar_challenges") + def test_get_logout_url(self, async_workos): result = async_workos.user_management.get_logout_url( session_id="test_session_id" @@ -1048,13 +1096,15 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): @pytest.mark.asyncio async def test_create_user(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("user.json")) + httpx_mock.add_response(json=load_fixture("user_create_response.json")) result = await async_workos.user_management.create_user( email="test_email", password=PasswordPlaintext(password="test_value") ) - assert isinstance(result, User) - assert result.object == "user" - assert result.id == "user_01E4ZCR3C56J083X43JQXF3JK5" + assert isinstance(result, UserCreateResponse) + assert ( + result.radar_auth_attempt_id + == "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" + ) request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/user_management/users") @@ -1333,13 +1383,19 @@ async def test_update_jwt_template(self, async_workos, httpx_mock): @pytest.mark.asyncio async def test_create_magic_auth(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("magic_auth.json")) + httpx_mock.add_response( + json=load_fixture( + "magic_auth_send_magic_auth_code_and_return_response.json" + ) + ) result = await async_workos.user_management.create_magic_auth( email="test_email" ) - assert isinstance(result, MagicAuth) - assert result.object == "magic_auth" - assert result.id == "magic_auth_01HWZBQZY2M3AMQW166Q22K88F" + assert isinstance(result, MagicAuthSendMagicAuthCodeAndReturnResponse) + assert ( + result.radar_auth_attempt_id + == "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" + ) request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/user_management/magic_auth") diff --git a/tests/test_user_management_models_round_trip.py b/tests/test_user_management_models_round_trip.py index 2512c4f3..c24eedf7 100644 --- a/tests/test_user_management_models_round_trip.py +++ b/tests/test_user_management_models_round_trip.py @@ -20,15 +20,18 @@ JwksResponse, JwksResponseKeys, MagicAuth, + MagicAuthSendMagicAuthCodeAndReturnResponse, PasswordReset, RedirectUri, ResetPasswordResponse, + SendRadarSmsChallengeResponse, SendVerificationEmailResponse, User, UserApiKey, UserApiKeyOwner, UserApiKeyWithValue, UserApiKeyWithValueOwner, + UserCreateResponse, UserIdentitiesGetItem, UserInvite, UserSessionsImpersonator, @@ -145,6 +148,24 @@ def test_cors_origin_response_minimal_payload(self): assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] + def test_send_radar_sms_challenge_response_round_trip(self): + data = load_fixture("send_radar_sms_challenge_response.json") + instance = SendRadarSmsChallengeResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = SendRadarSmsChallengeResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_send_radar_sms_challenge_response_minimal_payload(self): + data = { + "verification_id": "vrf_01HXYZ123456789ABCDEFGHIJ", + "phone_number": "+15555550123", + } + instance = SendRadarSmsChallengeResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized["verification_id"] == data["verification_id"] + assert serialized["phone_number"] == data["phone_number"] + def test_redirect_uri_round_trip(self): data = load_fixture("redirect_uri.json") instance = RedirectUri.from_dict(data) @@ -1071,6 +1092,46 @@ def test_invitation_round_trips_unknown_enum_values(self): instance = Invitation.from_dict(data) assert instance.to_dict() == data + def test_magic_auth_send_magic_auth_code_and_return_response_round_trip(self): + data = load_fixture("magic_auth_send_magic_auth_code_and_return_response.json") + instance = MagicAuthSendMagicAuthCodeAndReturnResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = MagicAuthSendMagicAuthCodeAndReturnResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_magic_auth_send_magic_auth_code_and_return_response_minimal_payload(self): + data = {} + instance = MagicAuthSendMagicAuthCodeAndReturnResponse.from_dict(data) + assert instance.to_dict() is not None + + def test_magic_auth_send_magic_auth_code_and_return_response_omits_absent_optional_non_nullable_fields( + self, + ): + data = {} + instance = MagicAuthSendMagicAuthCodeAndReturnResponse.from_dict(data) + serialized = instance.to_dict() + assert "radar_auth_attempt_id" not in serialized + + def test_user_create_response_round_trip(self): + data = load_fixture("user_create_response.json") + instance = UserCreateResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserCreateResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_create_response_minimal_payload(self): + data = {} + instance = UserCreateResponse.from_dict(data) + assert instance.to_dict() is not None + + def test_user_create_response_omits_absent_optional_non_nullable_fields(self): + data = {} + instance = UserCreateResponse.from_dict(data) + serialized = instance.to_dict() + assert "radar_auth_attempt_id" not in serialized + def test_email_change_confirmation_round_trip(self): data = load_fixture("email_change_confirmation.json") instance = EmailChangeConfirmation.from_dict(data) From 586a76b21c25994b52ebf85b344e97a358b3cbfb Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:30:07 +0000 Subject: [PATCH 2/5] feat(user_management): Add user management operations and models --- ...ation_code_session_authenticate_request.py | 5 ++ .../models/create_magic_code_and_return.py | 20 ++++++++ .../user_management/models/create_user.py | 19 +++++++ ...end_magic_auth_code_and_return_response.py | 34 +++++++++++++ .../password_session_authenticate_request.py | 10 ++++ .../models/send_radar_sms_challenge.py | 49 +++++++++++++++++++ .../send_radar_sms_challenge_response.py | 35 +++++++++++++ .../models/user_create_response.py | 8 +++ ...ion_code_session_authenticate_request.json | 3 +- .../create_magic_code_and_return.json | 6 ++- tests/fixtures/create_user.json | 3 ++ ...d_magic_auth_code_and_return_response.json | 3 ++ ...password_session_authenticate_request.json | 4 +- tests/fixtures/send_radar_sms_challenge.json | 7 +++ .../send_radar_sms_challenge_response.json | 4 ++ tests/fixtures/user_create_response.json | 3 ++ 16 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 src/workos/user_management/models/magic_auth_send_magic_auth_code_and_return_response.py create mode 100644 src/workos/user_management/models/send_radar_sms_challenge.py create mode 100644 src/workos/user_management/models/send_radar_sms_challenge_response.py create mode 100644 src/workos/user_management/models/user_create_response.py create mode 100644 tests/fixtures/magic_auth_send_magic_auth_code_and_return_response.json create mode 100644 tests/fixtures/send_radar_sms_challenge.json create mode 100644 tests/fixtures/send_radar_sms_challenge_response.json create mode 100644 tests/fixtures/user_create_response.json diff --git a/src/workos/user_management/models/authorization_code_session_authenticate_request.py b/src/workos/user_management/models/authorization_code_session_authenticate_request.py index f315cb11..954eb45a 100644 --- a/src/workos/user_management/models/authorization_code_session_authenticate_request.py +++ b/src/workos/user_management/models/authorization_code_session_authenticate_request.py @@ -28,6 +28,8 @@ class AuthorizationCodeSessionAuthenticateRequest: """A unique identifier for the device.""" user_agent: Optional[str] = None """The user agent string from the user's browser.""" + signals_id: Optional[str] = None + """An optional Radar signals ID to correlate client-side signals with this authentication attempt.""" @classmethod def from_dict( @@ -45,6 +47,7 @@ def from_dict( ip_address=data.get("ip_address"), device_id=data.get("device_id"), user_agent=data.get("user_agent"), + signals_id=data.get("signals_id"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("AuthorizationCodeSessionAuthenticateRequest", e) @@ -67,4 +70,6 @@ def to_dict(self) -> Dict[str, Any]: result["device_id"] = self.device_id if self.user_agent is not None: result["user_agent"] = self.user_agent + if self.signals_id is not None: + result["signals_id"] = self.signals_id return result diff --git a/src/workos/user_management/models/create_magic_code_and_return.py b/src/workos/user_management/models/create_magic_code_and_return.py index 1bbed0b5..6a9db8fe 100644 --- a/src/workos/user_management/models/create_magic_code_and_return.py +++ b/src/workos/user_management/models/create_magic_code_and_return.py @@ -15,6 +15,14 @@ class CreateMagicCodeAndReturn: """The email address to send the magic code to.""" invitation_token: Optional[str] = None """The invitation token to associate with this magic code.""" + ip_address: Optional[str] = None + """The IP address of the user's request.""" + user_agent: Optional[str] = None + """The user agent string from the user's request.""" + radar_auth_attempt_id: Optional[str] = None + """The ID of an existing Radar authentication attempt to associate with this request.""" + signals_id: Optional[str] = None + """An optional Radar signals ID to correlate client-side signals with this request.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "CreateMagicCodeAndReturn": @@ -23,6 +31,10 @@ def from_dict(cls, data: Dict[str, Any]) -> "CreateMagicCodeAndReturn": return cls( email=data["email"], invitation_token=data.get("invitation_token"), + ip_address=data.get("ip_address"), + user_agent=data.get("user_agent"), + radar_auth_attempt_id=data.get("radar_auth_attempt_id"), + signals_id=data.get("signals_id"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("CreateMagicCodeAndReturn", e) @@ -33,4 +45,12 @@ def to_dict(self) -> Dict[str, Any]: result["email"] = self.email if self.invitation_token is not None: result["invitation_token"] = self.invitation_token + if self.ip_address is not None: + result["ip_address"] = self.ip_address + if self.user_agent is not None: + result["user_agent"] = self.user_agent + if self.radar_auth_attempt_id is not None: + result["radar_auth_attempt_id"] = self.radar_auth_attempt_id + if self.signals_id is not None: + result["signals_id"] = self.signals_id return result diff --git a/src/workos/user_management/models/create_user.py b/src/workos/user_management/models/create_user.py index 5c01559a..d67a6c95 100644 --- a/src/workos/user_management/models/create_user.py +++ b/src/workos/user_management/models/create_user.py @@ -29,6 +29,12 @@ class CreateUser: """Object containing metadata key/value pairs associated with the user.""" external_id: Optional[str] = None """The external ID of the user.""" + ip_address: Optional[str] = None + """The IP address of the user's request.""" + user_agent: Optional[str] = None + """The user agent string from the user's request.""" + signals_id: Optional[str] = None + """An optional Radar signals ID to correlate client-side signals with this request.""" password: Optional[str] = None """The password to set for the user. Mutually exclusive with `password_hash` and `password_hash_type`.""" password_hash: Optional[str] = None @@ -48,6 +54,9 @@ def from_dict(cls, data: Dict[str, Any]) -> "CreateUser": email_verified=data.get("email_verified"), metadata=data.get("metadata"), external_id=data.get("external_id"), + ip_address=data.get("ip_address"), + user_agent=data.get("user_agent"), + signals_id=data.get("signals_id"), password=data.get("password"), password_hash=data.get("password_hash"), password_hash_type=CreateUserPasswordHashType(_v_password_hash_type) @@ -85,6 +94,16 @@ def to_dict(self) -> Dict[str, Any]: result["external_id"] = self.external_id else: result["external_id"] = None + if self.ip_address is not None: + result["ip_address"] = self.ip_address + else: + result["ip_address"] = None + if self.user_agent is not None: + result["user_agent"] = self.user_agent + else: + result["user_agent"] = None + if self.signals_id is not None: + result["signals_id"] = self.signals_id if self.password is not None: result["password"] = self.password else: diff --git a/src/workos/user_management/models/magic_auth_send_magic_auth_code_and_return_response.py b/src/workos/user_management/models/magic_auth_send_magic_auth_code_and_return_response.py new file mode 100644 index 00000000..d11374d6 --- /dev/null +++ b/src/workos/user_management/models/magic_auth_send_magic_auth_code_and_return_response.py @@ -0,0 +1,34 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class MagicAuthSendMagicAuthCodeAndReturnResponse: + """Magic Auth Send Magic Auth Code And Return Response model.""" + + radar_auth_attempt_id: Optional[str] = None + """The ID of the Radar authentication attempt created for this request when Radar is enabled. Pass this value to the authenticate endpoint to associate the subsequent authentication with this Radar attempt.""" + + @classmethod + def from_dict( + cls, data: Dict[str, Any] + ) -> "MagicAuthSendMagicAuthCodeAndReturnResponse": + """Deserialize from a dictionary.""" + try: + return cls( + radar_auth_attempt_id=data.get("radar_auth_attempt_id"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("MagicAuthSendMagicAuthCodeAndReturnResponse", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + if self.radar_auth_attempt_id is not None: + result["radar_auth_attempt_id"] = self.radar_auth_attempt_id + return result diff --git a/src/workos/user_management/models/password_session_authenticate_request.py b/src/workos/user_management/models/password_session_authenticate_request.py index f904a6fa..2b56323c 100644 --- a/src/workos/user_management/models/password_session_authenticate_request.py +++ b/src/workos/user_management/models/password_session_authenticate_request.py @@ -28,6 +28,10 @@ class PasswordSessionAuthenticateRequest: """A unique identifier for the device.""" user_agent: Optional[str] = None """The user agent string from the user's browser.""" + signals_id: Optional[str] = None + """An optional Radar signals ID to correlate client-side signals with this authentication attempt.""" + radar_auth_attempt_id: Optional[str] = None + """The ID of an existing Radar authentication attempt to associate with this authentication.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "PasswordSessionAuthenticateRequest": @@ -43,6 +47,8 @@ def from_dict(cls, data: Dict[str, Any]) -> "PasswordSessionAuthenticateRequest" ip_address=data.get("ip_address"), device_id=data.get("device_id"), user_agent=data.get("user_agent"), + signals_id=data.get("signals_id"), + radar_auth_attempt_id=data.get("radar_auth_attempt_id"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("PasswordSessionAuthenticateRequest", e) @@ -63,4 +69,8 @@ def to_dict(self) -> Dict[str, Any]: result["device_id"] = self.device_id if self.user_agent is not None: result["user_agent"] = self.user_agent + if self.signals_id is not None: + result["signals_id"] = self.signals_id + if self.radar_auth_attempt_id is not None: + result["radar_auth_attempt_id"] = self.radar_auth_attempt_id return result diff --git a/src/workos/user_management/models/send_radar_sms_challenge.py b/src/workos/user_management/models/send_radar_sms_challenge.py new file mode 100644 index 00000000..06aa140b --- /dev/null +++ b/src/workos/user_management/models/send_radar_sms_challenge.py @@ -0,0 +1,49 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class SendRadarSmsChallenge: + """Send Radar Sms Challenge model.""" + + user_id: str + """The ID of the user to send the SMS challenge to.""" + pending_authentication_token: str + """The pending authentication token from a previous authentication attempt that triggered the Radar challenge.""" + phone_number: str + """The phone number to send the SMS verification code to.""" + ip_address: Optional[str] = None + """The IP address of the user's request.""" + user_agent: Optional[str] = None + """The user agent string from the user's request.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "SendRadarSmsChallenge": + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + pending_authentication_token=data["pending_authentication_token"], + phone_number=data["phone_number"], + ip_address=data.get("ip_address"), + user_agent=data.get("user_agent"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("SendRadarSmsChallenge", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["user_id"] = self.user_id + result["pending_authentication_token"] = self.pending_authentication_token + result["phone_number"] = self.phone_number + if self.ip_address is not None: + result["ip_address"] = self.ip_address + if self.user_agent is not None: + result["user_agent"] = self.user_agent + return result diff --git a/src/workos/user_management/models/send_radar_sms_challenge_response.py b/src/workos/user_management/models/send_radar_sms_challenge_response.py new file mode 100644 index 00000000..9dd51197 --- /dev/null +++ b/src/workos/user_management/models/send_radar_sms_challenge_response.py @@ -0,0 +1,35 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class SendRadarSmsChallengeResponse: + """Send Radar Sms Challenge Response model.""" + + verification_id: str + """The ID of the SMS verification. Pass this to the authenticate endpoint to verify the code.""" + phone_number: str + """The phone number the verification code was sent to.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "SendRadarSmsChallengeResponse": + """Deserialize from a dictionary.""" + try: + return cls( + verification_id=data["verification_id"], + phone_number=data["phone_number"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("SendRadarSmsChallengeResponse", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["verification_id"] = self.verification_id + result["phone_number"] = self.phone_number + return result diff --git a/src/workos/user_management/models/user_create_response.py b/src/workos/user_management/models/user_create_response.py new file mode 100644 index 00000000..dcf60080 --- /dev/null +++ b/src/workos/user_management/models/user_create_response.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from .magic_auth_send_magic_auth_code_and_return_response import ( + MagicAuthSendMagicAuthCodeAndReturnResponse, +) + +UserCreateResponse: TypeAlias = MagicAuthSendMagicAuthCodeAndReturnResponse diff --git a/tests/fixtures/authorization_code_session_authenticate_request.json b/tests/fixtures/authorization_code_session_authenticate_request.json index 07c72c3a..0f50d5c6 100644 --- a/tests/fixtures/authorization_code_session_authenticate_request.json +++ b/tests/fixtures/authorization_code_session_authenticate_request.json @@ -7,5 +7,6 @@ "invitation_token": "inv_tok_01HXYZ123456789ABCDEFGHIJ", "ip_address": "203.0.113.42", "device_id": "device_01HXYZ123456789ABCDEFGHIJ", - "user_agent": "Mozilla/5.0" + "user_agent": "Mozilla/5.0", + "signals_id": "01JBS0GN92GC2RJQS4X9DBPQ2A" } diff --git a/tests/fixtures/create_magic_code_and_return.json b/tests/fixtures/create_magic_code_and_return.json index 10708580..487b3fad 100644 --- a/tests/fixtures/create_magic_code_and_return.json +++ b/tests/fixtures/create_magic_code_and_return.json @@ -1,4 +1,8 @@ { "email": "marcelina.davis@example.com", - "invitation_token": "Z1Y2X3W4V5U6T7S8R9Q0P1O2N3" + "invitation_token": "Z1Y2X3W4V5U6T7S8R9Q0P1O2N3", + "ip_address": "203.0.113.42", + "user_agent": "Mozilla/5.0", + "radar_auth_attempt_id": "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ", + "signals_id": "01JBS0GN92GC2RJQS4X9DBPQ2A" } diff --git a/tests/fixtures/create_user.json b/tests/fixtures/create_user.json index e63fca69..a851bca9 100644 --- a/tests/fixtures/create_user.json +++ b/tests/fixtures/create_user.json @@ -8,6 +8,9 @@ "timezone": "America/New_York" }, "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "ip_address": "203.0.113.42", + "user_agent": "Mozilla/5.0", + "signals_id": "01JBS0GN92GC2RJQS4X9DBPQ2A", "password": "strong_password_123!", "password_hash": "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy", "password_hash_type": "bcrypt" diff --git a/tests/fixtures/magic_auth_send_magic_auth_code_and_return_response.json b/tests/fixtures/magic_auth_send_magic_auth_code_and_return_response.json new file mode 100644 index 00000000..762e42d0 --- /dev/null +++ b/tests/fixtures/magic_auth_send_magic_auth_code_and_return_response.json @@ -0,0 +1,3 @@ +{ + "radar_auth_attempt_id": "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" +} diff --git a/tests/fixtures/password_session_authenticate_request.json b/tests/fixtures/password_session_authenticate_request.json index ea240764..ae578a45 100644 --- a/tests/fixtures/password_session_authenticate_request.json +++ b/tests/fixtures/password_session_authenticate_request.json @@ -7,5 +7,7 @@ "invitation_token": "inv_tok_01HXYZ123456789ABCDEFGHIJ", "ip_address": "203.0.113.42", "device_id": "device_01HXYZ123456789ABCDEFGHIJ", - "user_agent": "Mozilla/5.0" + "user_agent": "Mozilla/5.0", + "signals_id": "01JBS0GN92GC2RJQS4X9DBPQ2A", + "radar_auth_attempt_id": "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" } diff --git a/tests/fixtures/send_radar_sms_challenge.json b/tests/fixtures/send_radar_sms_challenge.json new file mode 100644 index 00000000..b112e128 --- /dev/null +++ b/tests/fixtures/send_radar_sms_challenge.json @@ -0,0 +1,7 @@ +{ + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "pending_authentication_token": "cTDQJTTkTkkVYxbn...", + "phone_number": "+15555550123", + "ip_address": "203.0.113.42", + "user_agent": "Mozilla/5.0" +} diff --git a/tests/fixtures/send_radar_sms_challenge_response.json b/tests/fixtures/send_radar_sms_challenge_response.json new file mode 100644 index 00000000..933637b9 --- /dev/null +++ b/tests/fixtures/send_radar_sms_challenge_response.json @@ -0,0 +1,4 @@ +{ + "verification_id": "vrf_01HXYZ123456789ABCDEFGHIJ", + "phone_number": "+15555550123" +} diff --git a/tests/fixtures/user_create_response.json b/tests/fixtures/user_create_response.json new file mode 100644 index 00000000..762e42d0 --- /dev/null +++ b/tests/fixtures/user_create_response.json @@ -0,0 +1,3 @@ +{ + "radar_auth_attempt_id": "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" +} From e955d9015f79e73c75847d820d815ffd9a5a7001 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:30:07 +0000 Subject: [PATCH 3/5] fix(user_management): Update user management API surface --- ..._auth_code_session_authenticate_request.py | 5 ++ ...lenge_code_session_authenticate_request.py | 72 ++++++++++++++++++ ...lenge_code_session_authenticate_request.py | 75 +++++++++++++++++++ ...uth_code_session_authenticate_request.json | 3 +- ...nge_code_session_authenticate_request.json | 11 +++ ...nge_code_session_authenticate_request.json | 12 +++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/workos/user_management/models/radar_email_challenge_code_session_authenticate_request.py create mode 100644 src/workos/user_management/models/radar_sms_challenge_code_session_authenticate_request.py create mode 100644 tests/fixtures/radar_email_challenge_code_session_authenticate_request.json create mode 100644 tests/fixtures/radar_sms_challenge_code_session_authenticate_request.json diff --git a/src/workos/user_management/models/magic_auth_code_session_authenticate_request.py b/src/workos/user_management/models/magic_auth_code_session_authenticate_request.py index 6679858b..b5ae2669 100644 --- a/src/workos/user_management/models/magic_auth_code_session_authenticate_request.py +++ b/src/workos/user_management/models/magic_auth_code_session_authenticate_request.py @@ -28,6 +28,8 @@ class MagicAuthCodeSessionAuthenticateRequest: """A unique identifier for the device.""" user_agent: Optional[str] = None """The user agent string from the user's browser.""" + radar_auth_attempt_id: Optional[str] = None + """The ID of an existing Radar authentication attempt to associate with this authentication.""" @classmethod def from_dict( @@ -47,6 +49,7 @@ def from_dict( ip_address=data.get("ip_address"), device_id=data.get("device_id"), user_agent=data.get("user_agent"), + radar_auth_attempt_id=data.get("radar_auth_attempt_id"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("MagicAuthCodeSessionAuthenticateRequest", e) @@ -67,4 +70,6 @@ def to_dict(self) -> Dict[str, Any]: result["device_id"] = self.device_id if self.user_agent is not None: result["user_agent"] = self.user_agent + if self.radar_auth_attempt_id is not None: + result["radar_auth_attempt_id"] = self.radar_auth_attempt_id return result diff --git a/src/workos/user_management/models/radar_email_challenge_code_session_authenticate_request.py b/src/workos/user_management/models/radar_email_challenge_code_session_authenticate_request.py new file mode 100644 index 00000000..8d241207 --- /dev/null +++ b/src/workos/user_management/models/radar_email_challenge_code_session_authenticate_request.py @@ -0,0 +1,72 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Literal, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class RadarEmailChallengeCodeSessionAuthenticateRequest: + """Radar Email Challenge Code Session Authenticate Request model.""" + + client_id: str + """The client ID of the application.""" + client_secret: str + """The client secret of the application.""" + grant_type: Literal["urn:workos:oauth:grant-type:radar-email-challenge:code"] + code: str + """The one-time code from the Radar email challenge.""" + radar_challenge_id: str + """The ID of the Radar email challenge being verified.""" + pending_authentication_token: str + """The pending authentication token from a previous authentication attempt.""" + ip_address: Optional[str] = None + """The IP address of the user's request.""" + device_id: Optional[str] = None + """A unique identifier for the device.""" + user_agent: Optional[str] = None + """The user agent string from the user's browser.""" + + @classmethod + def from_dict( + cls, data: Dict[str, Any] + ) -> "RadarEmailChallengeCodeSessionAuthenticateRequest": + """Deserialize from a dictionary.""" + try: + return cls( + client_id=data["client_id"], + client_secret=data["client_secret"], + grant_type=data.get( + "grant_type", + "urn:workos:oauth:grant-type:radar-email-challenge:code", + ), + code=data["code"], + radar_challenge_id=data["radar_challenge_id"], + pending_authentication_token=data["pending_authentication_token"], + ip_address=data.get("ip_address"), + device_id=data.get("device_id"), + user_agent=data.get("user_agent"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "RadarEmailChallengeCodeSessionAuthenticateRequest", e + ) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["client_id"] = self.client_id + result["client_secret"] = self.client_secret + result["grant_type"] = self.grant_type + result["code"] = self.code + result["radar_challenge_id"] = self.radar_challenge_id + result["pending_authentication_token"] = self.pending_authentication_token + if self.ip_address is not None: + result["ip_address"] = self.ip_address + if self.device_id is not None: + result["device_id"] = self.device_id + if self.user_agent is not None: + result["user_agent"] = self.user_agent + return result diff --git a/src/workos/user_management/models/radar_sms_challenge_code_session_authenticate_request.py b/src/workos/user_management/models/radar_sms_challenge_code_session_authenticate_request.py new file mode 100644 index 00000000..ee8f18cd --- /dev/null +++ b/src/workos/user_management/models/radar_sms_challenge_code_session_authenticate_request.py @@ -0,0 +1,75 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Literal, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class RadarSmsChallengeCodeSessionAuthenticateRequest: + """Radar Sms Challenge Code Session Authenticate Request model.""" + + client_id: str + """The client ID of the application.""" + client_secret: str + """The client secret of the application.""" + grant_type: Literal["urn:workos:oauth:grant-type:radar-sms-challenge:code"] + code: str + """The one-time code from the Radar SMS challenge.""" + verification_id: str + """The ID of the Radar SMS verification being confirmed.""" + phone_number: str + """The phone number the Radar SMS challenge was sent to.""" + pending_authentication_token: str + """The pending authentication token from a previous authentication attempt.""" + ip_address: Optional[str] = None + """The IP address of the user's request.""" + device_id: Optional[str] = None + """A unique identifier for the device.""" + user_agent: Optional[str] = None + """The user agent string from the user's browser.""" + + @classmethod + def from_dict( + cls, data: Dict[str, Any] + ) -> "RadarSmsChallengeCodeSessionAuthenticateRequest": + """Deserialize from a dictionary.""" + try: + return cls( + client_id=data["client_id"], + client_secret=data["client_secret"], + grant_type=data.get( + "grant_type", "urn:workos:oauth:grant-type:radar-sms-challenge:code" + ), + code=data["code"], + verification_id=data["verification_id"], + phone_number=data["phone_number"], + pending_authentication_token=data["pending_authentication_token"], + ip_address=data.get("ip_address"), + device_id=data.get("device_id"), + user_agent=data.get("user_agent"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "RadarSmsChallengeCodeSessionAuthenticateRequest", e + ) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["client_id"] = self.client_id + result["client_secret"] = self.client_secret + result["grant_type"] = self.grant_type + result["code"] = self.code + result["verification_id"] = self.verification_id + result["phone_number"] = self.phone_number + result["pending_authentication_token"] = self.pending_authentication_token + if self.ip_address is not None: + result["ip_address"] = self.ip_address + if self.device_id is not None: + result["device_id"] = self.device_id + if self.user_agent is not None: + result["user_agent"] = self.user_agent + return result diff --git a/tests/fixtures/magic_auth_code_session_authenticate_request.json b/tests/fixtures/magic_auth_code_session_authenticate_request.json index ebd5511b..484b83a4 100644 --- a/tests/fixtures/magic_auth_code_session_authenticate_request.json +++ b/tests/fixtures/magic_auth_code_session_authenticate_request.json @@ -7,5 +7,6 @@ "invitation_token": "inv_tok_01HXYZ123456789ABCDEFGHIJ", "ip_address": "203.0.113.42", "device_id": "device_01HXYZ123456789ABCDEFGHIJ", - "user_agent": "Mozilla/5.0" + "user_agent": "Mozilla/5.0", + "radar_auth_attempt_id": "radar_auth_attempt_01HXYZ123456789ABCDEFGHIJ" } diff --git a/tests/fixtures/radar_email_challenge_code_session_authenticate_request.json b/tests/fixtures/radar_email_challenge_code_session_authenticate_request.json new file mode 100644 index 00000000..fcd9246b --- /dev/null +++ b/tests/fixtures/radar_email_challenge_code_session_authenticate_request.json @@ -0,0 +1,11 @@ +{ + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "client_secret": "sk_test_....", + "grant_type": "urn:workos:oauth:grant-type:radar-email-challenge:code", + "code": "123456", + "radar_challenge_id": "radar_challenge_01HXYZ123456789ABCDEFGHIJ", + "pending_authentication_token": "cTDQJTTkTkkVYxbn...", + "ip_address": "203.0.113.42", + "device_id": "device_01HXYZ123456789ABCDEFGHIJ", + "user_agent": "Mozilla/5.0" +} diff --git a/tests/fixtures/radar_sms_challenge_code_session_authenticate_request.json b/tests/fixtures/radar_sms_challenge_code_session_authenticate_request.json new file mode 100644 index 00000000..5c515922 --- /dev/null +++ b/tests/fixtures/radar_sms_challenge_code_session_authenticate_request.json @@ -0,0 +1,12 @@ +{ + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "client_secret": "sk_test_....", + "grant_type": "urn:workos:oauth:grant-type:radar-sms-challenge:code", + "code": "123456", + "verification_id": "vrf_01HXYZ123456789ABCDEFGHIJ", + "phone_number": "+15555550123", + "pending_authentication_token": "cTDQJTTkTkkVYxbn...", + "ip_address": "203.0.113.42", + "device_id": "device_01HXYZ123456789ABCDEFGHIJ", + "user_agent": "Mozilla/5.0" +} From a8e8d8a2ff33c1f14b112e6c04b8d3865e053141 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:30:07 +0000 Subject: [PATCH 4/5] chore(generated): regenerate shared files for UserManagement, Radar --- .oagen-manifest.json | 15 +- src/workos/radar/_resource.py | 34 ++-- .../models/radar_standalone_assess_request.py | 7 +- .../radar_standalone_assess_request.json | 3 +- tests/test_common_models_round_trip.py | 153 ++++++++++++++++-- tests/test_radar_models_round_trip.py | 68 ++++++++ 6 files changed, 256 insertions(+), 24 deletions(-) create mode 100644 tests/test_radar_models_round_trip.py diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 86a5bc94..453021d7 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,7 +1,7 @@ { "version": 2, "language": "python", - "generatedAt": "2026-07-02T14:58:45.916Z", + "generatedAt": "2026-07-02T17:29:55.736Z", "files": [ "src/workos/_client.py", "src/workos/admin_portal/__init__.py", @@ -661,16 +661,21 @@ "src/workos/user_management/models/jwt_template_response.py", "src/workos/user_management/models/magic_auth.py", "src/workos/user_management/models/magic_auth_code_session_authenticate_request.py", + "src/workos/user_management/models/magic_auth_send_magic_auth_code_and_return_response.py", "src/workos/user_management/models/mfa_totp_session_authenticate_request.py", "src/workos/user_management/models/organization_selection_session_authenticate_request.py", "src/workos/user_management/models/password_reset.py", "src/workos/user_management/models/password_session_authenticate_request.py", + "src/workos/user_management/models/radar_email_challenge_code_session_authenticate_request.py", + "src/workos/user_management/models/radar_sms_challenge_code_session_authenticate_request.py", "src/workos/user_management/models/redirect_uri.py", "src/workos/user_management/models/refresh_token_session_authenticate_request.py", "src/workos/user_management/models/resend_user_invite_options.py", "src/workos/user_management/models/reset_password_response.py", "src/workos/user_management/models/revoke_session.py", "src/workos/user_management/models/send_email_change.py", + "src/workos/user_management/models/send_radar_sms_challenge.py", + "src/workos/user_management/models/send_radar_sms_challenge_response.py", "src/workos/user_management/models/send_verification_email_response.py", "src/workos/user_management/models/sso_device_authorization_request.py", "src/workos/user_management/models/update_jwt_template.py", @@ -679,6 +684,7 @@ "src/workos/user_management/models/user_api_key_owner.py", "src/workos/user_management/models/user_api_key_with_value.py", "src/workos/user_management/models/user_api_key_with_value_owner.py", + "src/workos/user_management/models/user_create_response.py", "src/workos/user_management/models/user_identities_get_item.py", "src/workos/user_management/models/user_invite.py", "src/workos/user_management/models/user_management_authentication_provider.py", @@ -1042,6 +1048,7 @@ "tests/fixtures/magic_auth_code_session_authenticate_request.json", "tests/fixtures/magic_auth_created.json", "tests/fixtures/magic_auth_created_data.json", + "tests/fixtures/magic_auth_send_magic_auth_code_and_return_response.json", "tests/fixtures/mfa_totp_session_authenticate_request.json", "tests/fixtures/new_connect_application_secret.json", "tests/fixtures/object_metadata.json", @@ -1110,7 +1117,9 @@ "tests/fixtures/pipes_connected_account_reauthorization_needed.json", "tests/fixtures/portal_link_response.json", "tests/fixtures/profile.json", + "tests/fixtures/radar_email_challenge_code_session_authenticate_request.json", "tests/fixtures/radar_list_entry_already_present_response.json", + "tests/fixtures/radar_sms_challenge_code_session_authenticate_request.json", "tests/fixtures/radar_standalone_assess_request.json", "tests/fixtures/radar_standalone_delete_radar_list_entry_request.json", "tests/fixtures/radar_standalone_response.json", @@ -1135,6 +1144,8 @@ "tests/fixtures/role_updated.json", "tests/fixtures/role_updated_data.json", "tests/fixtures/send_email_change.json", + "tests/fixtures/send_radar_sms_challenge.json", + "tests/fixtures/send_radar_sms_challenge_response.json", "tests/fixtures/send_verification_email_response.json", "tests/fixtures/session_created.json", "tests/fixtures/session_created_data.json", @@ -1178,6 +1189,7 @@ "tests/fixtures/user_authentication_factor_enroll_response.json", "tests/fixtures/user_consent_option.json", "tests/fixtures/user_consent_option_choice.json", + "tests/fixtures/user_create_response.json", "tests/fixtures/user_created.json", "tests/fixtures/user_deleted.json", "tests/fixtures/user_identities_get_item.json", @@ -1252,6 +1264,7 @@ "tests/test_pipes_models_round_trip.py", "tests/test_pipes_provider.py", "tests/test_radar.py", + "tests/test_radar_models_round_trip.py", "tests/test_sso.py", "tests/test_user_management.py", "tests/test_user_management_models_round_trip.py", diff --git a/src/workos/radar/_resource.py b/src/workos/radar/_resource.py index c5a85559..c0b4f427 100644 --- a/src/workos/radar/_resource.py +++ b/src/workos/radar/_resource.py @@ -32,6 +32,7 @@ def create_attempt( email: str, auth_method: Union[RadarStandaloneAssessRequestAuthMethod, str], action: Union[RadarStandaloneAssessRequestAction, str], + signals_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, ) -> RadarStandaloneResponse: """Create an attempt @@ -44,6 +45,7 @@ def create_attempt( email: The email address of the user making the request. auth_method: The authentication method being used. action: The action being performed. + signals_id: An optional Radar signals ID for the request. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -56,11 +58,16 @@ def create_attempt( ServerError: If the server returns a 5xx error. """ body: Dict[str, Any] = { - "ip_address": ip_address, - "user_agent": user_agent, - "email": email, - "auth_method": enum_value(auth_method), - "action": enum_value(action), + k: v + for k, v in { + "ip_address": ip_address, + "user_agent": user_agent, + "email": email, + "auth_method": enum_value(auth_method), + "action": enum_value(action), + "signals_id": signals_id, + }.items() + if v is not None } return self._client.request( method="post", @@ -198,6 +205,7 @@ async def create_attempt( email: str, auth_method: Union[RadarStandaloneAssessRequestAuthMethod, str], action: Union[RadarStandaloneAssessRequestAction, str], + signals_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, ) -> RadarStandaloneResponse: """Create an attempt @@ -210,6 +218,7 @@ async def create_attempt( email: The email address of the user making the request. auth_method: The authentication method being used. action: The action being performed. + signals_id: An optional Radar signals ID for the request. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -222,11 +231,16 @@ async def create_attempt( ServerError: If the server returns a 5xx error. """ body: Dict[str, Any] = { - "ip_address": ip_address, - "user_agent": user_agent, - "email": email, - "auth_method": enum_value(auth_method), - "action": enum_value(action), + k: v + for k, v in { + "ip_address": ip_address, + "user_agent": user_agent, + "email": email, + "auth_method": enum_value(auth_method), + "action": enum_value(action), + "signals_id": signals_id, + }.items() + if v is not None } return await self._client.request( method="post", diff --git a/src/workos/radar/models/radar_standalone_assess_request.py b/src/workos/radar/models/radar_standalone_assess_request.py index 5a772920..71d4ba03 100644 --- a/src/workos/radar/models/radar_standalone_assess_request.py +++ b/src/workos/radar/models/radar_standalone_assess_request.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Any, Dict +from typing import Any, Dict, Optional from workos._types import _raise_deserialize_error from workos.common.models.radar_standalone_assess_request_action import ( RadarStandaloneAssessRequestAction, @@ -28,6 +28,8 @@ class RadarStandaloneAssessRequest: """The authentication method being used.""" action: "RadarStandaloneAssessRequestAction" """The action being performed.""" + signals_id: Optional[str] = None + """An optional Radar signals ID for the request.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "RadarStandaloneAssessRequest": @@ -39,6 +41,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "RadarStandaloneAssessRequest": email=data["email"], auth_method=RadarStandaloneAssessRequestAuthMethod(data["auth_method"]), action=RadarStandaloneAssessRequestAction(data["action"]), + signals_id=data.get("signals_id"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("RadarStandaloneAssessRequest", e) @@ -57,4 +60,6 @@ def to_dict(self) -> Dict[str, Any]: result["action"] = ( self.action.value if isinstance(self.action, Enum) else self.action ) + if self.signals_id is not None: + result["signals_id"] = self.signals_id return result diff --git a/tests/fixtures/radar_standalone_assess_request.json b/tests/fixtures/radar_standalone_assess_request.json index 710b5312..969ae0ca 100644 --- a/tests/fixtures/radar_standalone_assess_request.json +++ b/tests/fixtures/radar_standalone_assess_request.json @@ -3,5 +3,6 @@ "user_agent": "Mozilla/5.0", "email": "user@example.com", "auth_method": "Password", - "action": "sign-in" + "action": "sign-in", + "signals_id": "01JBS0GN92GC2RJQS4X9DBPQ2A" } diff --git a/tests/test_common_models_round_trip.py b/tests/test_common_models_round_trip.py index d30a4ced..0980407c 100644 --- a/tests/test_common_models_round_trip.py +++ b/tests/test_common_models_round_trip.py @@ -4,24 +4,155 @@ from tests.generated_helpers import load_fixture -from workos.common.models import AuthMethodMismatchError +from workos.common.models import ( + ConnectApplicationM2M, + ConnectApplicationOAuth, + ConnectApplicationOAuthRedirectUris, +) class TestModelRoundTrip: - def test_auth_method_mismatch_error_round_trip(self): - data = load_fixture("auth_method_mismatch_error.json") - instance = AuthMethodMismatchError.from_dict(data) + def test_connect_application_oauth_round_trip(self): + data = load_fixture("connect_application_oauth.json") + instance = ConnectApplicationOAuth.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = AuthMethodMismatchError.from_dict(serialized) + restored = ConnectApplicationOAuth.from_dict(serialized) assert restored.to_dict() == serialized - def test_auth_method_mismatch_error_minimal_payload(self): + def test_connect_application_oauth_minimal_payload(self): data = { - "code": "auth_method_mismatch", - "message": "This installation uses oauth authentication. Use the POST /:slug/token endpoint instead.", + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": None, + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "application_type": "oauth", + "redirect_uris": [{"uri": "https://example.com/callback", "default": True}], + "uses_pkce": True, + "is_first_party": True, } - instance = AuthMethodMismatchError.from_dict(data) + instance = ConnectApplicationOAuth.from_dict(data) serialized = instance.to_dict() - assert serialized["code"] == data["code"] - assert serialized["message"] == data["message"] + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["client_id"] == data["client_id"] + assert serialized["description"] == data["description"] + assert serialized["name"] == data["name"] + assert serialized["scopes"] == data["scopes"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["application_type"] == data["application_type"] + assert serialized["redirect_uris"] == data["redirect_uris"] + assert serialized["uses_pkce"] == data["uses_pkce"] + assert serialized["is_first_party"] == data["is_first_party"] + + def test_connect_application_oauth_omits_absent_optional_non_nullable_fields(self): + data = { + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": "An application for managing user access", + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "application_type": "oauth", + "redirect_uris": [{"uri": "https://example.com/callback", "default": True}], + "uses_pkce": True, + "is_first_party": True, + } + instance = ConnectApplicationOAuth.from_dict(data) + serialized = instance.to_dict() + assert "was_dynamically_registered" not in serialized + assert "organization_id" not in serialized + + def test_connect_application_oauth_preserves_nullable_fields(self): + data = { + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": None, + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "application_type": "oauth", + "redirect_uris": [{"uri": "https://example.com/callback", "default": True}], + "uses_pkce": True, + "is_first_party": True, + "was_dynamically_registered": True, + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + } + instance = ConnectApplicationOAuth.from_dict(data) + serialized = instance.to_dict() + assert serialized["description"] is None + + def test_connect_application_oauth_redirect_uris_round_trip(self): + data = load_fixture("connect_application_oauth_redirect_uris.json") + instance = ConnectApplicationOAuthRedirectUris.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = ConnectApplicationOAuthRedirectUris.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_connect_application_oauth_redirect_uris_minimal_payload(self): + data = {"uri": "https://example.com/callback", "default": True} + instance = ConnectApplicationOAuthRedirectUris.from_dict(data) + serialized = instance.to_dict() + assert serialized["uri"] == data["uri"] + assert serialized["default"] == data["default"] + + def test_connect_application_m2m_round_trip(self): + data = load_fixture("connect_application_m2m.json") + instance = ConnectApplicationM2M.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = ConnectApplicationM2M.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_connect_application_m2m_minimal_payload(self): + data = { + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": None, + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "application_type": "m2m", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + } + instance = ConnectApplicationM2M.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["client_id"] == data["client_id"] + assert serialized["description"] == data["description"] + assert serialized["name"] == data["name"] + assert serialized["scopes"] == data["scopes"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["application_type"] == data["application_type"] + assert serialized["organization_id"] == data["organization_id"] + + def test_connect_application_m2m_preserves_nullable_fields(self): + data = { + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": None, + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "application_type": "m2m", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + } + instance = ConnectApplicationM2M.from_dict(data) + serialized = instance.to_dict() + assert serialized["description"] is None diff --git a/tests/test_radar_models_round_trip.py b/tests/test_radar_models_round_trip.py new file mode 100644 index 00000000..e0054408 --- /dev/null +++ b/tests/test_radar_models_round_trip.py @@ -0,0 +1,68 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Model round-trip tests: from_dict(to_dict()) preserves data.""" + +from tests.generated_helpers import load_fixture + +from workos.radar.models import ( + RadarListEntryAlreadyPresentResponse, + RadarStandaloneResponse, +) + + +class TestModelRoundTrip: + def test_radar_standalone_response_round_trip(self): + data = load_fixture("radar_standalone_response.json") + instance = RadarStandaloneResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = RadarStandaloneResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_radar_standalone_response_minimal_payload(self): + data = { + "verdict": "block", + "reason": "Detected enabled Radar control", + "attempt_id": "radar_att_01HZBC6N1EB1ZY7KG32X", + } + instance = RadarStandaloneResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized["verdict"] == data["verdict"] + assert serialized["reason"] == data["reason"] + assert serialized["attempt_id"] == data["attempt_id"] + + def test_radar_standalone_response_omits_absent_optional_non_nullable_fields(self): + data = { + "verdict": "block", + "reason": "Detected enabled Radar control", + "attempt_id": "radar_att_01HZBC6N1EB1ZY7KG32X", + } + instance = RadarStandaloneResponse.from_dict(data) + serialized = instance.to_dict() + assert "control" not in serialized + assert "blocklist_type" not in serialized + + def test_radar_standalone_response_round_trips_unknown_enum_values(self): + data = { + "verdict": "unexpected_radar_standalone_response_verdict", + "reason": "Detected enabled Radar control", + "attempt_id": "radar_att_01HZBC6N1EB1ZY7KG32X", + "control": "bot_detection", + "blocklist_type": "ip_address", + } + instance = RadarStandaloneResponse.from_dict(data) + assert instance.to_dict() == data + + def test_radar_list_entry_already_present_response_round_trip(self): + data = load_fixture("radar_list_entry_already_present_response.json") + instance = RadarListEntryAlreadyPresentResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = RadarListEntryAlreadyPresentResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_radar_list_entry_already_present_response_minimal_payload(self): + data = {"message": "Entry already present in list"} + instance = RadarListEntryAlreadyPresentResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized["message"] == data["message"] From 7607541c98bc9f653075c603056ed585a02dac9d Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:30:14 +0000 Subject: [PATCH 5/5] chore(generated): add release notes fragment --- ...1b13e04b6092eeef8253b4bfbcda055ceb2f35a.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .changelog-pending/2026-07-02T17-30-14-71b13e04b6092eeef8253b4bfbcda055ceb2f35a.md diff --git a/.changelog-pending/2026-07-02T17-30-14-71b13e04b6092eeef8253b4bfbcda055ceb2f35a.md b/.changelog-pending/2026-07-02T17-30-14-71b13e04b6092eeef8253b4bfbcda055ceb2f35a.md new file mode 100644 index 00000000..ddc5991f --- /dev/null +++ b/.changelog-pending/2026-07-02T17-30-14-71b13e04b6092eeef8253b4bfbcda055ceb2f35a.md @@ -0,0 +1,30 @@ +* [#686](https://github.com/workos/workos-python/pull/686) feat(generated)!: regenerate from spec (3 changes) + + **Features** + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Added model `SendRadarSmsChallenge` + * Added model `SendRadarSmsChallengeResponse` + * Added model `UrnWorkosOAuthGrantTypeRadarEmailChallengeCodeSessionAuthenticateRequest` + * Added model `UrnWorkosOAuthGrantTypeRadarSmsChallengeCodeSessionAuthenticateRequest` + * Added model `MagicAuthSendMagicAuthCodeAndReturnResponse` + * Added model `UserCreateResponse` + * Added `ip_address` to `CreateMagicCodeAndReturn` + * Added `user_agent` to `CreateMagicCodeAndReturn` + * Added `radar_auth_attempt_id` to `CreateMagicCodeAndReturn` + * Added `signals_id` to `CreateMagicCodeAndReturn` + * Added `ip_address` to `CreateUser` + * Added `user_agent` to `CreateUser` + * Added `signals_id` to `CreateUser` + * Added `signals_id` to `AuthorizationCodeSessionAuthenticateRequest` + * Added `signals_id` to `PasswordSessionAuthenticateRequest` + * Added `radar_auth_attempt_id` to `PasswordSessionAuthenticateRequest` + * Added `radar_auth_attempt_id` to `UrnWorkosOAuthGrantTypeMagicAuthCodeSessionAuthenticateRequest` + * Added endpoint `POST /user_management/radar_challenges` + * **[radar](https://workos.com/docs/reference/radar)**: + * Added `signals_id` to `RadarStandaloneAssessRequest` + + **Fixes** + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Changed request body for `UserManagementAuthentication.authenticate` + * Changed response of `UserManagementUsers.create` from `User` to `UserCreateResponse` + * Changed response of `UserManagementMagicAuth.sendMagicAuthCodeAndReturn` from `MagicAuth` to `MagicAuthSendMagicAuthCodeAndReturnResponse`