From 8b9221f3a6fc1a1fd0000f7cfb511eeccfdac4eb Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Tue, 11 Mar 2025 13:05:01 -0400 Subject: [PATCH 01/15] Improve Webhook and BaseWorkOSResource PHPDoc types --- lib/Resource/BaseWorkOSResource.php | 16 ++++++++++++++++ lib/Webhook.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/Resource/BaseWorkOSResource.php b/lib/Resource/BaseWorkOSResource.php index b8004a0..71b0a48 100644 --- a/lib/Resource/BaseWorkOSResource.php +++ b/lib/Resource/BaseWorkOSResource.php @@ -4,6 +4,22 @@ class BaseWorkOSResource { + /** + * Maps response keys to resource keys. + * Child classes should override this constant. + * + * @var array + */ + protected const RESPONSE_TO_RESOURCE_KEY = []; + + /** + * List of attributes available in this resource. + * Child classes should override this constant. + * + * @var array + */ + protected const RESOURCE_ATTRIBUTES = []; + /** * @var array $values; */ diff --git a/lib/Webhook.php b/lib/Webhook.php index 1435a1d..ce720fe 100644 --- a/lib/Webhook.php +++ b/lib/Webhook.php @@ -14,7 +14,7 @@ class Webhook /** * Initializes an Event object from a JSON payload * - * @return string + * @return string|Resource\Webhook */ public function constructEvent($sigHeader, $payload, $secret, $tolerance) { From 8988129c13234bb898161baabb5b96addc4b7e70 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Tue, 11 Mar 2025 13:16:09 -0400 Subject: [PATCH 02/15] Enhance Webhook class with improved code style and PHPDoc annotations --- lib/Resource/Webhook.php | 65 ++++++++++++++++++++++++++++++++++++++-- lib/Webhook.php | 37 +++++++++++------------ 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/lib/Resource/Webhook.php b/lib/Resource/Webhook.php index cf29a31..90539e6 100644 --- a/lib/Resource/Webhook.php +++ b/lib/Resource/Webhook.php @@ -6,15 +6,74 @@ * Class Webhook. * * Representation of a webhook resulting from a client ConstructEvent function. + * + * @property-read 'user_registration_action_context'|'authentication_action_context' $object The type of webhook event + * + * User Registration Action Properties + * @property-read ?object{ + * object: 'user_data', + * email: string, + * first_name: string, + * last_name: string + * } $user_data User information for registration events + * @property-read ?object{ + * object: 'invitation', + * id: string, + * email: string, + * expires_at: string, + * created_at: string, + * updated_at: string, + * accepted_at: ?string, + * revoked_at: ?string, + * organization_id: string, + * inviter_user_id: string + * } $invitation Invitation details for registration events + * + * Authentication Action Properties + * @property-read ?object{ + * object: 'user', + * id: string, + * email: string, + * first_name: string, + * last_name: string, + * email_verified: bool, + * profile_picture_url: string, + * created_at: string, + * updated_at: string + * } $user User information for authentication events + * @property-read ?string $issuer The authentication issuer + * @property-read ?object{ + * object: 'organization', + * id: string, + * name: string, + * allow_profiles_outside_organization: bool, + * domains: array, + * created_at: string, + * updated_at: string + * } $organization Organization details for authentication events + * @property-read ?object{ + * object: 'organization_membership', + * id: string, + * user_id: string, + * organization_id: string, + * role: array{slug: string}, + * status: string, + * created_at: string, + * updated_at: string + * } $organization_membership Organization membership details for authentication events + * + * Common Properties + * @property-read string $ip_address IP address of the event + * @property-read string $user_agent User agent string of the event + * @property-read string $device_fingerprint Device fingerprint of the event */ class Webhook { /** * Creates a webhook object from a payload. * - * @param $payload - * - * @return Webhook + * @param string $payload JSON string containing webhook data + * @return static */ public static function constructFromPayload($payload) { diff --git a/lib/Webhook.php b/lib/Webhook.php index ce720fe..64ea1a5 100644 --- a/lib/Webhook.php +++ b/lib/Webhook.php @@ -20,7 +20,7 @@ public function constructEvent($sigHeader, $payload, $secret, $tolerance) { $eventResult = $this->verifyHeader($sigHeader, $payload, $secret, $tolerance); - if ($eventResult == "pass") { + if ($eventResult == 'pass') { return Resource\Webhook::constructFromPayload($payload); } else { return $eventResult; @@ -31,45 +31,43 @@ public function constructEvent($sigHeader, $payload, $secret, $tolerance) * Verifies the header returned from WorkOS contains a valid timestamp * no older than 3 minutes, and computes the signature. * - * @param string $sigHeader WorkOS header containing v1 signature and timestamp - * @param string $payload Body of the webhook - * @param string $secret Webhook secret from the WorkOS dashboard - * @param int $tolerance Number of seconds old the webhook can be before it's invalid - * + * @param string $sigHeader WorkOS header containing v1 signature and timestamp + * @param string $payload Body of the webhook + * @param string $secret Webhook secret from the WorkOS dashboard + * @param int $tolerance Number of seconds old the webhook can be before it's invalid * @return bool true */ public function verifyHeader($sigHeader, $payload, $secret, $tolerance) { - $timestamp = (int)$this->getTimeStamp($sigHeader); + $timestamp = (int) $this->getTimeStamp($sigHeader); $signature = $this->getSignature($sigHeader); $currentTime = time(); - $signedPayload = $timestamp . "." . $payload; - $expectedSignature = hash_hmac("sha256", $signedPayload, $secret, false); + $signedPayload = $timestamp.'.'.$payload; + $expectedSignature = hash_hmac('sha256', $signedPayload, $secret, false); if (empty($timestamp)) { - return "No Timestamp available"; + return 'No Timestamp available'; } elseif (empty($signature)) { - return "No signature hash found with expected scheme v1"; + return 'No signature hash found with expected scheme v1'; } elseif ($timestamp < $currentTime - $tolerance) { - return "Timestamp outside of tolerance"; + return 'Timestamp outside of tolerance'; } elseif ($signature != $expectedSignature) { - return "Constructed signature " . $expectedSignature . "Does not match WorkOS Header Signature " . $signature; + return 'Constructed signature '.$expectedSignature.'Does not match WorkOS Header Signature '.$signature; } else { - return "pass"; + return 'pass'; } } /** * Splits WorkOS header's two values and pulls out timestamp value and returns it * - * @param string $sigHeader WorkOS header containing v1 signature and timestamp - * + * @param string $sigHeader WorkOS header containing v1 signature and timestamp * @return $timestamp */ public function getTimeStamp($sigHeader) { - $workosHeadersSplit = explode(",", $sigHeader, 2); + $workosHeadersSplit = explode(',', $sigHeader, 2); $timestamp = substr($workosHeadersSplit[0], 2); return $timestamp; @@ -78,13 +76,12 @@ public function getTimeStamp($sigHeader) /** * Splits WorkOS headers two values and pulls out the signature value and returns it * - * @param string $sigHeader WorkOS header containing v1 signature and timestamp - * + * @param string $sigHeader WorkOS header containing v1 signature and timestamp * @return string */ public function getSignature($sigHeader) { - $workosHeadersSplit = explode(",", $sigHeader, 2); + $workosHeadersSplit = explode(',', $sigHeader, 2); $signature = substr($workosHeadersSplit[1], 4); return $signature; From 92bc0c672a5c88bb5b28fc7144847fc912323dfe Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Sat, 15 Mar 2025 16:57:08 -0400 Subject: [PATCH 03/15] Add accessToken and refreshToken to AuthenticationResponse class --- lib/Resource/AuthenticationResponse.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Resource/AuthenticationResponse.php b/lib/Resource/AuthenticationResponse.php index b7bc2bf..0d44b8f 100644 --- a/lib/Resource/AuthenticationResponse.php +++ b/lib/Resource/AuthenticationResponse.php @@ -14,10 +14,14 @@ class AuthenticationResponse extends BaseWorkOSResource "user", "organizationId", "impersonator", + "accessToken", + "refreshToken", ]; public const RESPONSE_TO_RESOURCE_KEY = [ "organization_id" => "organizationId", + "access_token" => "accessToken", + "refresh_token" => "refreshToken", ]; public static function constructFromResponse($response) From eb67567bddca0724eda769799ca1515987694098 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Sat, 15 Mar 2025 17:28:56 -0400 Subject: [PATCH 04/15] Update AuthenticationResponse class to include organizationId as nullable, and add accessToken, refreshToken, and impersonator properties --- lib/Resource/AuthenticationResponse.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Resource/AuthenticationResponse.php b/lib/Resource/AuthenticationResponse.php index 0d44b8f..f4df188 100644 --- a/lib/Resource/AuthenticationResponse.php +++ b/lib/Resource/AuthenticationResponse.php @@ -6,7 +6,10 @@ * Class AuthenticationResponse. * * @property User $user - * @property string $organizationId + * @property ?string $organizationId + * @property string $accessToken + * @property string $refreshToken + * @property ?Impersonator $impersonator */ class AuthenticationResponse extends BaseWorkOSResource { From 0c5672a71d94fa905498968d1a9835cb336525ad Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Sat, 15 Mar 2025 17:56:11 -0400 Subject: [PATCH 05/15] Enhance OrganizationMembership class with additional PHPDoc properties for improved type documentation --- lib/Resource/OrganizationMembership.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Resource/OrganizationMembership.php b/lib/Resource/OrganizationMembership.php index 09f2749..00667dc 100644 --- a/lib/Resource/OrganizationMembership.php +++ b/lib/Resource/OrganizationMembership.php @@ -4,8 +4,16 @@ /** * Class OrganizationMembership. + * + * @property 'organization_membership' $object + * @property string $id + * @property string $userId + * @property string $organizationId + * @property RoleResponse $role + * @property 'active'|'inactive'|'pending' $status + * @property string $createdAt + * @property string $updatedAt */ - class OrganizationMembership extends BaseWorkOSResource { public const RESOURCE_TYPE = "organization_membership"; @@ -15,6 +23,7 @@ class OrganizationMembership extends BaseWorkOSResource "id", "userId", "organizationId", + "role", "status", "createdAt", "updatedAt" @@ -25,6 +34,7 @@ class OrganizationMembership extends BaseWorkOSResource "id" => "id", "user_id" => "userId", "organization_id" => "organizationId", + "role" => "role", "status" => "status", "created_at" => "createdAt", "updated_at" => "updatedAt" From ff55574e950969723ba9cf83a94f5862f2f143aa Mon Sep 17 00:00:00 2001 From: Eric Roberts Date: Fri, 21 Mar 2025 13:56:38 -0400 Subject: [PATCH 06/15] Add metadata and external id (#268) And allow to be passed when creating or updating a user or organization. --- lib/Organizations.php | 39 ++++++++++++++++++++++++++--- lib/Resource/Organization.php | 8 ++++-- lib/Resource/User.php | 8 ++++-- lib/UserManagement.php | 25 +++++++++++++++--- tests/WorkOS/OrganizationsTest.php | 20 +++++++++++---- tests/WorkOS/UserManagementTest.php | 32 +++++++++++++++++------ 6 files changed, 109 insertions(+), 23 deletions(-) diff --git a/lib/Organizations.php b/lib/Organizations.php index 2085901..4f9f095 100644 --- a/lib/Organizations.php +++ b/lib/Organizations.php @@ -67,13 +67,22 @@ public function listOrganizations( * @param null|boolean $allowProfilesOutsideOrganization [Deprecated] If you need to allow sign-ins from * any email domain, contact support@workos.com. * @param null|string $idempotencyKey is a unique string that identifies a distinct organization + * @param null|string $externalId The organization's external id + * @param null|array $metadata The organization's metadata * * @throws Exception\WorkOSException * * @return Resource\Organization */ - public function createOrganization($name, $domains = null, $allowProfilesOutsideOrganization = null, $idempotencyKey = null, $domain_data = null) - { + public function createOrganization( + $name, + $domains = null, + $allowProfilesOutsideOrganization = null, + $idempotencyKey = null, + $domain_data = null, + $externalId = null, + $metadata = null + ) { $idempotencyKey ? $headers = array("Idempotency-Key: $idempotencyKey") : $headers = null; $organizationsPath = "organizations"; @@ -88,6 +97,12 @@ public function createOrganization($name, $domains = null, $allowProfilesOutside if (isset($allowProfilesOutsideOrganization)) { $params["allow_profiles_outside_organization"] = $allowProfilesOutsideOrganization; } + if (isset($externalId)) { + $params["external_id"] = $externalId; + } + if (isset($metadata)) { + $params["metadata"] = $metadata; + } $response = Client::request(Client::METHOD_POST, $organizationsPath, $headers, $params, true); @@ -104,11 +119,21 @@ public function createOrganization($name, $domains = null, $allowProfilesOutside * @param null|boolean $allowProfilesOutsideOrganization [Deprecated] If you need to allow sign-ins from * any email domain, contact support@workos.com. * @param null|string $stripeCustomerId The Stripe Customer ID of the Organization. + * @param null|string $externalId The organization's external id + * @param null|array $metadata The organization's metadata * * @throws Exception\WorkOSException */ - public function updateOrganization($organization, $domains = null, $name = null, $allowProfilesOutsideOrganization = null, $domain_data = null, $stripeCustomerId = null) - { + public function updateOrganization( + $organization, + $domains = null, + $name = null, + $allowProfilesOutsideOrganization = null, + $domain_data = null, + $stripeCustomerId = null, + $externalId = null, + $metadata = null + ) { $organizationsPath = "organizations/{$organization}"; $params = [ "name" => $name ]; @@ -125,6 +150,12 @@ public function updateOrganization($organization, $domains = null, $name = null, if (isset($stripeCustomerId)) { $params["stripe_customer_id"] = $stripeCustomerId; } + if (isset($externalId)) { + $params["external_id"] = $externalId; + } + if (isset($metadata)) { + $params["metadata"] = $metadata; + } $response = Client::request(Client::METHOD_PUT, $organizationsPath, null, $params, true); diff --git a/lib/Resource/Organization.php b/lib/Resource/Organization.php index 659ee23..47ef5a2 100644 --- a/lib/Resource/Organization.php +++ b/lib/Resource/Organization.php @@ -14,13 +14,17 @@ class Organization extends BaseWorkOSResource "id", "name", "allowProfilesOutsideOrganization", - "domains" + "domains", + "externalId", + "metadata" ]; public const RESPONSE_TO_RESOURCE_KEY = [ "id" => "id", "name" => "name", "allow_profiles_outside_organization" => "allowProfilesOutsideOrganization", - "domains" => "domains" + "domains" => "domains", + "external_id" => "externalId", + "metadata" => "metadata" ]; } diff --git a/lib/Resource/User.php b/lib/Resource/User.php index 7df0c7a..b819a4a 100644 --- a/lib/Resource/User.php +++ b/lib/Resource/User.php @@ -19,7 +19,9 @@ class User extends BaseWorkOSResource "profilePictureUrl", "lastSignInAt", "createdAt", - "updatedAt" + "updatedAt", + "externalId", + "metadata" ]; public const RESPONSE_TO_RESOURCE_KEY = [ @@ -32,6 +34,8 @@ class User extends BaseWorkOSResource "profile_picture_url" => "profilePictureUrl", "last_sign_in_at" => "lastSignInAt", "created_at" => "createdAt", - "updated_at" => "updatedAt" + "updated_at" => "updatedAt", + "external_id" => "externalId", + "metadata" => "metadata" ]; } diff --git a/lib/UserManagement.php b/lib/UserManagement.php index febeecc..b402455 100644 --- a/lib/UserManagement.php +++ b/lib/UserManagement.php @@ -26,13 +26,24 @@ class UserManagement * @param boolean|null $emailVerified A boolean declaring if the user's email has been verified. * @param string|null $passwordHash The hashed password to set for the user. * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. + * @param string|null $externalId The user's external ID. + * @param array $metadata The user's metadata. * * @throws Exception\WorkOSException * * @return Resource\User */ - public function createUser($email, $password = null, $firstName = null, $lastName = null, $emailVerified = null, $passwordHash = null, $passwordHashType = null) - { + public function createUser( + $email, + $password = null, + $firstName = null, + $lastName = null, + $emailVerified = null, + $passwordHash = null, + $passwordHashType = null, + $externalId = null, + $metadata = null + ) { $path = "user_management/users"; $params = [ "email" => $email, @@ -42,6 +53,8 @@ public function createUser($email, $password = null, $firstName = null, $lastNam "email_verified" => $emailVerified, "password_hash" => $passwordHash, "password_hash_type" => $passwordHashType, + "external_id" => $externalId, + "metadata" => $metadata ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -77,6 +90,8 @@ public function getUser($userId) * @param string|null $password The password to set for the user. * @param string|null $passwordHash The hashed password to set for the user. * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. + * @param string|null $externalId The user's external ID. + * @param array|null $metadata The user's metadata. * * @throws Exception\WorkOSException * @@ -89,7 +104,9 @@ public function updateUser( $emailVerified = null, $password = null, $passwordHash = null, - $passwordHashType = null + $passwordHashType = null, + $externalId = null, + $metadata = null ) { $path = "user_management/users/{$userId}"; @@ -100,6 +117,8 @@ public function updateUser( "password" => $password, "password_hash" => $passwordHash, "password_hash_type" => $passwordHashType, + "external_id" => $externalId, + "metadata" => $metadata ]; $response = Client::request(Client::METHOD_PUT, $path, null, $params, true); diff --git a/tests/WorkOS/OrganizationsTest.php b/tests/WorkOS/OrganizationsTest.php index 8f05b1c..89bbf42 100644 --- a/tests/WorkOS/OrganizationsTest.php +++ b/tests/WorkOS/OrganizationsTest.php @@ -205,7 +205,9 @@ private function createOrganizationResponseFixture() "id" => "org_domain_01EHQMYV71XT8H31WE5HF8YK4A", "domain" => "example.com" ] - ] + ], + "external_id" => null, + "metadata" => [] ]); } @@ -225,7 +227,9 @@ private function organizationsResponseFixture() "id" => "org_domain_01EHQMYV71XT8H31WE5HF8YK4A", "domain" => "example.com" ] - ] + ], + "external_id" => null, + "metadata" => [] ], [ "object" => "organization", @@ -238,7 +242,9 @@ private function organizationsResponseFixture() "id" => "org_domain_01EHQMVDTZVA27PK614ME4YK7V", "domain" => "example2.com" ] - ] + ], + "external_id" => null, + "metadata" => [] ], [ "object" => "organization", @@ -251,7 +257,9 @@ private function organizationsResponseFixture() "id" => "org_domain_01EGP9Z6S6HVQ5CPD152GJBEA5", "domain" => "example5.com" ] - ] + ], + "external_id" => null, + "metadata" => [] ] ], "list_metadata" => [ @@ -273,7 +281,9 @@ private function organizationFixture() "id" => "org_domain_01EHQMYV71XT8H31WE5HF8YK4A", "domain" => "example.com" ] - ] + ], + "externalId" => null, + "metadata" => [] ]; } diff --git a/tests/WorkOS/UserManagementTest.php b/tests/WorkOS/UserManagementTest.php index 2c6b9bf..1b6ff72 100644 --- a/tests/WorkOS/UserManagementTest.php +++ b/tests/WorkOS/UserManagementTest.php @@ -55,6 +55,8 @@ public function testUpdateUser() "password" => null, "password_hash" => null, "password_hash_type" => null, + "external_id" => null, + "metadata" => null ]; $this->mockRequest( @@ -481,6 +483,8 @@ public function testCreateUser() "email_verified" => true, "password_hash" => null, "password_hash_type" => null, + "external_id" => null, + "metadata" => null ]; $this->mockRequest( @@ -1442,7 +1446,9 @@ private function userResponseFixture() "profile_picture_url" => "https://example.com/photo.jpg", "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ] ]); } @@ -1460,7 +1466,9 @@ private function userAndImpersonatorResponseFixture() "profile_picture_url" => "https://example.com/photo.jpg", "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ], "impersonator" => [ "email" => "admin@foocorp.com", @@ -1500,7 +1508,9 @@ private function createUserResponseFixture() 'profile_picture_url' => 'https://example.com/photo.jpg', "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ]); } @@ -1608,7 +1618,9 @@ private function getUserResponseFixture() "profile_picture_url" => "https://example.com/photo.jpg", "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ]); } @@ -1626,7 +1638,9 @@ private function listUsersResponseFixture() "profile_picture_url" => "https://example.com/photo.jpg", "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ] ], "list_metadata" => [ @@ -1656,7 +1670,9 @@ private function userFixture() "profilePictureUrl" => "https://example.com/photo.jpg", "lastSignInAt" => "2021-06-25T19:07:33.155Z", "createdAt" => "2021-06-25T19:07:33.155Z", - "updatedAt" => "2021-06-25T19:07:33.155Z" + "updatedAt" => "2021-06-25T19:07:33.155Z", + "externalId" => null, + "metadata" => [] ]; } @@ -1673,7 +1689,9 @@ private function userAndOrgResponseFixture() "profile_picture_url" => "https://example.com/photo.jpg", "last_sign_in_at" => "2021-06-25T19:07:33.155Z", "created_at" => "2021-06-25T19:07:33.155Z", - "updated_at" => "2021-06-25T19:07:33.155Z" + "updated_at" => "2021-06-25T19:07:33.155Z", + "external_id" => null, + "metadata" => [] ], "organization_id" => "org_01EHQMYV6MBK39QC5PZXHY59C3", ]); From 09c8949b0b54cc6505ab3678a39530789905647f Mon Sep 17 00:00:00 2001 From: Matt Dzwonczyk <9063128+mattgd@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:08:26 -0400 Subject: [PATCH 07/15] Add email standard attribute to DirectoryUser and mark deprecated standard attributes (#261) --- lib/Resource/DirectoryUser.php | 20 ++++++++++++++++++++ tests/WorkOS/DirectorySyncTest.php | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/lib/Resource/DirectoryUser.php b/lib/Resource/DirectoryUser.php index 2dfed82..eb5a809 100644 --- a/lib/Resource/DirectoryUser.php +++ b/lib/Resource/DirectoryUser.php @@ -14,9 +14,25 @@ class DirectoryUser extends BaseWorkOSResource "rawAttributes", "customAttributes", "firstName", + "email", + /** + * [Deprecated] Will be removed in a future major version. + * Enable the `emails` custom attribute in dashboard and pull from customAttributes instead. + * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. + */ "emails", + /** + * [Deprecated] Will be removed in a future major version. + * Enable the `username` custom attribute in dashboard and pull from customAttributes instead. + * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. + */ "username", "lastName", + /** + * [Deprecated] Will be removed in a future major version. + * Enable the `job_title` custom attribute in dashboard and pull from customAttributes instead. + * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. + */ "jobTitle", "state", "idpId", @@ -30,6 +46,7 @@ class DirectoryUser extends BaseWorkOSResource "raw_attributes" => "rawAttributes", "custom_attributes" => "customAttributes", "first_name" => "firstName", + "email" => "email", "emails" => "emails", "username" => "username", "last_name" => "lastName", @@ -41,6 +58,9 @@ class DirectoryUser extends BaseWorkOSResource "organization_id" => "organizationId" ]; + /** + * [Deprecated] Use `email` instead. + */ public function primaryEmail() { $response = $this; diff --git a/tests/WorkOS/DirectorySyncTest.php b/tests/WorkOS/DirectorySyncTest.php index ebed37d..c75dcd9 100644 --- a/tests/WorkOS/DirectorySyncTest.php +++ b/tests/WorkOS/DirectorySyncTest.php @@ -354,6 +354,7 @@ private function usersResponseFixture() "organization_id" => "org_123", "idp_id" => null, "groups" => null, + "email" => "yoon@seri.com", "emails" => [ [ "primary" => true, @@ -409,6 +410,7 @@ private function userResponseFixture() "organization_id" => "org_123", "idp_id" => null, "groups" => null, + "email" => "yoon@seri.com", "emails" => [ [ "primary" => true, @@ -462,6 +464,7 @@ private function userResponseFixtureNoEmail() "organization_id" => "org_123", "idp_id" => null, "groups" => null, + "email" => null, "emails" => [], "raw_attributes" => [ "schemas" => ["urn:scim:schemas:core:1.0"], @@ -532,6 +535,7 @@ private function userFixture() "fullName" => "Yoon Seri" ], "firstName" => "Yoon", + "email" => "yoon@seri.com", "emails" => [ [ "primary" => true, From 829e4186b9538b784e6e805f2feb777b58d7e686 Mon Sep 17 00:00:00 2001 From: Eric Roberts Date: Fri, 21 Mar 2025 14:46:09 -0400 Subject: [PATCH 08/15] Add function to get organization by external id (#270) And fix typo in getOrganization docstring --- lib/Organizations.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Organizations.php b/lib/Organizations.php index 4f9f095..dc62b78 100644 --- a/lib/Organizations.php +++ b/lib/Organizations.php @@ -163,7 +163,7 @@ public function updateOrganization( } /** - * Get a Directory Group. + * Get an Organization * * @param string $organization WorkOS organization ID * @@ -180,6 +180,24 @@ public function getOrganization($organization) return Resource\Organization::constructFromResponse($response); } + /** + * Get an Organization by its external id + * + * @param string $externalId external id + * + * @throws Exception\WorkOSException + * + * @return Resource\Organization + */ + public function getOrganizationByExternalId($externalId) + { + $organizationsPath = "organizations/external_id/{$externalId}"; + + $response = Client::request(Client::METHOD_GET, $organizationsPath, null, null, true); + + return Resource\Organization::constructFromResponse($response); + } + /** * Delete an Organization. * From f2d7f7bfbe045ee10290bcef980c081a42ecbd5f Mon Sep 17 00:00:00 2001 From: Pepe Date: Fri, 21 Mar 2025 19:49:05 +0100 Subject: [PATCH 09/15] Add support for creating, getting and updating users with external_id property (#267) Co-authored-by: Eric Roberts --- lib/UserManagement.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/UserManagement.php b/lib/UserManagement.php index b402455..f40bd88 100644 --- a/lib/UserManagement.php +++ b/lib/UserManagement.php @@ -33,6 +33,7 @@ class UserManagement * * @return Resource\User */ + public function createUser( $email, $password = null, @@ -80,6 +81,24 @@ public function getUser($userId) return Resource\User::constructFromResponse($response); } + /** + * Get a User by external ID. + * + * @param string $externalId The external ID of the user. + * + * @throws Exception\WorkOSException + * + * @return Resource\User + */ + public function getUserByExternalId($externalId) + { + $path = "user_management/users/external_id/{$externalId}"; + + $response = Client::request(Client::METHOD_GET, $path, null, null, true); + + return Resource\User::constructFromResponse($response); + } + /** * Update a User * From d366c83050863fd4e902febd623f799d1432d60a Mon Sep 17 00:00:00 2001 From: Matt Dzwonczyk <9063128+mattgd@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:08:31 -0400 Subject: [PATCH 10/15] Bump to version 4.22.0. (#269) --- lib/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Version.php b/lib/Version.php index 6a589a7..5005602 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -6,5 +6,5 @@ final class Version { public const SDK_IDENTIFIER = 'WorkOS PHP'; - public const SDK_VERSION = '4.21.0'; + public const SDK_VERSION = '4.22.0'; } From 1ec12f4b234e367e351479ca528b5b73de3a6614 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Fri, 21 Mar 2025 16:19:52 -0400 Subject: [PATCH 11/15] Structured responses to webhook events (#265) * Add WebhookResponse class for handling webhook actions and responses * Refactor WebhookResponse create method and improve validation * Resolve linting error --------- Co-authored-by: Braden Keith --- lib/Resource/WebhookResponse.php | 111 +++++++++++++ lib/Webhook.php | 17 +- tests/WorkOS/Resource/WebhookResponseTest.php | 149 ++++++++++++++++++ 3 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 lib/Resource/WebhookResponse.php create mode 100644 tests/WorkOS/Resource/WebhookResponseTest.php diff --git a/lib/Resource/WebhookResponse.php b/lib/Resource/WebhookResponse.php new file mode 100644 index 0000000..3f2032c --- /dev/null +++ b/lib/Resource/WebhookResponse.php @@ -0,0 +1,111 @@ +object = $type; + + $payload = [ + 'timestamp' => time() * 1000, + 'verdict' => $verdict + ]; + + if ($verdict === self::VERDICT_DENY) { + $payload['error_message'] = $errorMessage; + } + + $instance->payload = $payload; + + $timestamp = $payload['timestamp']; + $payloadString = json_encode($payload); + $instance->signature = (new Webhook())->computeSignature($timestamp, $payloadString, $secret); + + return $instance; + } + + /** + * Get the response as an array + * + * @return array + */ + public function toArray() + { + $response = [ + 'object' => $this->object, + 'payload' => $this->payload + ]; + + if ($this->signature) { + $response['signature'] = $this->signature; + } + + return $response; + } + + /** + * Get the response as a JSON string + * + * @return string + */ + public function toJson() + { + return json_encode($this->toArray()); + } +} diff --git a/lib/Webhook.php b/lib/Webhook.php index 64ea1a5..4d5d517 100644 --- a/lib/Webhook.php +++ b/lib/Webhook.php @@ -43,8 +43,7 @@ public function verifyHeader($sigHeader, $payload, $secret, $tolerance) $signature = $this->getSignature($sigHeader); $currentTime = time(); - $signedPayload = $timestamp.'.'.$payload; - $expectedSignature = hash_hmac('sha256', $signedPayload, $secret, false); + $expectedSignature = $this->computeSignature($timestamp, $payload, $secret); if (empty($timestamp)) { return 'No Timestamp available'; @@ -86,4 +85,18 @@ public function getSignature($sigHeader) return $signature; } + + /** + * Computes a signature for a webhook payload using the provided timestamp and secret + * + * @param int $timestamp Unix timestamp to use in signature + * @param string $payload The payload to sign + * @param string $secret Secret key used for signing + * @return string The computed HMAC SHA-256 signature + */ + public function computeSignature($timestamp, $payload, $secret) + { + $signedPayload = $timestamp . '.' . $payload; + return hash_hmac('sha256', $signedPayload, $secret, false); + } } diff --git a/tests/WorkOS/Resource/WebhookResponseTest.php b/tests/WorkOS/Resource/WebhookResponseTest.php new file mode 100644 index 0000000..9bda146 --- /dev/null +++ b/tests/WorkOS/Resource/WebhookResponseTest.php @@ -0,0 +1,149 @@ +traitSetUp(); + $this->withApiKey(); + + $this->secret = 'test_secret'; + $this->timestamp = time() * 1000; // milliseconds + } + + public function testCreateAllowResponse() + { + $response = WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + $this->secret, + WebhookResponse::VERDICT_ALLOW + ); + + $array = $response->toArray(); + + $this->assertEquals(WebhookResponse::USER_REGISTRATION_ACTION, $array['object']); + $this->assertArrayHasKey('payload', $array); + $this->assertArrayHasKey('signature', $array); + $this->assertEquals(WebhookResponse::VERDICT_ALLOW, $array['payload']['verdict']); + $this->assertArrayHasKey('timestamp', $array['payload']); + $this->assertArrayNotHasKey('error_message', $array['payload']); + } + + public function testCreateDenyResponse() + { + $errorMessage = 'Registration denied due to risk assessment'; + $response = WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + $this->secret, + WebhookResponse::VERDICT_DENY, + $errorMessage + ); + + $array = $response->toArray(); + + $this->assertEquals(WebhookResponse::USER_REGISTRATION_ACTION, $array['object']); + $this->assertArrayHasKey('payload', $array); + $this->assertArrayHasKey('signature', $array); + $this->assertEquals(WebhookResponse::VERDICT_DENY, $array['payload']['verdict']); + $this->assertEquals($errorMessage, $array['payload']['error_message']); + $this->assertArrayHasKey('timestamp', $array['payload']); + } + + public function testCreateAuthenticationResponse() + { + $response = WebhookResponse::create( + WebhookResponse::AUTHENTICATION_ACTION, + $this->secret, + WebhookResponse::VERDICT_ALLOW + ); + + $array = $response->toArray(); + + $this->assertEquals(WebhookResponse::AUTHENTICATION_ACTION, $array['object']); + $this->assertArrayHasKey('payload', $array); + $this->assertArrayHasKey('signature', $array); + } + + public function testCreateWithoutSecret() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Secret is required'); + + WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + '', + WebhookResponse::VERDICT_ALLOW + ); + } + + public function testInvalidResponseType() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid response type'); + + WebhookResponse::create( + 'invalid_type', + $this->secret, + WebhookResponse::VERDICT_ALLOW + ); + } + + public function testInvalidVerdict() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid verdict'); + + WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + $this->secret, + 'invalid_verdict' + ); + } + + public function testDenyWithoutErrorMessage() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Error message is required when verdict is Deny'); + + WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + $this->secret, + WebhookResponse::VERDICT_DENY + ); + } + + public function testToJson() + { + $response = WebhookResponse::create( + WebhookResponse::USER_REGISTRATION_ACTION, + $this->secret, + WebhookResponse::VERDICT_ALLOW + ); + + $json = $response->toJson(); + $decoded = json_decode($json, true); + + $this->assertIsString($json); + $this->assertIsArray($decoded); + $this->assertEquals(WebhookResponse::USER_REGISTRATION_ACTION, $decoded['object']); + } +} From 19e68d1ffe506b9ad608a41600eedd6dadd07d53 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Fri, 28 Mar 2025 14:01:56 -0400 Subject: [PATCH 12/15] Update deprecation notices in DirectoryUser class to include version information and improve clarity --- lib/Resource/DirectoryUser.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Resource/DirectoryUser.php b/lib/Resource/DirectoryUser.php index eb5a809..78be8ec 100644 --- a/lib/Resource/DirectoryUser.php +++ b/lib/Resource/DirectoryUser.php @@ -16,20 +16,20 @@ class DirectoryUser extends BaseWorkOSResource "firstName", "email", /** - * [Deprecated] Will be removed in a future major version. + * @deprecated 4.22.0 Will be removed in a future major version. * Enable the `emails` custom attribute in dashboard and pull from customAttributes instead. * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. */ "emails", /** - * [Deprecated] Will be removed in a future major version. + * @deprecated 4.22.0 Will be removed in a future major version. * Enable the `username` custom attribute in dashboard and pull from customAttributes instead. * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. */ "username", "lastName", /** - * [Deprecated] Will be removed in a future major version. + * @deprecated 4.22.0 Will be removed in a future major version. * Enable the `job_title` custom attribute in dashboard and pull from customAttributes instead. * See https://workos.com/docs/directory-sync/attributes/custom-attributes/auto-mapped-attributes for details. */ @@ -59,7 +59,9 @@ class DirectoryUser extends BaseWorkOSResource ]; /** - * [Deprecated] Use `email` instead. + * @deprecated 4.22.0 Use `email` property instead. + * + * @return string|null The primary email address if found, null otherwise */ public function primaryEmail() { From 52dc0ba7248630612ec9aeacdecf37b680df7e75 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Fri, 28 Mar 2025 14:10:42 -0400 Subject: [PATCH 13/15] Update deprecation notices in Organizations class to include version information and improve formatting --- lib/Organizations.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/Organizations.php b/lib/Organizations.php index dc62b78..283be2e 100644 --- a/lib/Organizations.php +++ b/lib/Organizations.php @@ -34,11 +34,11 @@ public function listOrganizations( ) { $organizationsPath = "organizations"; $params = [ - "limit" => $limit, - "before" => $before, - "after" => $after, - "domains" => $domains, - "order" => $order + "limit" => $limit, + "before" => $before, + "after" => $after, + "domains" => $domains, + "order" => $order ]; $response = Client::request( @@ -62,9 +62,9 @@ public function listOrganizations( * Create Organization. * * @param string $name The name of the Organization. - * @param null|array $domains [Deprecated] The domains of the Organization. Use domain_data instead. + * @param null|array $domains @deprecated 4.5.0 The domains of the Organization. Use domain_data instead. * @param null|array $domain_data The domains of the Organization. - * @param null|boolean $allowProfilesOutsideOrganization [Deprecated] If you need to allow sign-ins from + * @param null|boolean $allowProfilesOutsideOrganization @deprecated 4.5.0 If you need to allow sign-ins from * any email domain, contact support@workos.com. * @param null|string $idempotencyKey is a unique string that identifies a distinct organization * @param null|string $externalId The organization's external id @@ -86,7 +86,7 @@ public function createOrganization( $idempotencyKey ? $headers = array("Idempotency-Key: $idempotencyKey") : $headers = null; $organizationsPath = "organizations"; - $params = [ "name" => $name ]; + $params = ["name" => $name]; if (isset($domains)) { $params["domains"] = $domains; @@ -113,10 +113,10 @@ public function createOrganization( * Update Organization. * * @param string $organization An Organization identifier. - * @param null|array $domains [Deprecated] The domains of the Organization. Use domain_data instead. + * @param null|array $domains @deprecated 4.5.0 The domains of the Organization. Use domain_data instead. * @param null|array $domain_data The domains of the Organization. * @param null|string $name The name of the Organization. - * @param null|boolean $allowProfilesOutsideOrganization [Deprecated] If you need to allow sign-ins from + * @param null|boolean $allowProfilesOutsideOrganization @deprecated 4.5.0 If you need to allow sign-ins from * any email domain, contact support@workos.com. * @param null|string $stripeCustomerId The Stripe Customer ID of the Organization. * @param null|string $externalId The organization's external id @@ -136,7 +136,7 @@ public function updateOrganization( ) { $organizationsPath = "organizations/{$organization}"; - $params = [ "name" => $name ]; + $params = ["name" => $name]; if (isset($domains)) { $params["domains"] = $domains; From b25b57d8924f1b7f39df07c0222f4a751393a630 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Fri, 28 Mar 2025 14:53:23 -0400 Subject: [PATCH 14/15] Update doc blocks for deprecation notices --- lib/AuditLogs.php | 57 +++++++++++++++++++++--------------------- lib/MFA.php | 3 ++- lib/SSO.php | 2 +- lib/UserManagement.php | 2 ++ 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/AuditLogs.php b/lib/AuditLogs.php index 40b0835..464e1ac 100644 --- a/lib/AuditLogs.php +++ b/lib/AuditLogs.php @@ -10,36 +10,35 @@ class AuditLogs { /** + * Creates an audit log event for an organization. * - * @param string $organizationId the unique identifier for the organization. - * @param array $event Associative array containing the keys detailed below - * string "action" Specific activity performed by the actor. REQUIRED. - * string "occurred_at" ISO-8601 datetime at which the event happened. REQUIRED. - * array "actor" Associative array describing Actor of the event. REQUIRED. - * KEYS: - * string "id" - REQUIRED - * string "name" - NOT REQUIRED - * string "type" - REQUIRED - * array "metadata" - Associative array ["Any Key" => "Any Value] - NOT REQUIRED - * array "targets" Targets of the event. Nested array as there can be multiple targets. REQUIRED - * KEYS: - * string "id" - REQUIRED - * string "name" - NOT REQUIRED - * string "type" - REQUIRED - * array "metadata" - Associative array ["Any Key" => "Any Value] - NOT REQUIRED - * array "context" Context of the event. REQUIRED. - * KEYS: - * string "location" - REQUIRED - * string "user_agent" - NOT REQUIRED - * int "version" Version of the event. Required if version is anything other than 1. NOT REQUIRED. - * array "metadata" Arbitrary key-value data containing information associated with the event. NOT REQUIRED - * @param string $idempotencyKey Unique key guaranteeing idempotency of events for 24 hours. + * @param string $organizationId The unique identifier for the organization. + * @param array $event An associative array with the following keys: + * - **action** (string, *required*): Specific activity performed by the actor. + * - **occurred_at** (string, *required*): ISO-8601 datetime when the event occurred. + * - **actor** (array, *required*): Associative array describing the actor. + * - **id** (string, *required*): Unique identifier for the actor. + * - **name** (string, *optional*): Name of the actor. + * - **type** (string, *required*): Type or role of the actor. + * - **metadata** (array, *optional*): Arbitrary key-value data. + * - **targets** (array, *required*): Array of associative arrays for each target. + * Each target includes: + * - **id** (string, *required*): Unique identifier for the target. + * - **name** (string, *optional*): Name of the target. + * - **type** (string, *required*): Type or category of the target. + * - **metadata** (array, *optional*): Arbitrary key-value data. + * - **context** (array, *required*): Associative array providing additional context. + * - **location** (string, *required*): Location associated with the event. + * - **user_agent** (string, *optional*): User agent string if applicable. + * - **version** (int, *optional*): Event version. Required if the version is not 1. + * - **metadata** (array, *optional*): Additional arbitrary key-value data for the event. + * + * @param string $idempotencyKey A unique key ensuring idempotency of events for 24 hours. * * @throws Exception\WorkOSException * - * @return Resource\AuditLogCreateEventStatus + * @return Resource\AuditLogCreateEventStatus */ - public function createEvent($organizationId, $event, $idempotencyKey = null) { $eventsPath = "audit_logs/events"; @@ -64,7 +63,7 @@ public function createEvent($organizationId, $event, $idempotencyKey = null) * @var null|string $rangeStart ISO-8601 Timestamp of the start of Export's the date range. * @var null|string $rangeEnd ISO-8601 Timestamp of the end of Export's the date range. * @var null|array $actions Actions that Audit Log Events will be filtered by. - * @var null|array $actors Actor names that Audit Log Events will be filtered by. + * @var null|array $actors Actor names that Audit Log Events will be filtered by. @deprecated 3.3.0 Use $actorNames instead. This method will be removed in a future major version. * @var null|array $targets Target types that Audit Log Events will be filtered by. * @var null|array $actorNames Actor names that Audit Log Events will be filtered by. * @var null|array $actorIds Actor IDs that Audit Log Events will be filtered by. @@ -79,9 +78,9 @@ public function createExport($organizationId, $rangeStart, $rangeEnd, $actions = $createExportPath = "audit_logs/exports"; $params = [ - "organization_id" => $organizationId, - "range_end" => $rangeEnd, - "range_start" => $rangeStart + "organization_id" => $organizationId, + "range_end" => $rangeEnd, + "range_start" => $rangeStart ]; if (!is_null($actions)) { diff --git a/lib/MFA.php b/lib/MFA.php index 771d5c3..abdb358 100644 --- a/lib/MFA.php +++ b/lib/MFA.php @@ -108,11 +108,12 @@ public function challengeFactor( /** + * @deprecated 1.12.0 Use `verifyChallenge` instead. This method will be removed in a future major version. * Verifies the one time password provided by the end-user. * * @param string $authenticationChallengeId - The ID of the authentication challenge that provided the user the verification code. * @param string $code - The verification code sent to and provided by the end user. - */ + */ public function verifyFactor( $authenticationChallengeId, diff --git a/lib/SSO.php b/lib/SSO.php index 746f7e0..096f581 100644 --- a/lib/SSO.php +++ b/lib/SSO.php @@ -13,7 +13,7 @@ class SSO /** * Generates an OAuth 2.0 authorization URL used to initiate the SSO flow with WorkOS. * - * @param null|string $domain Domain of the user that will be going through SSO + * @param null|string $domain Domain of the user that will be going through SSO @deprecated 1.5.0 Use $connection or $organization instead. * @param null|string $redirectUri URI to direct the user to upon successful completion of SSO * @param null|array $state Associative array containing state that will be returned from WorkOS as a json encoded string * @param null|string $provider Service provider that handles the identity of the user diff --git a/lib/UserManagement.php b/lib/UserManagement.php index f40bd88..e21eae6 100644 --- a/lib/UserManagement.php +++ b/lib/UserManagement.php @@ -1096,6 +1096,7 @@ public function createPasswordReset( } /** + * @deprecated 4.9.0 Use `createPasswordReset` instead. This method will be removed in a future major version. * Create Password Reset Email. * * @param string $email The email of the user that wishes to reset their password. @@ -1204,6 +1205,7 @@ public function createMagicAuth( } /** + * @deprecated 4.6.0 Use `createMagicAuth` instead. This method will be removed in a future major version. * Creates a one-time Magic Auth code and emails it to the user. * * @param string $email The email address the one-time code will be sent to. From 053ff559a919487c29082f9bb1ea8705080729e1 Mon Sep 17 00:00:00 2001 From: Braden Keith Date: Fri, 28 Mar 2025 14:54:24 -0400 Subject: [PATCH 15/15] Update tests to expect Role Slug --- tests/WorkOS/UserManagementTest.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/WorkOS/UserManagementTest.php b/tests/WorkOS/UserManagementTest.php index 1b6ff72..9a2b74a 100644 --- a/tests/WorkOS/UserManagementTest.php +++ b/tests/WorkOS/UserManagementTest.php @@ -601,10 +601,10 @@ public function testCreatePasswordReset() { $path = "user_management/password_reset"; - $result = $this->passwordResetResponseFixture(); + $response = $this->passwordResetResponseFixture(); $params = [ - "email" => "someemail@test.com", + "email" => "someemail@test.com" ]; $this->mockRequest( @@ -613,7 +613,7 @@ public function testCreatePasswordReset() null, $params, true, - $result + $response ); $response = $this->userManagement->createPasswordReset( @@ -621,14 +621,14 @@ public function testCreatePasswordReset() ); $expected = $this->passwordResetFixture(); - - $this->assertSame($response->toArray(), $expected); + $this->assertSame($expected, $response->toArray()); } public function testSendPasswordResetEmail() { $path = "user_management/password_reset/send"; + // Mock the API request $responseCode = 200; $params = [ "email" => "test@test.com", @@ -647,6 +647,7 @@ public function testSendPasswordResetEmail() ); $response = $this->userManagement->sendPasswordResetEmail("test@test.com", "https://your-app.com/reset-password"); + // Test the functionality $this->assertSame(200, $responseCode); $this->assertSame($response, []); } @@ -1341,6 +1342,9 @@ private function organizationMembershipResponseFixture($status = "active") "id" => "om_01E4ZCR3C56J083X43JQXF3JK5", "user_id" => "user_01H7X1M4TZJN5N4HG4XXMA1234", "organization_id" => "org_01EHQMYV6MBK39QC5PZXHY59C3", + "role" => [ + "slug" => "admin", + ], "status" => $status, "created_at" => "2021-06-25T19:07:33.155Z", "updated_at" => "2021-06-25T19:07:33.155Z", @@ -1357,6 +1361,9 @@ private function organizationMembershipListResponseFixture() "id" => "om_01E4ZCR3C56J083X43JQXF3JK5", "user_id" => "user_01H7X1M4TZJN5N4HG4XXMA1234", "organization_id" => "org_01EHQMYV6MBK39QC5PZXHY59C3", + "role" => [ + "slug" => "admin", + ], "status" => "active", "created_at" => "2021-06-25T19:07:33.155Z", "updated_at" => "2021-06-25T19:07:33.155Z", @@ -1377,6 +1384,9 @@ private function organizationMembershipFixture() "id" => "om_01E4ZCR3C56J083X43JQXF3JK5", "userId" => "user_01H7X1M4TZJN5N4HG4XXMA1234", "organizationId" => "org_01EHQMYV6MBK39QC5PZXHY59C3", + "role" => [ + "slug" => "admin", + ], "status" => "active", "createdAt" => "2021-06-25T19:07:33.155Z", "updatedAt" => "2021-06-25T19:07:33.155Z",