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/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; diff --git a/lib/Resource/AuthenticationResponse.php b/lib/Resource/AuthenticationResponse.php index b7bc2bf..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 { @@ -14,10 +17,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) 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/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() { 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" 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/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. diff --git a/lib/Webhook.php b/lib/Webhook.php index 60715c9..4d5d517 100644 --- a/lib/Webhook.php +++ b/lib/Webhook.php @@ -14,13 +14,13 @@ class Webhook /** * Initializes an Event object from a JSON payload * - * @return string + * @return string|Resource\Webhook */ 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,44 +31,42 @@ 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(); $expectedSignature = $this->computeSignature($timestamp, $payload, $secret); 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; @@ -77,13 +75,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; 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",