From 0ea5204c63d5c564d266bc8871dee9bcdfe67024 Mon Sep 17 00:00:00 2001 From: Kevin J Gao <32936811+gaokevin1@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:13:33 -0800 Subject: [PATCH] added User management examples --- README.md | 199 ++++++++++++++++++++++++++-------- src/SDK/API.php | 20 ++-- src/SDK/Management/User.php | 208 +++++++++++++++++------------------- 3 files changed, 266 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index 03a24d9..b6b189c 100644 --- a/README.md +++ b/README.md @@ -97,52 +97,54 @@ $descopeSDK = new DescopeSDK([ Once you've configured your caching, you're ready to use the SDK. This SDK will easily allow you integrate Descope functionality with the following built-in functions: -## Password Authentication +## Authentication Methods -### Sign Up +### Passwords + +#### Sign Up ```php $response = $descopeSDK->auth->password->signUp("loginId", "password123"); print_r($response); ``` -### Sign In +#### Sign In ```php $response = $descopeSDK->auth->password->signIn("loginId", "password123"); print_r($response); ``` -### Send Reset Password +#### Send Reset Password ```php $response = $descopeSDK->auth->password->sendReset("loginId", "https://example.com/reset"); print_r($response); ``` -### Update Password +#### Update Password ```php $descopeSDK->auth->password->update("loginId", "newPassword123", "refreshToken"); ``` -### Replace Password +#### Replace Password ```php $response = $descopeSDK->auth->password->replace("loginId", "oldPassword123", "newPassword123"); print_r($response); ``` -### Get Password Policy +#### Get Password Policy ```php $response = $descopeSDK->auth->password->getPolicy(); print_r($response); ``` -## SSO Authentication +### SSO -### SSO Sign In +#### SSO Sign In ```php $response = $descopeSDK->auth->sso->signIn( @@ -157,106 +159,215 @@ $response = $descopeSDK->auth->sso->signIn( print_r($response); ``` -### Exchange Token +#### Exchange Token ```php $response = $descopeSDK->auth->sso->exchangeToken("code"); print_r($response); ``` -## Session Management +### Session Management + +1. `DescopeSDK->verify($sessionToken)` - will validate the session token and return either **TRUE** or **FALSE**, depending on if the JWT is valid and expired. +2. `DescopeSDK->refreshSession($refreshToken)` - will refresh your session and return a new session token, with the refresh token. +3. `DescopeSDK->verifyAndRefreshSession($sessionToken, $refreshToken)` - will validate the session token and return either **TRUE** or **FALSE**, and will refresh your session and return a new session token. +4. `DescopeSDK->logout($refreshToken)` - will invalidate the refresh token and log the user out of the current session. +5. `DescopeSDK->logoutAll($refreshToken)` - will invalidate all refresh tokens associated with a given project, thereby signing out of all sessions across multiple applications. +--- +6. `DescopeSDK->getClaims($sessionToken)` - will return all of the claims from the JWT in an array format. +7. `DescopeSDK->getUserDetails($refreshToken)` - will return all of the user information (email, phone, verification status, etc.) using a provided refresh token. -1. `DescopeSDK->verify($sessionToken)` - will validate the JWT signature and return either **TRUE** or **FALSE**, depending on if the JWT is valid and expired -2. `DescopeSDK->getClaims($sessionToken)` - will return all of the claims from the JWT in an array format -3. `DescopeSDK->getUserDetails($refreshToken)` - will return all of the user information (email, phone, verification status, etc.) using a provided refresh token +### User Management Functions -> **Note**: To use `verify()` and `getClaims()`, you will need to pass in your session token into the function argument. To use `getUserDetails()`, you will need to pass in your refresh token. +Each of these functions have code examples on how to use them. -## User Management Functions +> Some of these values may be incorrect for your environment, they exist purely as an example for your own implementation. -### Create User +#### Create User ```php $response = $descopeSDK->management->user->create( - "testuser1", - "user@example.com", - "1234567890", - "Test User", - "Test", - "Middle", - "User" + 'testuser1', // loginId + 'newemail@example.com', // email + '+1234567890', // phone + 'Updated User', // displayName + 'Updated', // givenName + 'Middle', // middleName + 'User', // familyName + null, // picture + null, // customAttributes + true, // verifiedEmail + true, // verifiedPhone + null, // inviteUrl + ['altUser1'], // additionalLoginIds + ['app123'], // ssoAppIds + null, // password + ['admin', 'editor'], // roleNames + [['tenantId' => 'tenant1']] // userTenants ); print_r($response); ``` -### Update User +#### Update User + +```php +$response = $descopeSDK->management->user->update( + 'testuser1', // loginId + 'updatedemail@example.com', // email + '+1234567890', // phone + 'Updated User', // displayName + 'Updated', // givenName + 'Middle', // middleName + 'User', // familyName + 'https://example.com/newpic.jpg', // picture + ['department' => 'HR'], // customAttributes + true, // verifiedEmail + true, // verifiedPhone + ['altUser1'], // additionalLoginIds + [''], // ssoAppIds +); +``` + +#### Invite User ```php -$descopeSDK->management->user->update( - "testuser1", - "newemail@example.com", - "0987654321", - "Updated User", - "Updated", - "Middle", - "User" +$response = $descopeSDK->management->user->invite( + 'newuser1', // loginId + 'invite@example.com', // email + '+1234567890', // phone + 'New User', // displayName + 'John', // givenName + 'Middle', // middleName + 'Doe', // familyName + 'https://example.com/profile.jpg', // picture + ['department' => 'Engineering'], // customAttributes + true, // verifiedEmail + true, // verifiedPhone + 'https://myapp.com/invite', // inviteUrl + true, // sendMail + true // sendSms ); +print_r($response); ``` -### Delete User +#### Batch Invite + +```php +$users = [ + new Descope\SDK\Management\UserObj( + 'batchuser1', // loginId + 'batch1@example.com', // email + null, // phone + 'Batch User One', // displayName + null, // givenName + null, // middleName + null, // familyName + ['admin'], // roleNames + [['tenantId' => 'tenant1']] // userTenants (can be an empty array if no tenant) + ), + + new Descope\SDK\Management\UserObj( + 'batchuser2', // loginId + 'batch2@example.com', // email + null, // phone + 'Batch User Two', // displayName + null, // givenName + null, // middleName + null, // familyName + ['viewer'], // roleNames + [['tenantId' => 'tenant2']] // userTenants (can be an empty array if no tenant) + ) +]; + +$response = $descopeSDK->management->user->inviteBatch( + $users, + 'https://myapp.com/batch-invite', // inviteUrl + true, // sendMail + true // sendSms +); + +print_r($response); +``` + +#### Delete User ```php $descopeSDK->management->user->delete("testuser1"); ``` -### Add Tenant +#### Search All Users + +```php +$response = $descopeSDK->management->user->searchAll( + "", // loginId + [], // tenantIds + ['admin', 'viewer'], // roleNames + 50, // limit + "", // text + 1, // page + false, // ssoOnly + false, // testUsersOnly + false, // withTestUser + null, // customAttributes + ['enabled'], // statuses + ['user@example.com'], // emails + ['+1234567890'], // phones + ['ssoApp123'], // ssoAppIds + [ // sort + ['field' => 'displayName', 'desc' => true] + ] +); +print_r($response); +``` + +#### Add Tenant ```php $response = $descopeSDK->management->user->addTenant("testuser1", "tenantId1"); print_r($response); ``` -### Remove Tenant +#### Remove Tenant ```php $response = $descopeSDK->management->user->removeTenant("testuser1", "tenantId1"); print_r($response); ``` -### Set Tenant Roles +#### Set Tenant Roles ```php $response = $descopeSDK->management->user->setTenantRoles("testuser1", "tenantId1", ["admin"]); print_r($response); ``` -### Add Tenant Roles +#### Add Tenant Roles ```php $response = $descopeSDK->management->user->addTenantRoles("testuser1", "tenantId1", ["user"]); print_r($response); ``` -### Remove Tenant Roles +#### Remove Tenant Roles ```php $response = $descopeSDK->management->user->removeTenantRoles("testuser1", "tenantId1", ["admin"]); print_r($response); ``` -### Set Temporary Password +#### Set Temporary Password ```php $descopeSDK->management->user->setTemporaryPassword("testuser1", new UserPassword(cleartext: "temporaryPassword123")); ``` -### Set Active Password +#### Set Active Password ```php $descopeSDK->management->user->setActivePassword("testuser1", new UserPassword(cleartext: "activePassword123")); ``` -### Set Password +#### Set Password ```php $descopeSDK->management->user->setPassword("testuser1", new UserPassword(cleartext: "password123"), true); @@ -288,10 +399,6 @@ The app should now be accessible at http://localhost:3000/ from your web browser This sample app showcases a Descope Flow using the WebJS SDK and PHP sessions to retain user information across multiple pages. It also showcases initializing the SDK and using it to validate the session token from formData sent from `login.php`. -## Other Code Samples - -1. [WordPress Plugin](https://github.com/descope-sample-apps/wordpress-plugin) - ## Feedback ### Contributing diff --git a/src/SDK/API.php b/src/SDK/API.php index 52b4fe6..8308070 100644 --- a/src/SDK/API.php +++ b/src/SDK/API.php @@ -58,15 +58,18 @@ public function __construct(string $projectId, ?string $managementKey) private function transformEmptyArraysToObjects($data) { if (is_array($data)) { - foreach ($data as &$value) { + // Check if the array is associative + $isAssociative = count(array_filter(array_keys($data), 'is_string')) > 0; + + // If the array is empty and associative, convert to stdClass object + if (empty($data) && $isAssociative) { + return new \stdClass(); + } + + foreach ($data as $key => &$value) { if (is_array($value)) { - // If the array is empty, ensure it's preserved as an empty array - if (empty($value)) { - $value = new \stdClass(); - } else { - // Recur for non-empty arrays - $value = $this->transformEmptyArraysToObjects($value); - } + // Recursively handle nested arrays + $value = $this->transformEmptyArraysToObjects($value); } } } @@ -96,6 +99,7 @@ public function doPost(string $uri, array $body, ?bool $useManagementKey = false $jsonBody = empty($body) ? '{}' : json_encode($body); try { + print_r($jsonBody); $response = $this->httpClient->post( $uri, [ diff --git a/src/SDK/Management/User.php b/src/SDK/Management/User.php index c495b0d..179efe8 100644 --- a/src/SDK/Management/User.php +++ b/src/SDK/Management/User.php @@ -147,39 +147,42 @@ public function create( ?array $additionalLoginIds = null, ?array $ssoAppIds = null, ?UserPassword $password = null, - ?array $roleNames = null, - ?array $userTenants = null + ?array $roleNames = [], + ?array $userTenants = [] ): array { - $roleNames = $roleNames ?? []; - $userTenants = $userTenants ?? []; - + $body = [ + 'loginId' => $loginId, + 'email' => $email, + 'phone' => $phone, + 'displayName' => $displayName, + 'givenName' => $givenName, + 'middleName' => $middleName, + 'familyName' => $familyName, + 'picture' => $picture, + 'customAttributes' => $customAttributes ? json_decode(json_encode($customAttributes), true) : null, + 'verifiedEmail' => $verifiedEmail, + 'verifiedPhone' => $verifiedPhone, + 'inviteUrl' => $inviteUrl, + 'additionalLoginIds' => $additionalLoginIds, + 'ssoAppIds' => $ssoAppIds, + 'password' => $password ? json_decode(json_encode($password), true) : null, + 'roleNames' => $roleNames, + 'userTenants' => $userTenants + ]; + + $body = array_filter($body, function ($value) { + if (is_array($value)) { + return !empty($value); + } + return $value !== null && $value !== ''; + }); + $response = $this->api->doPost( MgmtV1::$USER_CREATE_PATH, - $this->composeCreateBody( - $loginId, - $email, - $phone, - $displayName, - $givenName, - $middleName, - $familyName, - $roleNames, - $userTenants, - false, - false, - $picture, - $customAttributes, - $verifiedEmail, - $verifiedPhone, - $inviteUrl, - null, - null, - $additionalLoginIds, - $ssoAppIds, - $password - ), + $body, true ); + return $this->api->generateJwtResponse($response); } @@ -402,31 +405,37 @@ public function update( ?bool $verifiedPhone = null, ?array $additionalIdentifiers = null, ?array $ssoAppIds = null, - ?array $roleNames = null, - ?array $userTenants = null + ?array $roleNames = [], + ?array $userTenants = [] ): void { - $roleNames = $roleNames ?? []; - $userTenants = $userTenants ?? []; + $body = [ + 'loginId' => $loginId, + 'email' => $email, + 'phone' => $phone, + 'displayName' => $displayName, + 'givenName' => $givenName, + 'middleName' => $middleName, + 'familyName' => $familyName, + 'picture' => $picture, + 'customAttributes' => $customAttributes ? json_decode(json_encode($customAttributes), true) : null, + 'verifiedEmail' => $verifiedEmail, + 'verifiedPhone' => $verifiedPhone, + 'additionalIdentifiers' => $additionalIdentifiers, + 'ssoAppIds' => $ssoAppIds, + 'roleNames' => $roleNames, + 'userTenants' => $userTenants, + ]; - $this->api->doPost( + $body = array_filter($body, function ($value) { + if (is_array($value)) { + return !empty($value); + } + return $value !== null && $value !== ''; + }); + + $response = $this->api->doPost( MgmtV1::$USER_UPDATE_PATH, - $this->composeUpdateBody( - $loginId, - $email, - $phone, - $displayName, - $givenName, - $middleName, - $familyName, - $roleNames, - $userTenants, - $picture, - $customAttributes, - $verifiedEmail, - $verifiedPhone, - $additionalIdentifiers, - $ssoAppIds - ), + $body, true ); } @@ -528,66 +537,51 @@ public function loadByUserId(string $userId): array * @throws AuthException if search operation fails. */ public function searchAll( - ?array $tenantIds = null, - ?array $roleNames = null, - int $limit = 0, - int $page = 0, - bool $testUsersOnly = false, - bool $withTestUser = false, - ?array $customAttributes = null, - ?array $statuses = null, - ?array $emails = null, - ?array $phones = null, - ?array $ssoAppIds = null, - ?array $sort = null, - ?string $text = null - ): array { - // Initialize arrays if they are null - $tenantIds = $tenantIds ?? []; - $roleNames = $roleNames ?? []; - $statuses = $statuses ?? []; - $emails = $emails ?? []; - $phones = $phones ?? []; - $ssoAppIds = $ssoAppIds ?? []; - $sort = $sort ?? []; - $customAttributes = $customAttributes ?? (object)[]; - - if ($limit < 0) { - throw new AuthException( - 400, - 'ERROR_TYPE_INVALID_ARGUMENT', - 'limit must be non-negative' - ); - } - - if ($page < 0) { - throw new AuthException( - 400, - 'ERROR_TYPE_INVALID_ARGUMENT', - 'page must be non-negative' - ); - } - - // Prepare the request body + $loginId = null, + $tenantIds = null, + $roleNames = null, + $limit = 0, + $text = null, + $page = 0, + $ssoOnly = false, + $testUsersOnly = false, + $withTestUser = false, + $customAttributes = null, + $statuses = null, + $emails = null, + $phones = null, + $ssoAppIds = null, + $sort = null + ) { + // Prepare the request body ensuring PHP 7.x compatibility $body = [ - 'loginId' => '', - 'tenantIds' => $tenantIds, - 'roleNames' => $roleNames, - 'limit' => (string)$limit, - 'page' => (string)$page, + 'loginId' => $loginId ?? '', + 'tenantIds' => is_array($tenantIds) ? $tenantIds : [], + 'roleNames' => is_array($roleNames) ? $roleNames : [], + 'limit' => $limit > 0 ? $limit : 0, 'text' => $text ?? '', - 'ssoOnly' => '', - 'withTestUser' => $withTestUser ? true : false, - 'testUsersOnly' => $testUsersOnly ? true : false, - 'customAttributes' => $customAttributes, - 'statuses' => $statuses, - 'emails' => $emails, - 'phones' => $phones, - 'ssoAppIds' => $ssoAppIds, - 'sort' => $sort, - 'loginIds' => [], + 'page' => $page > 0 ? $page : 0, + 'ssoOnly' => (bool)$ssoOnly, + 'testUsersOnly' => (bool)$testUsersOnly, + 'withTestUser' => (bool)$withTestUser, + 'customAttributes' => $customAttributes !== null ? (array)$customAttributes : new \stdClass(), + 'statuses' => is_array($statuses) ? $statuses : [], + 'emails' => is_array($emails) ? $emails : [], + 'phones' => is_array($phones) ? $phones : [], + 'ssoAppIds' => is_array($ssoAppIds) ? $ssoAppIds : [], + 'sort' => is_array($sort) ? array_map(function ($item) { + return [ + 'field' => isset($item['field']) ? $item['field'] : '', + 'desc' => isset($item['desc']) ? (bool)$item['desc'] : false + ]; + }, $sort) : [], + 'loginIds' => [] ]; - + + $body = array_filter($body, function ($value) { + return $value !== null && $value !== ''; + }); + try { return $this->api->doPost( MgmtV1::$USERS_SEARCH_PATH,