diff --git a/sample/callback.php b/sample/callback.php
index 63b0580..7e1da49 100644
--- a/sample/callback.php
+++ b/sample/callback.php
@@ -17,21 +17,29 @@
echo "Descope Project ID not present. Please check .env file.";
exit(1);
}
-
+
$descopeSDK = new DescopeSDK([
'projectId' => $_ENV['DESCOPE_PROJECT_ID']
]);
- if (isset($_POST["sessionToken"]) && $descopeSDK->verify($_POST["sessionToken"])) {
- $_SESSION["user"] = json_decode($_POST["userDetails"], true);
- $_SESSION["sessionToken"] = $_POST["sessionToken"];
-
- session_write_close();
-
- // Redirect to dashboard
- header('Location: dashboard.php');
- exit();
+ if (isset($_POST["sessionToken"])) {
+ if ($descopeSDK->verify($_POST["sessionToken"])) {
+ $_SESSION["user"] = json_decode($_POST["userDetails"], true);
+ $_SESSION["sessionToken"] = $_POST["sessionToken"];
+ session_write_close();
+
+ // Redirect to dashboard
+ header('Location: dashboard.php');
+ exit();
+ } else {
+ error_log("Session token verification failed.");
+ $descopeSDK->logout();
+ // Redirect to login page
+ header('Location: login.php');
+ exit();
+ }
} else {
+ error_log("Session token is not set in POST request.");
// Redirect to login page
header('Location: login.php');
exit();
diff --git a/sample/dashboard.php b/sample/dashboard.php
index a115f11..527189e 100644
--- a/sample/dashboard.php
+++ b/sample/dashboard.php
@@ -1,22 +1,10 @@
load();
- if (!isset($_ENV['DESCOPE_PROJECT_ID'])) {
- echo "Descope Project ID not present. Please check .env file.";
- exit(1);
- }
+session_start();
- $descopeSDK = new DescopeSDK([
- 'projectId' => $_ENV['DESCOPE_PROJECT_ID']
- ]);
+$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
+$dotenv->load();
if (!isset($_SESSION["user"])) {
session_destroy();
diff --git a/sample/login.php b/sample/login.php
index 3088f2c..8e37477 100644
--- a/sample/login.php
+++ b/sample/login.php
@@ -37,16 +37,32 @@ function sendFormData(sessionToken, userDetails) {
return user;
}
+ async function handleLogout() {
+
+ }
+
async function handleLogin() {
try {
- // Wait for refresh to ensure token validity
+ console.log("Attempting to refresh the session...");
await sdk.refresh();
const sessionToken = sdk.getSessionToken();
+ console.log("Session token obtained:", sessionToken);
+
+ if (!sessionToken) {
+ console.log("Session token is missing after refresh. Redirecting to login.");
+ window.location.href = 'login.php'; // Redirect to login if session token is invalid
+ return;
+ }
+
const user = await getUserDetails();
+ console.log("User details obtained:", user);
+
sendFormData(sessionToken, user.data);
} catch (error) {
console.log("Error during login:", error);
+ sdk.logout();
+ window.location.href = 'login.php'; // Redirect to login on error
}
}
@@ -57,15 +73,18 @@ function sendFormData(sessionToken, userDetails) {
console.log("Valid refresh token found. Logging in...");
handleLogin();
} else {
+ sdk.logout();
+ console.log("No valid refresh token. Displaying login form.");
const container = document.getElementById("container")
container.innerHTML = '';
const wcElement = document.getElementsByTagName('descope-wc')[0];
const onSuccess = async (e) => {
+ console.log("Login successful, handling login.");
await handleLogin(); // Wait for login and session details
}
- const onError = (err) => console.log(err);
+ const onError = (err) => console.log("Login error:", err);
if (wcElement) {
wcElement.addEventListener('success', onSuccess);
diff --git a/src/SDK/API.php b/src/SDK/API.php
index 880efcc..abd435c 100644
--- a/src/SDK/API.php
+++ b/src/SDK/API.php
@@ -5,6 +5,7 @@
namespace Descope\SDK;
use GuzzleHttp\Client;
+use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Descope\SDK\Exception\AuthException;
use Descope\SDK\EndpointsV1;
@@ -16,22 +17,31 @@ class API
private $projectId;
private $managementKey;
- const SESSION_TOKEN_NAME = 'sessionToken';
- const REFRESH_SESSION_TOKEN_NAME = 'refreshSessionToken';
- const COOKIE_DATA_NAME = 'cookieData';
-
- const SESSION_COOKIE_NAME = "DS";
- const REFRESH_SESSION_COOKIE_NAME = "DSR";
- const REDIRECT_LOCATION_COOKIE_NAME = "Location";
-
/**
* Constructor for API class.
*
- * @param string $managementKey Management key for authentication.
+ * @param string $projectId
+ * @param string|null $managementKey Management key for authentication.
*/
public function __construct(string $projectId, ?string $managementKey)
{
$this->httpClient = new Client();
+
+ if (!empty($_ENV['DESCOPE_LOG_PATH'])) {
+ $log = new Logger('descope_guzzle_log');
+ $log->pushHandler(new StreamHandler($_ENV['DESCOPE_LOG_PATH'], Logger::DEBUG));
+ $stack = HandlerStack::create();
+ $stack->push(
+ Middleware::log(
+ $log,
+ new MessageFormatter(MessageFormatter::DEBUG)
+ )
+ );
+ $this->httpClient = new Client(['handler' => $stack]);
+ } else {
+ $this->httpClient = new Client();
+ }
+
$this->projectId = $projectId;
$this->managementKey = $managementKey ?? '';
}
@@ -42,7 +52,7 @@ public function __construct(string $projectId, ?string $managementKey)
* This function ensures that empty arrays in the input data are
* converted to empty objects (stdClass) before being JSON encoded.
*
- * @param mixed $data The data to transform, which can be an array or any other type.
+ * @param mixed $data The data to transform, which can be an array or any other type.
* @return mixed The transformed data with empty arrays replaced by empty objects.
*/
private function transformEmptyArraysToObjects($data)
@@ -50,9 +60,11 @@ private function transformEmptyArraysToObjects($data)
if (is_array($data)) {
foreach ($data as &$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);
}
}
@@ -64,11 +76,11 @@ private function transformEmptyArraysToObjects($data)
/**
* Requests JwtResponse from Descope APIs with the given body and auth token.
*
- * @param string $uri URI endpoint.
- * @param array $body Request body.
- * @param bool $useManagementKey Whether to use the management key for authentication.
+ * @param string $uri URI endpoint.
+ * @param array $body Request body.
+ * @param bool $useManagementKey Whether to use the management key for authentication.
* @return array JWT response array.
- * @throws AuthException If the request fails.
+ * @throws AuthException|GuzzleException|\JsonException If the request fails.
*/
public function doPost(string $uri, array $body, ?bool $useManagementKey = false, ?string $refreshToken = null): array
{
@@ -87,8 +99,8 @@ public function doPost(string $uri, array $body, ?bool $useManagementKey = false
$response = $this->httpClient->post(
$uri,
[
- 'headers' => $this->getHeaders($authToken),
- 'body' => $jsonBody,
+ 'headers' => $this->getHeaders($authToken),
+ 'body' => $jsonBody,
]
);
@@ -96,8 +108,13 @@ public function doPost(string $uri, array $body, ?bool $useManagementKey = false
if (!is_object($response) || !method_exists($response, 'getBody') || !method_exists($response, 'getHeader')) {
throw new AuthException(500, 'internal error', 'Invalid response from API');
}
- $resp = json_decode($response->getBody()->getContents(), true);
- return $resp;
+
+ // Read Body
+ $body = $response->getBody();
+ $body->rewind();
+ $contents = $body->getContents() ?? [];
+
+ return json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -113,10 +130,10 @@ public function doPost(string $uri, array $body, ?bool $useManagementKey = false
/**
* Sends a GET request to the specified URI with an optional auth token.
*
- * @param string $uri URI endpoint.
- * @param bool $useManagementKey Whether to use the management key for authentication.
+ * @param string $uri URI endpoint.
+ * @param bool $useManagementKey Whether to use the management key for authentication.
* @return array JWT response array.
- * @throws AuthException If the request fails.
+ * @throws AuthException|GuzzleException|\JsonException If the request fails.
*/
public function doGet(string $uri, bool $useManagementKey, ?string $refreshToken = null): array
{
@@ -140,9 +157,54 @@ public function doGet(string $uri, bool $useManagementKey, ?string $refreshToken
if (!is_object($response) || !method_exists($response, 'getBody') || !method_exists($response, 'getHeader')) {
throw new AuthException(500, 'internal error', 'Invalid response from API');
}
- $resp = json_decode($response->getBody()->getContents(), true);
-
- return $resp;
+
+ // Read Body
+ $body = $response->getBody();
+ $body->rewind();
+ $contents = $body->getContents() ?? [];
+
+ return json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
+ } catch (RequestException $e) {
+ $statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
+ $responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
+ echo "Error: HTTP Status Code: $statusCode, Response: $responseBody";
+ return [
+ 'statusCode' => $statusCode,
+ 'response' => $responseBody,
+ ];
+ }
+ }
+
+ /**
+ * Sends a DELETE request to the specified URI with an auth token.
+ *
+ * @param string $uri URI endpoint.
+ * @return array JWT response array.
+ * @throws AuthException|GuzzleException|\JsonException If the request fails.
+ */
+ public function doDelete(string $uri): array
+ {
+ $authToken = $this->getAuthToken(true);
+
+ try {
+ $response = $this->httpClient->delete(
+ $uri,
+ [
+ 'headers' => $this->getHeaders($authToken),
+ ]
+ );
+
+ // Ensure the response is an object with getBody method
+ if (!is_object($response) || !method_exists($response, 'getBody') || !method_exists($response, 'getHeader')) {
+ throw new AuthException(500, 'internal error', 'Invalid response from API');
+ }
+
+ // Read Body
+ $body = $response->getBody();
+ $body->rewind();
+ $contents = $body->getContents() ?? [];
+
+ return json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -157,9 +219,9 @@ public function doGet(string $uri, bool $useManagementKey, ?string $refreshToken
/**
* Generates a JWT response array with the given parameters.
*
- * @param array $resp Response data.
- * @param string|null $refreshToken Refresh token.
- * @param string|null $audience Audience.
+ * @param array $responseBody
+ * @param string|null $refreshToken Refresh token.
+ * @param string|null $audience Audience.
* @return array JWT response array.
*/
public function generateJwtResponse(array $responseBody, ?string $refreshToken = null, ?string $audience = null): array
@@ -175,7 +237,7 @@ public function generateJwtResponse(array $responseBody, ?string $refreshToken =
/**
* Generates headers for the HTTP request.
*
- * @param string|null $authToken Authentication token.
+ * @param string|null $authToken Authentication token.
* @return array Headers array.
*/
private function getHeaders(string $authToken): array
@@ -193,7 +255,7 @@ private function getHeaders(string $authToken): array
/**
* Constructs the auth token based on whether the management key is used.
*
- * @param bool $useManagementKey Whether to use the management key for authentication.
+ * @param bool $useManagementKey Whether to use the management key for authentication.
* @return string The constructed auth token.
*/
private function getAuthToken(bool $useManagementKey, ?string $refreshToken = null): string
@@ -209,27 +271,39 @@ private function getAuthToken(bool $useManagementKey, ?string $refreshToken = nu
return $this->projectId;
}
+ /**
+ * Generates authentication information from the response body.
+ *
+ * This method processes the response body to extract JWTs, session data,
+ * and cookie settings, and adjusts properties based on the token type.
+ *
+ * @param array $responseBody The API response body containing JWTs and user data.
+ * @param string|null $refreshToken Optional refresh token.
+ * @param bool $userJwt Indicates if user-related JWT information should be processed.
+ * @param string|null $audience Optional audience identifier.
+ * @return array The structured JWT response array containing session and user data.
+ */
private function generateAuthInfo(array $responseBody, ?string $refreshToken, bool $userJwt, ?string $audience): array
{
$jwtResponse = [];
$stJwt = $responseBody['sessionJwt'] ?? '';
if ($stJwt) {
- $jwtResponse[self::SESSION_TOKEN_NAME] = $stJwt;
+ $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME] = $stJwt;
}
$rtJwt = $responseBody['refreshJwt'] ?? '';
if ($refreshToken) {
- $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME] = $refreshToken;
+ $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME] = $refreshToken;
} elseif ($rtJwt) {
- $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME] = $rtJwt;
+ $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME] = $rtJwt;
}
$jwtResponse = $this->adjustProperties($jwtResponse, $userJwt);
if ($userJwt) {
- $jwtResponse[self::COOKIE_DATA_NAME] = [
+ $jwtResponse[EndpointsV1::$COOKIE_DATA_NAME] = [
'exp' => $responseBody['cookieExpiration'] ?? 0,
'maxAge' => $responseBody['cookieMaxAge'] ?? 0,
'domain' => $responseBody['cookieDomain'] ?? '',
@@ -240,31 +314,41 @@ private function generateAuthInfo(array $responseBody, ?string $refreshToken, bo
return $jwtResponse;
}
+ /**
+ * Adjusts properties of the JWT response array.
+ *
+ * This method sets permissions, roles, and tenant data from the JWT
+ * and processes the issuer and subject values to extract project and user IDs.
+ *
+ * @param array $jwtResponse The JWT response array to adjust.
+ * @param bool $userJwt Indicates if user-related JWT information should be processed.
+ * @return array The adjusted JWT response array with updated properties.
+ */
private function adjustProperties(array $jwtResponse, bool $userJwt): array
{
- if (isset($jwtResponse[self::SESSION_TOKEN_NAME])) {
- $jwtResponse['permissions'] = $jwtResponse[self::SESSION_TOKEN_NAME]['permissions'] ?? [];
- $jwtResponse['roles'] = $jwtResponse[self::SESSION_TOKEN_NAME]['roles'] ?? [];
- $jwtResponse['tenants'] = $jwtResponse[self::SESSION_TOKEN_NAME]['tenants'] ?? [];
- } elseif (isset($jwtResponse[self::REFRESH_SESSION_TOKEN_NAME])) {
- $jwtResponse['permissions'] = $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME]['permissions'] ?? [];
- $jwtResponse['roles'] = $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME]['roles'] ?? [];
- $jwtResponse['tenants'] = $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME]['tenants'] ?? [];
+ if (isset($jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME])) {
+ $jwtResponse['permissions'] = $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME]['permissions'] ?? [];
+ $jwtResponse['roles'] = $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME]['roles'] ?? [];
+ $jwtResponse['tenants'] = $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME]['tenants'] ?? [];
+ } elseif (isset($jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME])) {
+ $jwtResponse['permissions'] = $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME]['permissions'] ?? [];
+ $jwtResponse['roles'] = $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME]['roles'] ?? [];
+ $jwtResponse['tenants'] = $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME]['tenants'] ?? [];
} else {
$jwtResponse['permissions'] = $jwtResponse['permissions'] ?? [];
$jwtResponse['roles'] = $jwtResponse['roles'] ?? [];
$jwtResponse['tenants'] = $jwtResponse['tenants'] ?? [];
}
- $issuer = $jwtResponse[self::SESSION_TOKEN_NAME]['iss'] ??
- $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME]['iss'] ??
+ $issuer = $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME]['iss'] ??
+ $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME]['iss'] ??
$jwtResponse['iss'] ?? '';
$issuerParts = explode("/", $issuer);
$jwtResponse['projectId'] = end($issuerParts);
- $sub = $jwtResponse[self::SESSION_TOKEN_NAME]['sub'] ??
- $jwtResponse[self::REFRESH_SESSION_TOKEN_NAME]['sub'] ??
+ $sub = $jwtResponse[EndpointsV1::$SESSION_TOKEN_NAME]['sub'] ??
+ $jwtResponse[EndpointsV1::$REFRESH_TOKEN_NAME]['sub'] ??
$jwtResponse['sub'] ?? '';
if ($userJwt) {
diff --git a/src/SDK/Auth/Password.php b/src/SDK/Auth/Password.php
index 03ba143..a6bbea8 100644
--- a/src/SDK/Auth/Password.php
+++ b/src/SDK/Auth/Password.php
@@ -10,6 +10,9 @@
class Password
{
+ /**
+ * @var API The API object for making authenticated requests.
+ */
private $api;
/**
@@ -29,7 +32,7 @@ public function __construct(API $api)
* @param string $password Password for the new user.
* @param array|null $user Optional user details.
* @return array JWT response array.
- * @throws AuthException If login ID or password is empty.
+ * @throws AuthException
*/
public function signUp(string $loginId, string $password, ?array $user = null, ?array $loginOptions = null): array
{
@@ -54,7 +57,7 @@ public function signUp(string $loginId, string $password, ?array $user = null, ?
* @param string $loginId Login ID of the user.
* @param string $password Password of the user.
* @return array JWT response array.
- * @throws AuthException If login ID or password is empty.
+ * @throws AuthException
*/
public function signIn(string $loginId, string $password): array
{
@@ -78,7 +81,7 @@ public function signIn(string $loginId, string $password): array
* @param string|null $redirectUrl Optional redirect URL.
* @param array|null $templateOptions Optional template options.
* @return array Response array.
- * @throws AuthException If login ID is empty.
+ * @throws AuthException
*/
public function sendReset(string $loginId, ?string $redirectUrl = null, ?array $templateOptions = null): array
{
@@ -104,7 +107,7 @@ public function sendReset(string $loginId, ?string $redirectUrl = null, ?array $
*
* @param string $loginId Login ID of the user.
* @param string $newPassword New password for the user.
- * @throws AuthException If login ID, new password, or refresh token is empty.
+ * @throws AuthException
*/
public function update(string $loginId, string $newPassword, string $refreshToken): void
{
@@ -131,7 +134,7 @@ public function update(string $loginId, string $newPassword, string $refreshToke
* @param string $oldPassword Old password of the user.
* @param string $newPassword New password for the user.
* @return array JWT response array.
- * @throws AuthException If login ID, old password, or new password is empty.
+ * @throws AuthException
*/
public function replace(string $loginId, string $oldPassword, string $newPassword): array
{
diff --git a/src/SDK/Auth/SSO.php b/src/SDK/Auth/SSO.php
index 71d4b46..f71a803 100644
--- a/src/SDK/Auth/SSO.php
+++ b/src/SDK/Auth/SSO.php
@@ -10,6 +10,9 @@
class SSO
{
+ /**
+ * @var API The API object for making authenticated requests.
+ */
private $api;
/**
@@ -33,7 +36,7 @@ public function __construct(API $api)
* @param array $customClaims Custom claims to include in the token.
* @param string|null $ssoAppId SSO application identifier.
* @return array Response array.
- * @throws AuthException If tenant or redirect URL validation fails.
+ * @throws AuthException
*/
public function signIn(?string $tenant = null, ?string $redirectUrl = null, ?string $prompt = null, bool $stepup = false, bool $mfa = false, array $customClaims = [], ?string $ssoAppId = null): array
{
@@ -58,7 +61,7 @@ public function signIn(?string $tenant = null, ?string $redirectUrl = null, ?str
/**
* Exchanges SSO code for authentication.
*
- * @param string|null $code The exchange code.
+ * @param string|null $code The exchange code.
* @return array Response array.
*/
public function exchangeToken(?string $code = null): array
@@ -74,9 +77,9 @@ public function exchangeToken(?string $code = null): array
/**
* Composes the SSO sign-in URL.
*
- * @param string|null $tenant Tenant identifier.
- * @param string|null $redirectUrl Redirect URL.
- * @param string|null $prompt Prompt parameter.
+ * @param string|null $tenant Tenant identifier.
+ * @param string|null $redirectUrl Redirect URL.
+ * @param string|null $prompt Prompt parameter.
* @return string Composed URL.
*/
private function composeSignInUrl(?string $tenant, ?string $redirectUrl, ?string $prompt): string
@@ -101,8 +104,8 @@ private function composeSignInUrl(?string $tenant, ?string $redirectUrl, ?string
/**
* Validates the tenant parameter.
*
- * @param string|null $tenant The tenant identifier.
- * @throws AuthException If the tenant is invalid.
+ * @param string|null $tenant The tenant identifier.
+ * @throws AuthException
*/
private function validateTenant(?string $tenant): void
{
@@ -114,8 +117,8 @@ private function validateTenant(?string $tenant): void
/**
* Validates the redirect URL parameter.
*
- * @param string|null $redirectUrl The redirect URL.
- * @throws AuthException If the redirect URL is invalid.
+ * @param string|null $redirectUrl The redirect URL.
+ * @throws AuthException
*/
private function validateRedirectUrl(?string $redirectUrl): void
{
diff --git a/src/SDK/DescopeSDK.php b/src/SDK/DescopeSDK.php
index a16bc9f..67ac5a2 100644
--- a/src/SDK/DescopeSDK.php
+++ b/src/SDK/DescopeSDK.php
@@ -25,7 +25,7 @@ class DescopeSDK
/**
* Constructor for DescopeSDK class.
*
- * @param SDKConfig $config Base configuration options for the SDK.
+ * @param array $config Base configuration options for the SDK.
*/
public function __construct(array $config)
{
@@ -49,53 +49,97 @@ public function __construct(array $config)
$this->sso = new SSO($this->api);
}
- /**
- * Verify if the JWT is valid and not expired.
- */
- public function verify($sessionToken)
+ /**
+ * Verify if the JWT is valid and not expired.
+ *
+ * @param string|null $sessionToken The session token to verify.
+ * @return bool Verification result.
+ * @throws AuthException
+ */
+ public function verify($sessionToken = null)
{
+ $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME_NAME] ?? null;
+
+ if (!$sessionToken) {
+ throw new \InvalidArgumentException('Session token is required.');
+ }
+
$verifier = new Verifier($this->config);
return $verifier->verify($sessionToken);
}
/**
- * Refresh session token with refresh token.
+ * Refresh session token using the refresh token.
+ *
+ * @param string|null $refreshToken The refresh token to use.
+ * @return array The new session information.
+ * @throws AuthException
*/
- public function refreshSession($refreshToken)
+ public function refreshSession($refreshToken = null)
{
+ $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+
+ if (!$refreshToken) {
+ throw new \InvalidArgumentException('Refresh token is required.');
+ }
+
$verifier = new Verifier($this->config);
return $verifier->refreshSession($refreshToken);
}
/**
- * Verify if the JWT is valid and not expired.
+ * Verify and refresh the session using session and refresh tokens.
+ *
+ * @param string|null $sessionToken The session token.
+ * @param string|null $refreshToken The refresh token.
+ * @return array The refreshed session information.
+ * @throws AuthException
*/
- public function verifyAndRefreshSession($sessionToken, $refreshToken)
+ public function verifyAndRefreshSession($sessionToken = null, $refreshToken = null)
{
+ $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME] ?? null;
+ $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+
+ if (!$sessionToken || !$refreshToken) {
+ throw new \InvalidArgumentException('Session token and refresh token are required.');
+ }
+
$verifier = new Verifier($this->config);
return $verifier->verifyAndRefreshSession($sessionToken, $refreshToken);
}
/**
- * Returns the JWT claims, if the JWT is valid.
+ * Get the JWT claims if the token is valid.
+ *
+ * @param string|null $token The token to extract claims from.
+ * @return array The JWT claims.
+ * @throws AuthException
*/
- public function getClaims($token)
+ public function getClaims($token = null)
{
+ $token = $token ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME] ?? null;
+
+ if (!$token) {
+ throw new \InvalidArgumentException('Token is required.');
+ }
+
$extractor = new Extractor($this->config);
return $extractor->getClaims($token);
}
/**
- * Returns the user details, using the refresh token.
+ * Retrieve user details using the refresh token.
*
- * @param string $refreshToken The refresh token of the user.
- * @return void
- * @throws AuthException if the logout operation fails.
+ * @param string|null $refreshToken The refresh token of the user.
+ * @return array The user details.
+ * @throws AuthException
*/
- public function getUserDetails(string $refreshToken)
+ public function getUserDetails(string $refreshToken = null)
{
- if (empty(EndpointsV1::$ME_PATH)) {
- throw new \RuntimeException('ME_PATH is not initialized.');
+ $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+
+ if (!$refreshToken) {
+ throw new \InvalidArgumentException('Refresh token is required.');
}
return $this->api->doGet(
@@ -106,16 +150,18 @@ public function getUserDetails(string $refreshToken)
}
/**
- * Logout a user from all devices.
+ * Logout a user using the refresh token.
*
- * @param string $refreshToken The refresh token of the user.
+ * @param string|null $refreshToken The refresh token of the user.
* @return void
- * @throws AuthException if the logout operation fails.
+ * @throws AuthException
*/
- public function logout(string $refreshToken): void
+ public function logout(string $refreshToken = null): void
{
- if (empty(EndpointsV1::$LOGOUT_PATH)) {
- throw new \RuntimeException('ME_PATH is not initialized.');
+ $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+
+ if (!$refreshToken) {
+ throw new \InvalidArgumentException('Refresh token is required.');
}
$this->api->doPost(
@@ -127,14 +173,20 @@ public function logout(string $refreshToken): void
}
/**
- * Logout a user from all devices.
+ * Logout a user from all devices using the refresh token.
*
- * @param string $refreshToken The refresh token of the user.
+ * @param string|null $refreshToken The refresh token of the user.
* @return void
- * @throws AuthException if the logout operation fails.
+ * @throws AuthException
*/
- public function logoutAll(string $refreshToken): void
+ public function logoutAll(string $refreshToken = null): void
{
+ $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+
+ if (!$refreshToken) {
+ throw new \InvalidArgumentException('Refresh token is required.');
+ }
+
$this->api->doPost(
EndpointsV1::LOGOUT_ALL_PATH,
[],
@@ -145,6 +197,8 @@ public function logoutAll(string $refreshToken): void
/**
* Get the Password component.
+ *
+ * @return Password The Password instance.
*/
public function password(): Password
{
@@ -153,6 +207,8 @@ public function password(): Password
/**
* Get the SSO component.
+ *
+ * @return SSO The SSO instance.
*/
public function sso(): SSO
{
@@ -161,6 +217,8 @@ public function sso(): SSO
/**
* Get the Management component.
+ *
+ * @return Management The Management instance.
*/
public function management(): Management
{
diff --git a/src/SDK/EndpointsV1.php b/src/SDK/EndpointsV1.php
index ea9d606..6549681 100644
--- a/src/SDK/EndpointsV1.php
+++ b/src/SDK/EndpointsV1.php
@@ -9,11 +9,31 @@
const DEFAULT_DOMAIN = "descope.com";
const DEFAULT_TIMEOUT_SECONDS = 60;
+const SESSION_COOKIE = "DS";
+const REFRESH_COOKIE = "DSR";
+
+const SESSION_TOKEN = "sessionToken";
+const REFRESH_TOKEN = "refreshSessionToken";
+
+const COOKIE_DATA = "cookieData";
+
+const REDIRECT_LOCATION_NAME = "Location";
+
const PHONE_REGEX = '/^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?){0,}((?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$/';
class EndpointsV1
{
- private static $baseUrl = DEFAULT_URL_PREFIX . '.' . DEFAULT_DOMAIN;
+ public static $baseUrl = DEFAULT_URL_PREFIX . '.' . DEFAULT_DOMAIN;
+
+ public static $SESSION_COOKIE_NAME = SESSION_COOKIE;
+ public static $REFRESH_COOKIE_NAME = REFRESH_COOKIE;
+
+ public static $SESSION_TOKEN_NAME = SESSION_TOKEN;
+ public static $REFRESH_TOKEN_NAME = REFRESH_TOKEN;
+
+ public static $COOKIE_DATA_NAME = COOKIE_DATA;
+
+ public static $REDIRECT_LOCATION_COOKIE_NAME = REDIRECT_LOCATION_NAME;
public static $REFRESH_TOKEN_PATH;
public static $SELECT_TENANT_PATH;
@@ -62,6 +82,13 @@ class EndpointsV1
public static $REPLACE_PASSWORD_PATH;
public static $PASSWORD_POLICY_PATH;
+ /**
+ * Set the base URL for API endpoints using the project ID.
+ * The project ID is used to determine the correct region-specific base URL.
+ *
+ * @param string $projectId The project ID for the Descope project.
+ * @return void
+ */
public static function setBaseUrl(string $projectId): void
{
$region = self::extractRegionFromProjectId($projectId);
@@ -75,6 +102,12 @@ public static function setBaseUrl(string $projectId): void
self::updatePaths();
}
+ /**
+ * Extracts the region information from the given project ID.
+ *
+ * @param string $projectId The project ID for the Descope project.
+ * @return string|null Returns the region if extracted, otherwise null.
+ */
public static function extractRegionFromProjectId(string $projectId): ?string
{
if (strlen($projectId) >= 32) {
@@ -84,6 +117,11 @@ public static function extractRegionFromProjectId(string $projectId): ?string
return null;
}
+ /**
+ * Updates the API endpoint paths to reflect the currently set base URL.
+ *
+ * @return void
+ */
public static function updatePaths(): void
{
self::$REFRESH_TOKEN_PATH = self::$baseUrl . "/v1/auth/refresh";
@@ -139,6 +177,13 @@ class EndpointsV2
{
private static $baseUrl;
+ /**
+ * Set the base URL for API endpoints using the project ID.
+ * The project ID is used to determine the correct region-specific base URL.
+ *
+ * @param string $projectId The project ID for the Descope project.
+ * @return void
+ */
public static function setBaseUrl(string $projectId): void
{
$region = EndpointsV1::extractRegionFromProjectId($projectId);
@@ -151,6 +196,11 @@ public static function setBaseUrl(string $projectId): void
self::$baseUrl = "$urlPrefix." . DEFAULT_DOMAIN;
}
+ /**
+ * Fetches the public JWK set path with the proper base url.
+ *
+ * @return string Returns the public key path of the JWK set.
+ */
public static function getPublicKeyPath(): string
{
return self::$baseUrl . "/v2/keys";
diff --git a/src/SDK/Management/AssociatedTenant.php b/src/SDK/Management/AssociatedTenant.php
index 25786b1..a4a3b93 100644
--- a/src/SDK/Management/AssociatedTenant.php
+++ b/src/SDK/Management/AssociatedTenant.php
@@ -5,39 +5,43 @@
class AssociatedTenant
{
/**
- * Represents a tenant association for a User or Access Key. The tenant will be used to determine permissions and roles for the entity.
+ * Represents a tenant association for a User or Access Key.
+ * The tenant will be used to determine permissions and roles for the entity.
*
- * @var string The Tenant ID
+ * @var string The Tenant ID.
*/
public $tenantId;
/**
- * Represents the role names for a user in the Tenant
+ * Represents the role names for a user in the Tenant.
*
- * @var array The Role Names
+ * @var array The Role Names.
*/
public $roleNames = [];
/**
- * Represents the role IDs for a user in the Tenant
+ * Constructor for the AssociatedTenant class.
*
- * @var array The Role IDs
+ * @param string $tenantId The Tenant ID.
+ * @param array $roleNames The role names for the user in the tenant.
+ * @param array $roleIds The role IDs for the user in the tenant.
*/
- public $roleIds = [];
-
- public function __construct($tenantId, $roleNames = [], $roleIds = [])
+ public function __construct(string $tenantId, array $roleNames = [])
{
$this->tenantId = $tenantId;
$this->roleNames = $roleNames;
- $this->roleIds = $roleIds;
}
+ /**
+ * Converts the AssociatedTenant object to an associative array.
+ *
+ * @return array The associative array representation of the tenant and roles.
+ */
public function toArray(): array
{
return [
'tenantId' => $this->tenantId,
- 'roleNames' => $this->roleNames,
- 'roleIds' => $this->roleIds,
+ 'roleNames' => $this->roleNames
];
}
}
diff --git a/src/SDK/Management/Management.php b/src/SDK/Management/Management.php
index 070f454..b536edd 100644
--- a/src/SDK/Management/Management.php
+++ b/src/SDK/Management/Management.php
@@ -4,9 +4,19 @@
use Descope\SDK\API;
+/**
+ * Class Management
+ *
+ * Represents the management functionality for Descope, providing access to
+ * user management capabilities.
+ */
class Management
{
+ /**
+ * @var User The User management component.
+ */
public User $user;
+ public Audit $audit;
/**
* Constructor for Management class.
@@ -16,13 +26,26 @@ class Management
public function __construct(API $auth)
{
$this->user = new User($auth);
+ $this->audit = new Audit($auth);
}
/**
* Get the User Management component.
+ *
+ * @return User The User management instance.
*/
public function user(): User
{
return $this->user;
}
+
+ /**
+ * Get the Audit Management component.
+ *
+ * @return Audit The Audit management instance.
+ */
+ public function audit(): Audit
+ {
+ return $this->audit;
+ }
}
diff --git a/src/SDK/Management/MgmtV1.php b/src/SDK/Management/MgmtV1.php
index d249db2..1033da1 100644
--- a/src/SDK/Management/MgmtV1.php
+++ b/src/SDK/Management/MgmtV1.php
@@ -10,8 +10,100 @@
class MgmtV1
{
- private static $baseUrl = DEFAULT_URL_PREFIX . '.' . DEFAULT_DOMAIN;
+ /**
+ * Base URL for the Descope API.
+ *
+ * @var string
+ */
+ public static string $baseUrl = DEFAULT_URL_PREFIX . '.' . DEFAULT_DOMAIN;
+ // Paths for various management operations
+ public static string $TEMPLATE_EXPORT_PATH;
+ public static string $TEMPLATE_IMPORT_PATH;
+ public static string $FLOW_EXPORT_PATH;
+ public static string $FLOW_DELETE_PATH;
+ public static string $FLOW_LIST_PATH;
+ public static string $ROLE_SEARCH_PATH;
+ public static string $ROLE_LOAD_ALL_PATH;
+ public static string $ROLE_DELETE_PATH;
+ public static string $ROLE_UPDATE_PATH;
+ public static string $ROLE_CREATE_PATH;
+ public static string $PERMISSION_LOAD_ALL_PATH;
+ public static string $PERMISSION_DELETE_PATH;
+ public static string $PERMISSION_UPDATE_PATH;
+ public static string $PERMISSION_CREATE_PATH;
+ public static string $IMPERSONATE_PATH;
+ public static string $UPDATE_JWT_PATH;
+ public static string $SSO_CONFIGURE_SAML_BY_METADATA_SETTINGS;
+ public static string $SSO_CONFIGURE_SAML_SETTINGS;
+ public static string $SSO_CONFIGURE_OIDC_SETTINGS;
+ public static string $SSO_LOAD_SETTINGS_PATH;
+ public static string $SSO_MAPPING_PATH;
+ public static string $SSO_METADATA_PATH;
+ public static string $SSO_SETTINGS_PATH;
+ public static string $ACCESS_KEY_DELETE_PATH;
+ public static string $ACCESS_KEY_ACTIVATE_PATH;
+ public static string $ACCESS_KEY_DEACTIVATE_PATH;
+ public static string $ACCESS_KEY_UPDATE_PATH;
+ public static string $ACCESS_KEYS_SEARCH_PATH;
+ public static string $ACCESS_KEY_LOAD_PATH;
+ public static string $ACCESS_KEY_CREATE_PATH;
+ public static string $USER_HISTORY_PATH;
+ public static string $USER_GENERATE_EMBEDDED_LINK_PATH;
+ public static string $USER_GENERATE_ENCHANTED_LINK_FOR_TEST_PATH;
+ public static string $USER_GENERATE_MAGIC_LINK_FOR_TEST_PATH;
+ public static string $USER_GENERATE_OTP_FOR_TEST_PATH;
+ public static string $USER_REMOVE_TENANT_PATH;
+ public static string $USER_ADD_TENANT_PATH;
+ public static string $USER_REMOVE_ALL_PASSKEYS_PATH;
+ public static string $USER_EXPIRE_PASSWORD_PATH;
+ public static string $USER_SET_ACTIVE_PASSWORD_PATH;
+ public static string $USER_SET_TEMPORARY_PASSWORD_PATH;
+ public static string $USER_SET_PASSWORD_PATH;
+ public static string $USER_REMOVE_SSO_APPS;
+ public static string $USER_SET_SSO_APPS;
+ public static string $USER_ADD_SSO_APPS;
+ public static string $USER_REMOVE_ROLE_PATH;
+ public static string $USER_ADD_ROLE_PATH;
+ public static string $USER_SET_ROLE_PATH;
+ public static string $USER_UPDATE_CUSTOM_ATTRIBUTE_PATH;
+ public static string $USER_UPDATE_PICTURE_PATH;
+ public static string $USER_UPDATE_NAME_PATH;
+ public static string $USER_UPDATE_PHONE_PATH;
+ public static string $USER_UPDATE_EMAIL_PATH;
+ public static string $USER_UPDATE_LOGIN_ID_PATH;
+ public static string $USER_UPDATE_STATUS_PATH;
+ public static string $USER_GET_PROVIDER_TOKEN;
+ public static string $USERS_SEARCH_PATH;
+ public static string $USER_LOAD_PATH;
+ public static string $USER_DELETE_ALL_TEST_USERS_PATH;
+ public static string $USER_LOGOUT_PATH;
+ public static string $USER_DELETE_PATH;
+ public static string $USER_UPDATE_PATH;
+ public static string $USER_CREATE_BATCH_PATH;
+ public static string $USER_CREATE_PATH;
+ public static string $SSO_APPLICATION_LOAD_ALL_PATH;
+ public static string $SSO_APPLICATION_LOAD_PATH;
+ public static string $SSO_APPLICATION_DELETE_PATH;
+ public static string $SSO_APPLICATION_SAML_UPDATE_PATH;
+ public static string $SSO_APPLICATION_OIDC_UPDATE_PATH;
+ public static string $SSO_APPLICATION_SAML_CREATE_PATH;
+ public static string $SSO_APPLICATION_OIDC_CREATE_PATH;
+ public static string $TENANT_SEARCH_ALL_PATH;
+ public static string $TENANT_LOAD_ALL_PATH;
+ public static string $TENANT_LOAD_PATH;
+ public static string $TENANT_DELETE_PATH;
+ public static string $TENANT_UPDATE_PATH;
+ public static string $TENANT_CREATE_PATH;
+ public static string $AUDIT_SEARCH;
+ public static string $AUDIT_CREATE_EVENT;
+
+ /**
+ * Sets the base URL based on the project ID, taking into account the region.
+ *
+ * @param string $projectId The project ID for determining the region.
+ * @return void
+ */
public static function setBaseUrl(string $projectId): void
{
$region = self::extractRegionFromProjectId($projectId);
@@ -25,14 +117,26 @@ public static function setBaseUrl(string $projectId): void
self::updatePaths();
}
+ /**
+ * Extracts the region from a given project ID.
+ *
+ * @param string $projectId The project ID to extract the region from.
+ * @return string|null The extracted region or null if not found.
+ */
private static function extractRegionFromProjectId(string $projectId): ?string
{
if (strlen($projectId) >= 32) {
$region = substr($projectId, 1, 5);
return !empty($region) ? $region : null;
}
+ return null;
}
+ /**
+ * Updates all API endpoint paths based on the current base URL.
+ *
+ * @return void
+ */
private static function updatePaths(): void
{
// Tenant
@@ -129,16 +233,46 @@ private static function updatePaths(): void
self::$FLOW_EXPORT_PATH = self::$baseUrl . "/v1/mgmt/flow/export";
self::$TEMPLATE_IMPORT_PATH = self::$baseUrl . "/v1/mgmt/template/import";
self::$TEMPLATE_EXPORT_PATH = self::$baseUrl . "/v1/mgmt/template/export";
+
+ // Audit
+ self::$AUDIT_SEARCH = self::$baseUrl . "/v1/mgmt/audit/search";
+ self::$AUDIT_CREATE_EVENT = self::$baseUrl . "/v1/mgmt/audit/event";
}
}
+/**
+ * Class representing login options for various authentication methods.
+ */
class LoginOptions
{
+ /**
+ * @var bool Indicates if step-up authentication is required.
+ */
public bool $stepup;
+
+ /**
+ * @var bool Indicates if MFA is required.
+ */
public bool $mfa;
+
+ /**
+ * @var array|null Custom claims to include in the JWT.
+ */
public ?array $customClaims;
+
+ /**
+ * @var array|null Options for templates.
+ */
public ?array $templateOptions;
+ /**
+ * Constructor for the LoginOptions class.
+ *
+ * @param bool $stepup Whether step-up is required.
+ * @param bool $mfa Whether MFA is required.
+ * @param array|null $customClaims Additional custom claims for the JWT.
+ * @param array|null $templateOptions Options for templates.
+ */
public function __construct(
bool $stepup = false,
bool $mfa = false,
@@ -151,17 +285,25 @@ public function __construct(
$this->templateOptions = $templateOptions;
}
- public function toArray()
+ /**
+ * Converts the LoginOptions object to an array.
+ *
+ * @return array The array representation of the login options.
+ */
+ public function toArray(): array
{
return [
'stepup' => $this->stepup,
'mfa' => $this->mfa,
'customClaims' => $this->customClaims,
- 'templateOptions' => $this->templateOptions
+ 'templateOptions' => $this->templateOptions,
];
}
}
+/**
+ * Class representing different delivery methods for authentication.
+ */
class DeliveryMethod
{
public const WHATSAPP = 1;
@@ -170,43 +312,42 @@ class DeliveryMethod
public const EMBEDDED = 4;
public const VOICE = 5;
+ /**
+ * @var int The delivery method value.
+ */
private int $value;
+ /**
+ * Constructor for the DeliveryMethod class.
+ *
+ * @param int $value The delivery method value.
+ */
private function __construct(int $value)
{
$this->value = $value;
}
- public static function WHATSAPP(): self
- {
- return new self(self::WHATSAPP);
- }
-
- public static function SMS(): self
- {
- return new self(self::SMS);
- }
-
- public static function EMAIL(): self
- {
- return new self(self::EMAIL);
- }
-
- public static function EMBEDDED(): self
- {
- return new self(self::EMBEDDED);
- }
-
- public static function VOICE(): self
- {
- return new self(self::VOICE);
- }
+ public static function WHATSAPP(): self { return new self(self::WHATSAPP); }
+ public static function SMS(): self { return new self(self::SMS); }
+ public static function EMAIL(): self { return new self(self::EMAIL); }
+ public static function EMBEDDED(): self { return new self(self::EMBEDDED); }
+ public static function VOICE(): self { return new self(self::VOICE); }
+ /**
+ * Gets the value of the delivery method.
+ *
+ * @return int The delivery method value.
+ */
public function getValue(): int
{
return $this->value;
}
+ /**
+ * Converts the delivery method to a string.
+ *
+ * @return string The string representation of the delivery method.
+ */
public function __toString(): string
{
return (string) $this->value;
diff --git a/src/SDK/Management/Password/UserPassword.php b/src/SDK/Management/Password/UserPassword.php
new file mode 100644
index 0000000..e1d0b26
--- /dev/null
+++ b/src/SDK/Management/Password/UserPassword.php
@@ -0,0 +1,56 @@
+cleartext = $cleartext;
+ $this->hashed = $hashed;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ $data = [];
+ if ($this->cleartext !== null) {
+ $data['cleartext'] = $this->cleartext;
+ }
+ if ($this->hashed !== null) {
+ $data['hashed'] = $this->hashed->toArray();
+ }
+ return $data;
+ }
+}
diff --git a/src/SDK/Management/Password/UserPasswordBcrypt.php b/src/SDK/Management/Password/UserPasswordBcrypt.php
new file mode 100644
index 0000000..c166168
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordBcrypt.php
@@ -0,0 +1,40 @@
+hash = $hash;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ return [
+ 'bcrypt' => [
+ 'hash' => $this->hash,
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/SDK/Management/Password/UserPasswordDjango.php b/src/SDK/Management/Password/UserPasswordDjango.php
new file mode 100644
index 0000000..4d2528c
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordDjango.php
@@ -0,0 +1,40 @@
+hash = $hash;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ return [
+ 'django' => [
+ 'hash' => $this->hash,
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/SDK/Management/Password/UserPasswordFirebase.php b/src/SDK/Management/Password/UserPasswordFirebase.php
new file mode 100644
index 0000000..2ee4339
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordFirebase.php
@@ -0,0 +1,66 @@
+hash = $hash;
+ $this->salt = $salt;
+ $this->saltSeparator = $saltSeparator;
+ $this->signerKey = $signerKey;
+ $this->memory = $memory;
+ $this->rounds = $rounds;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ return [
+ 'firebase' => [
+ 'hash' => $this->hash,
+ 'salt' => $this->salt,
+ 'saltSeparator' => $this->saltSeparator,
+ 'signerKey' => $this->signerKey,
+ 'memory' => $this->memory,
+ 'rounds' => $this->rounds,
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/SDK/Management/Password/UserPasswordPHPass.php b/src/SDK/Management/Password/UserPasswordPHPass.php
new file mode 100644
index 0000000..33a4257
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordPHPass.php
@@ -0,0 +1,52 @@
+hash = $hash;
+ $this->salt = $salt;
+ $this->iterations = $iterations;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ return [
+ 'phpass' => [
+ 'hash' => $this->hash,
+ 'salt' => $this->salt,
+ 'iterations' => $this->iterations
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/SDK/Management/Password/UserPasswordPbkdf2.php b/src/SDK/Management/Password/UserPasswordPbkdf2.php
new file mode 100644
index 0000000..1de37dd
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordPbkdf2.php
@@ -0,0 +1,56 @@
+hash = $hash;
+ $this->salt = $salt;
+ $this->iterations = $iterations;
+ $this->variant = $variant;
+ }
+
+ /**
+ * Convert object data to an array format.
+ *
+ * @return array The password data as an associative array.
+ */
+ public function toArray(): array
+ {
+ return [
+ 'pbkdf2' => [
+ 'hash' => $this->hash,
+ 'salt' => $this->salt,
+ 'iterations' => $this->iterations,
+ 'type' => $this->variant,
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/SDK/Management/User.php b/src/SDK/Management/User.php
index efcdb32..c495b0d 100644
--- a/src/SDK/Management/User.php
+++ b/src/SDK/Management/User.php
@@ -8,9 +8,13 @@
use Descope\SDK\Management\AssociatedTenant;
use Descope\SDK\Management\MgmtV1;
use Descope\SDK\Management\LoginOptions;
-use Descope\SDK\Management\UserPassword;
+use Descope\SDK\Management\Password\UserPassword;
use Descope\SDK\API;
+use GuzzleHttp\Exception\RequestException;
+/**
+ * UserObj class represents the details of a user.
+ */
class UserObj
{
public string $loginId;
@@ -30,6 +34,26 @@ class UserObj
public ?array $ssoAppIds;
public ?UserPassword $password;
+ /**
+ * Constructor for UserObj.
+ *
+ * @param string $loginId The user's login ID.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family name.
+ * @param array|null $roleNames The roles assigned to the user.
+ * @param array|null $userTenants The tenants associated with the user.
+ * @param string|null $picture The URL of the user's profile picture.
+ * @param array|null $customAttributes Custom attributes associated with the user.
+ * @param bool|null $verifiedEmail Whether the user's email is verified.
+ * @param bool|null $verifiedPhone Whether the user's phone number is verified.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password The user's password.
+ */
public function __construct(
string $loginId,
?string $email = null,
@@ -67,15 +91,46 @@ public function __construct(
}
}
+/**
+ * User class provides methods to interact with user-related functionalities in the Descope API.
+ */
class User
{
private API $api;
+ /**
+ * Constructor for the User class.
+ *
+ * @param API $api The API instance to be used for HTTP requests.
+ */
public function __construct(API $api)
{
$this->api = $api;
}
+ /**
+ * Creates a new user.
+ *
+ * @param string $loginId The login ID for the user.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family name.
+ * @param string|null $picture The user's profile picture URL.
+ * @param array|null $customAttributes Custom attributes for the user.
+ * @param bool|null $verifiedEmail Indicates if the user's email is verified.
+ * @param bool|null $verifiedPhone Indicates if the user's phone is verified.
+ * @param string|null $inviteUrl URL to invite the user.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password The user's password.
+ * @param array|null $roleNames Roles assigned to the user.
+ * @param array|null $userTenants Tenants associated with the user.
+ * @return array The created user's information.
+ * @throws AuthException
+ */
public function create(
string $loginId,
?string $email = null,
@@ -128,6 +183,29 @@ public function create(
return $this->api->generateJwtResponse($response);
}
+ /**
+ * Creates a test user.
+ *
+ * @param string $loginId The login ID for the test user.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family name.
+ * @param string|null $picture The user's profile picture URL.
+ * @param array|null $customAttributes Custom attributes for the user.
+ * @param bool|null $verifiedEmail Indicates if the user's email is verified.
+ * @param bool|null $verifiedPhone Indicates if the user's phone is verified.
+ * @param string|null $inviteUrl URL to invite the user.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password The user's password.
+ * @param array|null $roleNames Roles assigned to the user.
+ * @param array|null $userTenants Tenants associated with the user.
+ * @return array The created test user's information.
+ * @throws AuthException
+ */
public function createTestUser(
string $loginId,
?string $email = null,
@@ -180,6 +258,31 @@ public function createTestUser(
return $this->api->generateJwtResponse($response);
}
+ /**
+ * Invites a user.
+ *
+ * @param string $loginId The login ID for the user.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family name.
+ * @param string|null $picture The user's profile picture URL.
+ * @param array|null $customAttributes Custom attributes for the user.
+ * @param bool|null $verifiedEmail Indicates if the user's email is verified.
+ * @param bool|null $verifiedPhone Indicates if the user's phone is verified.
+ * @param string|null $inviteUrl URL to invite the user.
+ * @param bool|null $sendMail Indicates if the invite should be sent via email.
+ * @param bool|null $sendSms Indicates if the invite should be sent via SMS.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password The user's password.
+ * @param array|null $roleNames Roles assigned to the user.
+ * @param array|null $userTenants Tenants associated with the user.
+ * @return array The invited user's information.
+ * @throws AuthException
+ */
public function invite(
string $loginId,
?string $email = null,
@@ -234,6 +337,16 @@ public function invite(
return $this->api->generateJwtResponse($response);
}
+ /**
+ * Invites a batch of users.
+ *
+ * @param array $users The array of UserObj instances representing users to be invited.
+ * @param string|null $inviteUrl URL to invite the users.
+ * @param bool|null $sendMail Indicates if the invite should be sent via email.
+ * @param bool|null $sendSms Indicates if the invite should be sent via SMS.
+ * @return array The response containing details of the invited users.
+ * @throws AuthException
+ */
public function inviteBatch(
array $users,
?string $inviteUrl = null,
@@ -253,6 +366,28 @@ public function inviteBatch(
return $this->api->generateJwtResponse($response);
}
+ /**
+ * Updates an existing user's details.
+ *
+ * @param string $loginId The login ID of the user to update.
+ * @param string|null $email The user's new email address.
+ * @param string|null $phone The user's new phone number.
+ * @param string|null $displayName The user's new display name.
+ * @param string|null $givenName The user's new given name.
+ * @param string|null $middleName The user's new middle name.
+ * @param string|null $familyName The user's new family name.
+ * @param string|null $picture The user's new profile picture URL.
+ * @param array|null $customAttributes Updated custom attributes for the user.
+ * @param bool|null $verifiedEmail Indicates if the user's email is verified.
+ * @param bool|null $verifiedPhone Indicates if the user's phone is verified.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password The user's new password.
+ * @param array|null $roleNames Updated roles for the user.
+ * @param array|null $userTenants Updated tenants associated with the user.
+ * @return void
+ * @throws AuthException
+ */
public function update(
string $loginId,
?string $email = null,
@@ -265,9 +400,8 @@ public function update(
?array $customAttributes = null,
?bool $verifiedEmail = null,
?bool $verifiedPhone = null,
- ?array $additionalLoginIds = null,
+ ?array $additionalIdentifiers = null,
?array $ssoAppIds = null,
- ?UserPassword $password = null,
?array $roleNames = null,
?array $userTenants = null
): void {
@@ -286,14 +420,12 @@ public function update(
$familyName,
$roleNames,
$userTenants,
- false,
$picture,
$customAttributes,
$verifiedEmail,
$verifiedPhone,
- $additionalLoginIds,
- $ssoAppIds,
- $password
+ $additionalIdentifiers,
+ $ssoAppIds
),
true
);
@@ -302,7 +434,7 @@ public function update(
/**
* Delete an existing user by login ID. IMPORTANT: This action is irreversible. Use carefully.
*
- * @param string $loginId The login ID from the user's JWT.
+ * @param string $loginId The login ID from the user's JWT.
* @return void
* @throws AuthException if the delete operation fails.
*/
@@ -318,9 +450,9 @@ public function delete(string $loginId): void
/**
* Delete an existing user by user ID. IMPORTANT: This action is irreversible. Use carefully.
*
- * @param string $userId The user ID from the user's JWT.
+ * @param string $userId The user ID from the user's JWT.
* @return void
- * @throws AuthException if the delete operation fails.
+ * @throws AuthException
*/
public function deleteByUserId(string $userId): void
{
@@ -331,7 +463,13 @@ public function deleteByUserId(string $userId): void
);
}
-
+ /**
+ * Deletes all test users in the system.
+ * IMPORTANT: This action is irreversible. Use with caution.
+ *
+ * @return void
+ * @throws AuthException
+ */
public function deleteAllTestUsers(): void
{
$this->api->doDelete(
@@ -340,22 +478,34 @@ public function deleteAllTestUsers(): void
);
}
+ /**
+ * Loads user details using the login ID.
+ *
+ * @param string $loginId The login ID of the user to retrieve.
+ * @return array The user's details.
+ * @throws AuthException
+ */
public function load(string $loginId): array
{
- $response = $this->api->doGet(
+ return $this->api->doGet(
MgmtV1::$USER_LOAD_PATH . "?loginId=" . $loginId,
true
);
- return $response;
}
+ /**
+ * Loads user details using the user ID.
+ *
+ * @param string $userId The user ID of the user to retrieve.
+ * @return array The user's details.
+ * @throws AuthException
+ */
public function loadByUserId(string $userId): array
{
- $response = $this->api->doGet(
+ return $this->api->doGet(
MgmtV1::$USER_LOAD_PATH . "?userId=" . $userId,
true
);
- return $response;
}
/**
@@ -395,6 +545,12 @@ public function searchAll(
// 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(
@@ -412,63 +568,32 @@ public function searchAll(
);
}
+ // Prepare the request body
$body = [
+ 'loginId' => '',
'tenantIds' => $tenantIds,
'roleNames' => $roleNames,
- 'limit' => $limit,
- 'page' => $page,
- 'testUsersOnly' => $testUsersOnly,
- 'withTestUser' => $withTestUser,
- 'customAttributes' => $customAttributes ?? (object)[],
+ 'limit' => (string)$limit,
+ 'page' => (string)$page,
+ 'text' => $text ?? '',
+ 'ssoOnly' => '',
+ 'withTestUser' => $withTestUser ? true : false,
+ 'testUsersOnly' => $testUsersOnly ? true : false,
+ 'customAttributes' => $customAttributes,
+ 'statuses' => $statuses,
+ 'emails' => $emails,
+ 'phones' => $phones,
+ 'ssoAppIds' => $ssoAppIds,
+ 'sort' => $sort,
+ 'loginIds' => [],
];
- $allowedStatuses = ['enabled', 'disabled', 'invited'];
- if ($statuses !== null) {
- foreach ($statuses as $status) {
- if (!in_array($status, $allowedStatuses)) {
- throw new AuthException(
- 400,
- 'ERROR_TYPE_INVALID_ARGUMENT',
- "The status '$status' is invalid. Allowed values are: " . implode(", ", $allowedStatuses)
- );
- }
- }
- }
-
- if ($emails !== null) {
- $body['emails'] = $emails;
- }
-
- if ($phones !== null) {
- $body['phones'] = $phones;
- }
-
- if ($customAttributes !== null) {
- $body['customAttributes'] = $customAttributes;
- }
-
- if ($ssoAppIds !== null) {
- $body['ssoAppIds'] = $ssoAppIds;
- }
-
- if ($text !== null) {
- $body['text'] = $text;
- }
-
- if ($sort !== null) {
- $body['sort'] = $this->sortToArray($sort);
- }
-
- $jsonBody = json_encode($body);
-
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USERS_SEARCH_PATH,
$body,
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -493,18 +618,18 @@ private function sortToArray(array $sort): array
/**
* Retrieve the provider token for a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $provider The name of the provider.
+ * @param string $loginId The login ID of the user.
+ * @param string $provider The name of the provider.
* @return array The provider token details.
+ * @throws AuthException
*/
public function getProviderToken(string $loginId, string $provider): array
{
try {
- $response = $this->api->doGet(
+ return $this->api->doGet(
MgmtV1::$USER_GET_PROVIDER_TOKEN . "?loginId=" . $loginId . "&provider=" . $provider. "&withRefreshToken=true",
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -515,19 +640,18 @@ public function getProviderToken(string $loginId, string $provider): array
/**
* Activate a user.
*
- * @param string $loginId The login ID of the user.
+ * @param string $loginId The login ID of the user.
* @return array The activation status.
+ * @throws AuthException
*/
public function activate(string $loginId): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_STATUS_PATH,
['loginId' => $loginId, 'status' => 'enabled'],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -538,18 +662,18 @@ public function activate(string $loginId): array
/**
* Deactivate a user.
*
- * @param string $loginId The login ID of the user.
+ * @param string $loginId The login ID of the user.
* @return array The deactivation status.
+ * @throws AuthException
*/
public function deactivate(string $loginId): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_STATUS_PATH,
['loginId' => $loginId, 'status' => 'disabled'],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -560,19 +684,19 @@ public function deactivate(string $loginId): array
/**
* Update the login ID of a user.
*
- * @param string $loginId The current login ID of the user.
- * @param string $newLoginId The new login ID for the user.
+ * @param string $loginId The current login ID of the user.
+ * @param string $newLoginId The new login ID for the user.
* @return array The updated user details.
+ * @throws AuthException
*/
public function updateLoginId(string $loginId, string $newLoginId): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_LOGIN_ID_PATH,
['loginId' => $loginId, 'newLoginId' => $newLoginId],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -583,21 +707,20 @@ public function updateLoginId(string $loginId, string $newLoginId): array
/**
* Update the email address of a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $email The new email address.
- * @param bool $verified Whether the email is verified.
+ * @param string $loginId The login ID of the user.
+ * @param string $email The new email address.
+ * @param bool $verified Whether the email is verified.
* @return array The updated user details.
+ * @throws AuthException
*/
public function updateEmail(string $loginId, string $email, bool $verified): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_EMAIL_PATH,
['loginId' => $loginId, 'email' => $email, 'verified' => $verified],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -608,21 +731,20 @@ public function updateEmail(string $loginId, string $email, bool $verified): arr
/**
* Update the phone number of a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $phone The new phone number.
- * @param bool $verified Whether the phone number is verified.
+ * @param string $loginId The login ID of the user.
+ * @param string $phone The new phone number.
+ * @param bool $verified Whether the phone number is verified.
* @return array The updated user details.
+ * @throws AuthException
*/
public function updatePhone(string $loginId, string $phone, bool $verified): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_PHONE_PATH,
['loginId' => $loginId, 'phone' => $phone, 'verified' => $verified],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -633,12 +755,13 @@ public function updatePhone(string $loginId, string $phone, bool $verified): arr
/**
* Update the display name of a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $displayName The new display name.
- * @param string|null $givenName The given name (optional).
- * @param string|null $middleName The middle name (optional).
- * @param string|null $familyName The family name (optional).
+ * @param string $loginId The login ID of the user.
+ * @param string $displayName The new display name.
+ * @param string|null $givenName The given name (optional).
+ * @param string|null $middleName The middle name (optional).
+ * @param string|null $familyName The family name (optional).
* @return array The updated user details.
+ * @throws AuthException
*/
public function updateDisplayName(
string $loginId,
@@ -659,13 +782,11 @@ public function updateDisplayName(
}
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_NAME_PATH,
$body,
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -676,20 +797,19 @@ public function updateDisplayName(
/**
* Update the profile picture of a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $picture The new profile picture URL.
+ * @param string $loginId The login ID of the user.
+ * @param string $picture The new profile picture URL.
* @return array The updated user details.
+ * @throws AuthException
*/
public function updatePicture(string $loginId, string $picture): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_PICTURE_PATH,
['loginId' => $loginId, 'picture' => $picture],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -700,21 +820,20 @@ public function updatePicture(string $loginId, string $picture): array
/**
* Update a custom attribute for a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $attributeKey The key of the custom attribute.
- * @param mixed $attributeValue The value of the custom attribute.
+ * @param string $loginId The login ID of the user.
+ * @param string $attributeKey The key of the custom attribute.
+ * @param mixed $attributeValue The value of the custom attribute.
* @return array The updated user details.
+ * @throws AuthException
*/
public function updateCustomAttribute(string $loginId, string $attributeKey, $attributeValue): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_UPDATE_CUSTOM_ATTRIBUTE_PATH,
['loginId' => $loginId, 'attributeKey' => $attributeKey, 'attributeValue' => $attributeValue],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -725,20 +844,19 @@ public function updateCustomAttribute(string $loginId, string $attributeKey, $at
/**
* Set roles for a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $roleNames The list of role names to set.
+ * @param string $loginId The login ID of the user.
+ * @param array $roleNames The list of role names to set.
* @return array The updated user details.
+ * @throws AuthException
*/
public function setRoles(string $loginId, array $roleNames): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_SET_ROLE_PATH,
['loginId' => $loginId, 'roleNames' => $roleNames],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -749,20 +867,19 @@ public function setRoles(string $loginId, array $roleNames): array
/**
* Add roles to a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $roleNames The list of role names to add.
+ * @param string $loginId The login ID of the user.
+ * @param array $roleNames The list of role names to add.
* @return array The updated user details.
+ * @throws AuthException
*/
public function addRoles(string $loginId, array $roleNames): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_ADD_ROLE_PATH,
['loginId' => $loginId, 'roleNames' => $roleNames],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -773,20 +890,19 @@ public function addRoles(string $loginId, array $roleNames): array
/**
* Remove roles from a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $roleNames The list of role names to remove.
+ * @param string $loginId The login ID of the user.
+ * @param array $roleNames The list of role names to remove.
* @return array The updated user details.
+ * @throws AuthException
*/
public function removeRoles(string $loginId, array $roleNames): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_REMOVE_ROLE_PATH,
['loginId' => $loginId, 'roleNames' => $roleNames],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -797,20 +913,19 @@ public function removeRoles(string $loginId, array $roleNames): array
/**
* Set SSO applications for a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $ssoAppIds The list of SSO application IDs to set.
+ * @param string $loginId The login ID of the user.
+ * @param array $ssoAppIds The list of SSO application IDs to set.
* @return array The updated user details.
+ * @throws AuthException
*/
public function setSsoApps(string $loginId, array $ssoAppIds): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_SET_SSO_APPS,
['loginId' => $loginId, 'ssoAppIds' => $ssoAppIds],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -821,20 +936,19 @@ public function setSsoApps(string $loginId, array $ssoAppIds): array
/**
* Add SSO applications to a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $ssoAppIds The list of SSO application IDs to add.
+ * @param string $loginId The login ID of the user.
+ * @param array $ssoAppIds The list of SSO application IDs to add.
* @return array The updated user details.
+ * @throws AuthException
*/
public function addSsoApps(string $loginId, array $ssoAppIds): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_ADD_SSO_APPS,
['loginId' => $loginId, 'ssoAppIds' => $ssoAppIds],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -845,20 +959,19 @@ public function addSsoApps(string $loginId, array $ssoAppIds): array
/**
* Remove SSO applications from a user.
*
- * @param string $loginId The login ID of the user.
- * @param array $ssoAppIds The list of SSO application IDs to remove.
+ * @param string $loginId The login ID of the user.
+ * @param array $ssoAppIds The list of SSO application IDs to remove.
* @return array The updated user details.
+ * @throws AuthException
*/
public function removeSsoApps(string $loginId, array $ssoAppIds): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_REMOVE_SSO_APPS,
['loginId' => $loginId, 'ssoAppIds' => $ssoAppIds],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -869,20 +982,19 @@ public function removeSsoApps(string $loginId, array $ssoAppIds): array
/**
* Add a tenant to a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $tenantId The tenant ID to add.
+ * @param string $loginId The login ID of the user.
+ * @param string $tenantId The tenant ID to add.
* @return array The updated user details.
+ * @throws AuthException
*/
public function addTenant(string $loginId, string $tenantId): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_ADD_TENANT_PATH,
['loginId' => $loginId, 'tenantId' => $tenantId],
true
);
-
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -893,19 +1005,19 @@ public function addTenant(string $loginId, string $tenantId): array
/**
* Remove a tenant from a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $tenantId The tenant ID to remove.
+ * @param string $loginId The login ID of the user.
+ * @param string $tenantId The tenant ID to remove.
* @return array The updated user details.
+ * @throws AuthException
*/
public function removeTenant(string $loginId, string $tenantId): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_REMOVE_TENANT_PATH,
['loginId' => $loginId, 'tenantId' => $tenantId],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -916,20 +1028,20 @@ public function removeTenant(string $loginId, string $tenantId): array
/**
* Set roles for a user in a tenant.
*
- * @param string $loginId The login ID of the user.
- * @param string $tenantId The tenant ID.
- * @param array $roleNames The list of role names to set.
+ * @param string $loginId The login ID of the user.
+ * @param string $tenantId The tenant ID.
+ * @param array $roleNames The list of role names to set.
* @return array The updated user details.
+ * @throws AuthException
*/
public function setTenantRoles(string $loginId, string $tenantId, array $roleNames): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_SET_ROLE_PATH,
['loginId' => $loginId, 'tenantId' => $tenantId, 'roleNames' => $roleNames],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -940,20 +1052,20 @@ public function setTenantRoles(string $loginId, string $tenantId, array $roleNam
/**
* Remove roles from a user in a tenant.
*
- * @param string $loginId The login ID of the user.
- * @param string $tenantId The tenant ID.
- * @param array $roleNames The list of role names to remove.
+ * @param string $loginId The login ID of the user.
+ * @param string $tenantId The tenant ID.
+ * @param array $roleNames The list of role names to remove.
* @return array The updated user details.
+ * @throws AuthException
*/
public function removeTenantRoles(string $loginId, string $tenantId, array $roleNames): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_REMOVE_ROLE_PATH,
['loginId' => $loginId, 'tenantId' => $tenantId, 'roleNames' => $roleNames],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -964,9 +1076,10 @@ public function removeTenantRoles(string $loginId, string $tenantId, array $role
/**
* Set a temporary password for a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $password The new temporary password.
+ * @param string $loginId The login ID of the user.
+ * @param UserPassword $password The new temporary password.
* @return void
+ * @throws AuthException
*/
public function setTemporaryPassword(string $loginId, UserPassword $password): void
{
@@ -986,9 +1099,10 @@ public function setTemporaryPassword(string $loginId, UserPassword $password): v
/**
* Set an active password for a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $password The new active password.
+ * @param string $loginId The login ID of the user.
+ * @param UserPassword $password The new active password.
* @return void
+ * @throws AuthException
*/
public function setActivePassword(string $loginId, UserPassword $password): void
{
@@ -1008,10 +1122,11 @@ public function setActivePassword(string $loginId, UserPassword $password): void
/**
* Set a password for a user.
*
- * @param string $loginId The login ID of the user.
- * @param string $password The new password.
- * @param bool $setActive Whether to set the password as active.
+ * @param string $loginId The login ID of the user.
+ * @param string $password The new password.
+ * @param bool $setActive Whether to set the password as active.
* @return void
+ * @throws AuthException
*/
public function setPassword(string $loginId, string $password, bool $setActive = false): void
{
@@ -1031,10 +1146,11 @@ public function setPassword(string $loginId, string $password, bool $setActive =
/**
* Update the user's password.
*
- * @param string $loginId The login ID of the user.
- * @param string $password The new password.
- * @param bool $setActive Whether to set the user as active.
+ * @param string $loginId The login ID of the user.
+ * @param string $password The new password.
+ * @param bool $setActive Whether to set the user as active.
* @return void
+ * @throws AuthException
*/
public function updatePassword(string $loginId, string $password, bool $setActive): void
{
@@ -1054,8 +1170,9 @@ public function updatePassword(string $loginId, string $password, bool $setActiv
/**
* Expire the user's password.
*
- * @param string $loginId The login ID of the user.
+ * @param string $loginId The login ID of the user.
* @return void
+ * @throws AuthException
*/
public function expirePassword(string $loginId): void
{
@@ -1075,8 +1192,9 @@ public function expirePassword(string $loginId): void
/**
* Remove all passkeys for a user.
*
- * @param string $loginId The login ID of the user.
+ * @param string $loginId The login ID of the user.
* @return void
+ * @throws AuthException
*/
public function removeAllPasskeys(string $loginId): void
{
@@ -1096,10 +1214,11 @@ public function removeAllPasskeys(string $loginId): void
/**
* Generate an OTP for a test user.
*
- * @param string $loginId The login ID of the user.
- * @param string $method The delivery method for the OTP.
- * @param array|null $loginOptions Optional login options.
+ * @param string $loginId The login ID of the user.
+ * @param int $method The delivery method for the OTP.
+ * @param LoginOptions|null $loginOptions Optional login options.
* @return array The generated OTP details.
+ * @throws AuthException
*/
public function generateOtpForTestUser(string $loginId, int $method, ?LoginOptions $loginOptions = null): array
{
@@ -1124,16 +1243,17 @@ public function generateOtpForTestUser(string $loginId, int $method, ?LoginOptio
/**
* Generate a magic link for a test user.
*
- * @param string $loginId The login ID of the user.
- * @param string $method The delivery method for the magic link.
- * @param string $uri The URI for the magic link.
- * @param array|null $loginOptions Optional login options.
+ * @param string $loginId The login ID of the user.
+ * @param int $method The delivery method for the magic link.
+ * @param string $uri The URI for the magic link.
+ * @param LoginOptions|null $loginOptions Optional login options.
* @return array The generated magic link details.
+ * @throws AuthException
*/
public function generateMagicLinkForTestUser(string $loginId, int $method, string $uri, ?LoginOptions $loginOptions = null): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_GENERATE_MAGIC_LINK_FOR_TEST_PATH,
[
'loginId' => $loginId,
@@ -1143,7 +1263,6 @@ public function generateMagicLinkForTestUser(string $loginId, int $method, strin
],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -1155,16 +1274,16 @@ public function generateMagicLinkForTestUser(string $loginId, int $method, strin
* Generate Enchanted Link for the given login ID of a test user.
* This is useful when running tests and don't want to use 3rd party messaging services.
*
- * @param string $loginId The login ID of the test user being validated.
- * @param string $uri Optional redirect uri which will be used instead of any global configuration.
- * @param array|null $loginOptions Optional, can be provided to set custom claims to the generated jwt.
+ * @param string $loginId The login ID of the test user being validated.
+ * @param string $uri The redirect URI to be used instead of any global configuration.
+ * @param array|null $loginOptions Optional, can be provided to set custom claims to the generated jwt.
* @return array The enchanted link for the login (exactly as it sent via Email or Phone messaging) and pendingRef.
- * @throws AuthException if the operation fails.
+ * @throws AuthException
*/
public function generateEnchantedLinkForTestUser(string $loginId, string $uri, ?LoginOptions $loginOptions = null): array
{
try {
- $response = $this->api->doPost(
+ return $this->api->doPost(
MgmtV1::$USER_GENERATE_ENCHANTED_LINK_FOR_TEST_PATH,
[
'loginId' => $loginId,
@@ -1173,7 +1292,6 @@ public function generateEnchantedLinkForTestUser(string $loginId, string $uri, ?
],
true
);
- return $response;
} catch (RequestException $e) {
$statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
$responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
@@ -1185,17 +1303,20 @@ public function generateEnchantedLinkForTestUser(string $loginId, string $uri, ?
* Generate Embedded Link for the given user login ID.
* The return value is a token that can be verified via magic link, or using flows.
*
- * @param string $loginId The login ID of the user to authenticate with.
- * @param array|null $customClaims Additional claims to place on the jwt after verification.
+ * @param string $loginId The login ID of the user to authenticate with.
+ * @param array|null $customClaims Additional claims to place on the jwt after verification.
* @return string The token to be used in the verification process.
- * @throws AuthException if the operation fails.
+ * @throws AuthException
*/
public function generateEmbeddedLink(string $loginId, ?array $customClaims = null): string
{
try {
$response = $this->api->doPost(
MgmtV1::$USER_GENERATE_EMBEDDED_LINK_PATH,
- ['loginId' => $loginId, 'customClaims' => $customClaims],
+ [
+ 'loginId' => $loginId,
+ 'customClaims' => $customClaims
+ ],
true
);
@@ -1207,29 +1328,68 @@ public function generateEmbeddedLink(string $loginId, ?array $customClaims = nul
}
}
- // /**
- // * Retrieve users' authentication history, by the given user's IDs.
- // *
- // * @param array $userIds List of users' IDs.
- // * @return array The authentication history of the users.
- // * @throws AuthException if the operation fails.
- // */
- // public function history(array $userIds): array {
- // try {
- // $response = $this->api->doPost(
- // MgmtV1::$USER_HISTORY_PATH,
- // $userIds,
- // true
- // );
+ /**
+ * Retrieve users' authentication history, by the given user's IDs.
+ *
+ * @param array $userIds List of users' IDs.
+ * @return array The authentication history of the users.
+ * @throws AuthException
+ */
+ public function history(array $userIds): array {
+ try {
+ $response = $this->api->doPost(
+ MgmtV1::$USER_HISTORY_PATH,
+ ['userIds' => $userIds],
+ true
+ );
- // return $response;
- // } catch (RequestException $e) {
- // $statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
- // $responseBody = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : 'No response body';
- // throw new AuthException($statusCode, 'RequestException', $e->getMessage());
- // }
- // }
+ // Process response to ensure it's an array of structured UserHistory objects
+ return array_map(function($historyItem) {
+ return [
+ 'userId' => $historyItem['userId'] ?? '',
+ 'loginTime' => $historyItem['loginTime'] ?? 0,
+ 'city' => $historyItem['city'] ?? '',
+ 'country' => $historyItem['country'] ?? '',
+ 'ip' => $historyItem['ip'] ?? '',
+ ];
+ }, $response['usersAuthHistory'] ?? []);
+ } catch (RequestException $e) {
+ $statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : 'N/A';
+ throw new AuthException($statusCode, 'RequestException', $e->getMessage());
+ }
+ }
+ /**
+ * Composes the request body for creating a user.
+ *
+ * This method structures the user information, including login ID, email, phone,
+ * display name, roles, tenants, and additional attributes, into an array format
+ * suitable for creating a user via the API. It also handles password assignment
+ * and the inclusion of optional properties such as verified email and phone.
+ *
+ * @param string $loginId The unique login ID for the user.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given (first) name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family (last) name.
+ * @param array $roleNames An array of role names assigned to the user.
+ * @param array $userTenants An array of user tenants with roles.
+ * @param bool $invited Flag to indicate if the user is invited.
+ * @param bool $test Flag to indicate if the user is a test user.
+ * @param string|null $picture URL of the user's profile picture.
+ * @param array|null $customAttributes Additional custom attributes for the user.
+ * @param bool|null $verifiedEmail Flag to indicate if the user's email is verified.
+ * @param bool|null $verifiedPhone Flag to indicate if the user's phone is verified.
+ * @param string|null $inviteUrl Optional URL for inviting the user.
+ * @param bool|null $sendMail Flag to send an invitation email.
+ * @param bool|null $sendSms Flag to send an invitation SMS.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password User's password information (cleartext or hashed).
+ * @return array The composed request body for user creation.
+ */
public function composeCreateBody(
string $loginId,
?string $email,
@@ -1253,43 +1413,56 @@ public function composeCreateBody(
?array $ssoAppIds,
?UserPassword $password
): array {
- $res = [
- 'loginId' => $loginId,
- 'email' => $email,
- 'phone' => $phone,
- 'displayName' => $displayName,
- 'givenName' => $givenName,
- 'middleName' => $middleName,
- 'familyName' => $familyName,
- 'roleNames' => $roleNames,
- 'userTenants' => $userTenants,
- 'invited' => $invited,
- 'test' => $test,
- 'picture' => $picture,
+ $res = array_filter([
+ 'loginId' => $loginId ?? null,
+ 'email' => $email ?? null,
+ 'phone' => $phone ?? null,
+ 'displayName' => $displayName ?? null,
+ 'givenName' => $givenName ?? null,
+ 'middleName' => $middleName ?? null,
+ 'familyName' => $familyName ?? null,
+ 'roleNames' => $roleNames ?? null,
+ 'userTenants' => $userTenants ?? null,
+ 'invite' => $invited ?? null,
+ 'test' => $test ?? null,
+ 'picture' => $picture ?? null,
'customAttributes' => $customAttributes ?? (object)[],
- 'verifiedEmail' => $verifiedEmail,
- 'verifiedPhone' => $verifiedPhone,
- 'inviteUrl' => $inviteUrl,
- 'sendMail' => $sendMail,
- 'sendSms' => $sendSms,
- 'additionalLoginIds' => $additionalLoginIds,
- 'ssoAppIds' => $ssoAppIds,
- ];
+ 'verifiedEmail' => $verifiedEmail ?? null,
+ 'verifiedPhone' => $verifiedPhone ?? null,
+ 'inviteUrl' => $inviteUrl ?? null,
+ 'sendMail' => $sendMail ?? null,
+ 'sendSMS' => $sendSms ?? null,
+ 'additionalLoginIds' => $additionalLoginIds ?? null,
+ 'ssoAppIds' => $ssoAppIds ?? null,
+ ], static function ($value) {
+ return !empty($value);
+ });
+
if ($password !== null) {
if (isset($password->cleartext)) {
$res['password'] = $password->cleartext;
- } else {
- if (isset($password->hashedPassword)) {
- $res['hashedPassword'] = $password->hashedPassword;
- }
+ } else if (isset($password->hashed)) {
+ $res['hashedPassword'] = $password->hashed;
}
}
- print_r($res);
-
return $res;
}
+ /**
+ * Composes the request body for batch user creation.
+ *
+ * This method iterates over a list of user objects, converting each into an array
+ * using the `composeCreateBody` method. It includes user details such as login ID,
+ * email, phone, and additional attributes. The resulting array of users is formatted
+ * for a batch creation API request.
+ *
+ * @param array $users The array of user objects to be created.
+ * @param string|null $inviteUrl Optional URL for sending invitations to the created users.
+ * @param bool|null $sendMail Optional flag indicating whether to send an invitation email.
+ * @param bool|null $sendSms Optional flag indicating whether to send an invitation SMS.
+ * @return array The structured request body for creating multiple users.
+ */
public function composeCreateBatchBody(
array $users,
?string $inviteUrl,
@@ -1326,6 +1499,31 @@ public function composeCreateBatchBody(
return ['users' => $userArr];
}
+ /**
+ * Composes the request body for updating a user's information.
+ *
+ * This method creates a structured array with user details, including login ID,
+ * email, phone, display name, roles, tenants, and optional attributes like profile
+ * picture and custom attributes. It is used when sending a request to update user data.
+ *
+ * @param string $loginId The login ID of the user.
+ * @param string|null $email The user's email address.
+ * @param string|null $phone The user's phone number.
+ * @param string|null $displayName The user's display name.
+ * @param string|null $givenName The user's given (first) name.
+ * @param string|null $middleName The user's middle name.
+ * @param string|null $familyName The user's family (last) name.
+ * @param array|null $roleNames An array of role names assigned to the user.
+ * @param array|null $userTenants An array of user tenants with roles.
+ * @param string|null $picture URL of the user's profile picture.
+ * @param array|null $customAttributes Additional custom attributes for the user.
+ * @param bool|null $verifiedEmail Flag to indicate if the user's email is verified.
+ * @param bool|null $verifiedPhone Flag to indicate if the user's phone is verified.
+ * @param array|null $additionalLoginIds Additional login IDs for the user.
+ * @param array|null $ssoAppIds SSO app IDs associated with the user.
+ * @param UserPassword|null $password User's password information (cleartext or hashed).
+ * @return array The composed request body for updating user information.
+ */
public function composeUpdateBody(
string $loginId,
?string $email,
@@ -1336,57 +1534,33 @@ public function composeUpdateBody(
?string $familyName,
?array $roleNames,
?array $userTenants,
- ?bool $test,
?string $picture,
?array $customAttributes,
?bool $verifiedEmail,
?bool $verifiedPhone,
- ?array $additionalLoginIds,
- ?array $ssoAppIds,
- ?UserPassword $password
+ ?array $additionalIdentifiers,
+ ?array $ssoAppIds
): array {
$res = [
'loginId' => $loginId,
'email' => $email,
- 'phone' => $phone,
- 'displayName' => $displayName,
- 'givenName' => $givenName,
- 'middleName' => $middleName,
- 'familyName' => $familyName,
- 'roleNames' => $roleNames,
- 'userTenants' => $userTenants,
- 'test' => $test,
- 'picture' => $picture,
+ 'phone' => $phone ?? '',
+ 'verifiedEmail' => $verifiedEmail ?? '',
+ 'verifiedPhone' => $verifiedPhone ?? '',
+ 'name' => $displayName ?? '',
+ 'roleNames' => $roleNames ?? [],
+ 'userTenants' => $userTenants ?? [],
'customAttributes' => $customAttributes ?? (object)[],
- 'additionalLoginIds' => $additionalLoginIds,
- 'ssoAppIds' => $ssoAppIds,
+ 'picture' => $picture ?? '',
+ 'additionalIdentifiers' => $additionalIdentifiers ?? [],
+ 'givenName' => $givenName ?? '',
+ 'middleName' => $middleName ?? '',
+ 'familyName' => $familyName ?? '',
+ 'ssoAppIds' => $ssoAppIds ?? [],
];
- print_r($customAttributes);
- if ($verifiedEmail !== null) {
- $res['verifiedEmail'] = $verifiedEmail;
- }
- if ($givenName !== null) {
- $res['givenName'] = $givenName;
- }
- if ($middleName !== null) {
- $res['middleName'] = $middleName;
- }
- if ($familyName !== null) {
- $res['familyName'] = $familyName;
- }
- if ($verifiedPhone !== null) {
- $res['verifiedPhone'] = $verifiedPhone;
- }
- if ($password !== null) {
- if (isset($password->cleartext)) {
- $res['password'] = $password->cleartext;
- } else {
- if (isset($password->hashedPassword)) {
- $res['hashedPassword'] = $password->hashedPassword;
- }
- }
- }
-
- return $res;
+
+ return array_filter($res, function ($value) {
+ return $value !== null;
+ });
}
}
diff --git a/src/SDK/Management/UserPassword.php b/src/SDK/Management/UserPassword.php
deleted file mode 100644
index c2612f7..0000000
--- a/src/SDK/Management/UserPassword.php
+++ /dev/null
@@ -1,193 +0,0 @@
-hash = $hash;
- $this->salt = $salt;
- $this->iterations = $iterations;
- }
-
- public function toArray(): array
- {
- return [
- 'phpass' => [
- 'hash' => $this->hash,
- 'salt' => $this->salt,
- 'iterations' => $this->iterations
- ],
- ];
- }
-}
-
-class UserPasswordBcrypt
-{
- private string $hash;
-
- public function __construct(string $hash)
- {
- /**
- * The bcrypt hash in plaintext format, for example "$2a$..."
- */
- $this->hash = $hash;
- }
-
- public function toArray(): array
- {
- return [
- 'bcrypt' => [
- 'hash' => $this->hash,
- ],
- ];
- }
-}
-
-class UserPasswordFirebase
-{
- private string $hash;
- private string $salt;
- private string $saltSeparator;
- private string $signerKey;
- private int $memory;
- private int $rounds;
-
- public function __construct(
- string $hash,
- string $salt,
- string $saltSeparator,
- string $signerKey,
- int $memory,
- int $rounds
- ) {
- /**
- * The hash, salt, salt separator, and signer key should be base64 strings using
- * standard encoding with padding.
- * The memory cost value is an integer, usually between 12 to 17.
- * The rounds cost value is an integer, usually between 6 to 10.
- */
- $this->hash = $hash;
- $this->salt = $salt;
- $this->saltSeparator = $saltSeparator;
- $this->signerKey = $signerKey;
- $this->memory = $memory;
- $this->rounds = $rounds;
- }
-
- public function toArray(): array
- {
- return [
- 'firebase' => [
- 'hash' => $this->hash,
- 'salt' => $this->salt,
- 'saltSeparator' => $this->saltSeparator,
- 'signerKey' => $this->signerKey,
- 'memory' => $this->memory,
- 'rounds' => $this->rounds,
- ],
- ];
- }
-}
-
-class UserPasswordPbkdf2
-{
- private string $hash;
- private string $salt;
- private int $iterations;
- private string $variant;
-
- public function __construct(
- string $hash,
- string $salt,
- int $iterations,
- string $variant
- ) {
- /**
- * The hash and salt should be base64 strings using standard encoding with padding.
- * The iterations cost value is an integer, usually in the thousands.
- * The hash variant should be either "sha1", "sha256", or "sha512".
- */
- $this->hash = $hash;
- $this->salt = $salt;
- $this->iterations = $iterations;
- $this->variant = $variant;
- }
-
- public function toArray(): array
- {
- return [
- 'pbkdf2' => [
- 'hash' => $this->hash,
- 'salt' => $this->salt,
- 'iterations' => $this->iterations,
- 'type' => $this->variant,
- ],
- ];
- }
-}
-
-class UserPasswordDjango
-{
- private string $hash;
-
- public function __construct(string $hash)
- {
- /**
- * The django hash in plaintext format, for example "pbkdf2_sha256$..."
- */
- $this->hash = $hash;
- }
-
- public function toArray(): array
- {
- return [
- 'django' => [
- 'hash' => $this->hash,
- ],
- ];
- }
-}
-
-class UserPassword
-{
- private ?string $cleartext;
- private ?object $hashed;
-
- public function __construct(?string $cleartext = null, ?object $hashed = null)
- {
- /**
- * Set a UserPassword on UserObj objects when calling invite_batch to create or invite users
- * with a cleartext or prehashed password. Note that only one of the two options should be set.
- */
- $this->cleartext = $cleartext;
- $this->hashed = $hashed;
- }
-
- public function toArray(): array
- {
- $data = [];
- if ($this->cleartext !== null) {
- $data['cleartext'] = $this->cleartext;
- }
- if ($this->hashed !== null) {
- $data['hashed'] = $this->hashed->toArray();
- }
- return $data;
- }
-}
diff --git a/src/SDK/Token/Extractor.php b/src/SDK/Token/Extractor.php
index 603d2fe..fcee856 100644
--- a/src/SDK/Token/Extractor.php
+++ b/src/SDK/Token/Extractor.php
@@ -37,12 +37,7 @@ public function __construct($config)
public function getClaims($sessionToken): array
{
$jws = $this->parseToken($sessionToken);
- $claims = json_decode($jws->getPayload(), true);
-
- if (!is_array($claims)) {
- return [];
- }
- return $claims;
+ return json_decode($jws->getPayload(), true) ?? [];
}
/**
@@ -53,20 +48,14 @@ public function getClaims($sessionToken): array
public function getUserDetails($refreshToken)
{
$client = $this->config->client;
+ $url = EndpointsV1::$ME_PATH;
+ $header = 'Bearer ' . $this->config->projectId . ":" . $refreshToken;
+
try {
- $url = EndpointsV1::$ME_PATH;
- $header = 'Bearer ' . $this->config->projectId . ":" . $refreshToken;
- $res = $client->request(
- 'GET',
- $url,
- [
- 'headers' => ['Authorization' => $header]
- ]
- );
- $jwkSets = json_decode($res->getBody(), true);
- return $jwkSets;
- } catch (RequestException $re) {
- return $re->getMessage();
+ $response = $client->get($url, ['headers' => ['Authorization' => $header]]);
+ return json_decode($response->getBody(), true);
+ } catch (RequestException $e) {
+ throw new TokenException('Failed to retrieve user details: ' . $e->getMessage());
}
}
diff --git a/src/tests/Management/AuditTest.php b/src/tests/Management/AuditTest.php
index 9711adc..75b310c 100644
--- a/src/tests/Management/AuditTest.php
+++ b/src/tests/Management/AuditTest.php
@@ -3,106 +3,72 @@
namespace Descope\Tests\Management;
use PHPUnit\Framework\TestCase;
-use Descope\SDK\API;
-use Descope\SDK\Management\Audit;
-use Descope\SDK\Management\MgmtV1;
+use Descope\SDK\DescopeSDK;
class AuditTest extends TestCase
{
- private $apiMock;
- private $audit;
+ private DescopeSDK $descopeSDK;
protected function setUp(): void
{
- $this->apiMock = $this->createMock(API::class);
- $this->audit = new Audit($this->apiMock);
+ $config = [
+ 'projectId' => 'P2OkfVnJi5Ht7mpCqHjx17nV5epH',
+ 'managementKey' => 'K2o2rLwk3N3QI7kyJcRUmULKXqB7mKzpY7Dk6Hl24IXRM25YcYDYPFMKCO4SmUTDJJluxlu',
+ ];
+
+ $this->descopeSDK = new DescopeSDK($config);
}
- public function testSearch()
+ public function testSearchAudit()
{
- $response = [
- 'audits' => [
- [
- 'projectId' => 'project1',
- 'userId' => 'user1',
- 'action' => 'login',
- 'occurred' => 1650000000000,
- 'device' => 'mobile',
- 'method' => 'otp',
- 'geo' => 'US',
- 'remoteAddress' => '192.168.1.1',
- 'externalIds' => ['login1'],
- 'tenants' => ['tenant1'],
- 'data' => ['key' => 'value']
- ]
- ]
- ];
-
- $this->apiMock
- ->expects($this->once())
- ->method('doPost')
- ->with(MgmtV1::AUDIT_SEARCH, $this->anything(), true)
- ->willReturn($response);
+ $userIds = ['user1'];
+ $actions = ['login'];
- $result = $this->audit->search(['user1'], ['login']);
+ // Perform the search
+ $result = $this->descopeSDK->management->audit->search($userIds, $actions);
+ // Assertions
$this->assertIsArray($result);
$this->assertArrayHasKey('audits', $result);
- $this->assertCount(1, $result['audits']);
- $this->assertEquals('project1', $result['audits'][0]['projectId']);
- $this->assertEquals('user1', $result['audits'][0]['userId']);
+ $this->assertIsArray($result['audits']);
+
+ foreach ($result['audits'] as $audit) {
+ $this->assertArrayHasKey('projectId', $audit);
+ $this->assertArrayHasKey('userId', $audit);
+ $this->assertArrayHasKey('action', $audit);
+ $this->assertArrayHasKey('occurred', $audit);
+ }
}
- public function testCreateEvent()
+ public function testCreateAuditEvent()
{
- $this->apiMock
- ->expects($this->once())
- ->method('doPost')
- ->with(
- MgmtV1::AUDIT_CREATE_EVENT,
- [
- 'action' => 'login',
- 'type' => 'info',
- 'actorId' => 'actor1',
- 'tenantId' => 'tenant1',
- 'userId' => 'user1',
- 'data' => ['key' => 'value']
- ],
- true
- );
+ // Create an audit event
+ $this->descopeSDK->management->audit->createEvent(
+ 'login',
+ 'info',
+ 'actor1',
+ 'tenant1',
+ 'user1',
+ ['key' => 'value']
+ );
- $this->audit->createEvent('login', 'info', 'actor1', 'tenant1', 'user1', ['key' => 'value']);
+ // If no exceptions were thrown, the test passes.
+ $this->assertTrue(true);
}
- public function testConvertAuditRecord()
+ public function testCreateAuditEventWithoutUserId()
{
- $auditRecord = [
- 'projectId' => 'project1',
- 'userId' => 'user1',
- 'action' => 'login',
- 'occurred' => 1650000000000,
- 'device' => 'mobile',
- 'method' => 'otp',
- 'geo' => 'US',
- 'remoteAddress' => '192.168.1.1',
- 'externalIds' => ['login1'],
- 'tenants' => ['tenant1'],
- 'data' => ['key' => 'value']
- ];
+ // Create an audit event without a userId
+ $this->descopeSDK->management->audit->createEvent(
+ 'login',
+ 'info',
+ 'actor1',
+ 'tenant1',
+ null,
+ ['key' => 'value']
+ );
- $result = $this->audit->convertAuditRecord($auditRecord);
-
- $this->assertIsArray($result);
- $this->assertEquals('project1', $result['projectId']);
- $this->assertEquals('user1', $result['userId']);
- $this->assertEquals('login', $result['action']);
- $this->assertInstanceOf(DateTime::class, $result['occurred']);
- $this->assertEquals('mobile', $result['device']);
- $this->assertEquals('otp', $result['method']);
- $this->assertEquals('US', $result['geo']);
- $this->assertEquals('192.168.1.1', $result['remoteAddress']);
- $this->assertEquals(['login1'], $result['loginIds']);
- $this->assertEquals(['tenant1'], $result['tenants']);
- $this->assertEquals(['key' => 'value'], $result['data']);
+ // If no exceptions were thrown, the test passes.
+ $this->assertTrue(true);
}
}
diff --git a/src/tests/Management/UserPwdTest.php b/src/tests/Management/UserPwdTest.php
index 821eb0e..ec6eeb6 100644
--- a/src/tests/Management/UserPwdTest.php
+++ b/src/tests/Management/UserPwdTest.php
@@ -3,13 +3,13 @@
namespace Descope\Tests\Management;
use PHPUnit\Framework\TestCase;
-use Descope\SDK\Management\UserPassword;
-use Descope\SDK\Management\UserPasswordBcrypt;
-use Descope\SDK\Management\UserPasswordFirebase;
-use Descope\SDK\Management\UserPasswordPbkdf2;
-use Descope\SDK\Management\UserPasswordDjango;
+use Descope\SDK\Management\Password\UserPassword;
+use Descope\SDK\Management\Password\UserPasswordBcrypt;
+use Descope\SDK\Management\Password\UserPasswordFirebase;
+use Descope\SDK\Management\Password\UserPasswordPbkdf2;
+use Descope\SDK\Management\Password\UserPasswordDjango;
-class UserPasswordTest extends TestCase
+class UserPwdTest extends TestCase
{
public function testUserPasswordBcrypt()
{
diff --git a/src/tests/Management/UserTest.php b/src/tests/Management/UserTest.php
index ddc9e31..19ecf65 100644
--- a/src/tests/Management/UserTest.php
+++ b/src/tests/Management/UserTest.php
@@ -4,24 +4,27 @@
use PHPUnit\Framework\TestCase;
use Descope\SDK\DescopeSDK;
-use Descope\SDK\Management\UserPassword;
-use Descope\SDK\Management\UserPasswordBcrypt;
-use Descope\SDK\Management\UserPasswordFirebase;
-use Descope\SDK\Management\UserPasswordPbkdf2;
-use Descope\SDK\Management\UserPasswordDjango;
+use Descope\SDK\Management\Password\UserPassword;
+use Descope\SDK\Management\Password\UserPasswordBcrypt;
+use Descope\SDK\Management\Password\UserPasswordFirebase;
+use Descope\SDK\Management\Password\UserPasswordPbkdf2;
+use Descope\SDK\Management\Password\UserPasswordDjango;
use Descope\SDK\Management\User;
use Descope\SDK\Management\AssociatedTenant;
use Descope\SDK\Management\UserObj;
-use Descope\SDK\Auth\LoginOptions;
-use Descope\SDK\DeliveryMethod;
+use Descope\SDK\Management\LoginOptions;
+use Descope\SDK\Common\DeliveryMethod;
+use Descope\SDK\Exception\AuthException;
+use GuzzleHttp\Exception\RequestException;
-class UserPasswordTest extends TestCase
+class UserTest extends TestCase
{
- private $descopeSDK;
+ private DescopeSDK $descopeSDK;
+ private string $createdUserLoginId;
+ private string $createdUserId;
protected function setUp(): void
{
- // Assuming $config is an array with necessary configuration
$config = [
'projectId' => 'YOUR_PROJECT_ID',
'managementKey' => 'YOUR_MANAGEMENT_KEY',
@@ -47,11 +50,14 @@ public function testCreateUser()
"http://example.com/invite",
["additionalLoginId1"],
["SA2ZsUj73JFqUn8iQx9tblndjKCc6"],
- new UserPassword(cleartext: "password123"),
- [],
- [new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant Admin"])]
+ new UserPassword("Password123!"),
+ ["user"],
+ [new AssociatedTenant("T2o2zKibuWuCVH4lqJrSfFuXss06", ["Tenant Admin"])]
);
- print_r($response);
+ $this->createdUserLoginId = $response['user']['loginIds'][0] ?? null;
+ $this->createdUserId = $response['user']['userId'] ?? null;
+
+ $this->assertArrayHasKey('userId', $response);
}
public function testCreateTestUser()
@@ -71,11 +77,10 @@ public function testCreateTestUser()
"http://example.com/invite2",
["additionalLoginId2"],
["SA2ZsUj73JFqUn8iQx9tblndjKCc6"],
- new UserPassword(cleartext: "password456"),
- ["user"],
- [new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant User"])]
+ new UserPassword("Password456!"),
+ ["user"]
);
- print_r($response);
+ $this->assertArrayHasKey('userId', $response);
}
public function testInviteUser()
@@ -96,11 +101,12 @@ public function testInviteUser()
true,
true,
["additionalLoginId3"],
- [],
- new UserPassword(hashed: new UserPasswordBcrypt("$2y$10$/brZw23J/ya5sOJl8vm7H.BqhDnLqH4ohtSKcZYvSVP/hE6veK.0K")),
- [new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant User"])]
+ ["SA2ZsUj73JFqUn8iQx9tblndjKCc6"],
+ new UserPassword("", new UserPasswordBcrypt("$2y$10$/brZw23J/ya5sOJl8vm7H.BqhDnLqH4ohtSKcZYvSVP/hE6veK.0K")),
+ ["user"],
+ [new AssociatedTenant("T2o2zKibuWuCVH4lqJrSfFuXss06", ["Tenant Admin"])]
);
- print_r($response);
+ $this->assertArrayHasKey('userId', $response);
}
public function testInviteBatchUsers()
@@ -115,14 +121,14 @@ public function testInviteBatchUsers()
"Middle",
"User",
["user"],
- [new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant User"])],
+ [new AssociatedTenant("T2o2zKibuWuCVH4lqJrSfFuXss06", ["Tenant Admin"])],
"http://example.com/picture1.jpg",
- ["customAttr1" => "value1"],
+ [],
true,
true,
["additionalLoginId1"],
[],
- new UserPassword(cleartext: "password123")
+ new UserPassword("Password123!"),
),
new UserObj(
"batchuser2",
@@ -133,64 +139,68 @@ public function testInviteBatchUsers()
"Middle",
"User",
["user"],
- [new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant User"])],
+ [],
"http://example.com/picture2.jpg",
- ["customAttr2" => "value2"],
+ [],
true,
true,
["additionalLoginId2"],
[],
- new UserPassword(cleartext: "password456")
+ new UserPassword("Password456!"),
)
];
+
$response = $this->descopeSDK->management->user->inviteBatch($users, "http://example.com/invitebatch", true, true);
- print_r($response);
+ $this->assertArrayHasKey('createdUsers', $response);
}
public function testUpdateUser()
{
$this->descopeSDK->management->user->update(
- "testuser1",
+ "use login id from previously created user",
"newtestuser1@example.com",
- "+14152464801",
+ "",
"Updated Test User",
- "Updated",
- "Middle",
- "User",
+ "",
+ "",
+ "",
"http://example.com/newpicture.jpg",
- ["dob" => "newvalue1"],
+ [],
true,
- true
+ false,
+ ["additionalLoginId1"]
);
- }
-
- public function testDeleteUser()
- {
- $this->descopeSDK->management->user->delete("testuser1");
+ $this->assertTrue(true);
}
public function testLoadUser()
{
- $response = $this->descopeSDK->management->user->load("gaokevin1");
- print_r($response);
+ $response = $this->descopeSDK->management->user->load($this->createdUserLoginId);
+ $this->assertArrayHasKey('user', $response);
}
public function testLoadUserByUserId()
{
- $response = $this->descopeSDK->management->user->loadByUserId("U2goH2ldn4SzXoFm6IWKlRiEq6JV");
- print_r($response);
+ $response = $this->descopeSDK->management->user->loadByUserId($this->createdUserId);
+ $this->assertArrayHasKey('user', $response);
}
- public function testSignIn()
+ public function testGenerateEmbeddedLink()
{
- $response = $this->descopeSDK->password->signIn("gaokevin", "6ny8UPNgTVtwB,tcjltg");
- print_r($response);
+ $response = $this->descopeSDK->management->user->generateEmbeddedLink("kevin+1@descope.com");
+ $this->assertIsString($response);
}
- public function testLogoutUser()
+ public function testGenerateEnchantedLinkForTestUser()
{
- $response = $this->descopeSDK->password->signIn("gaokevin", "6ny8UPNgTVtwB,tcjltg");
- $this->descopeSDK->logout($response['refreshSessionToken']);
+ $loginOptions = new LoginOptions(true, true);
+ $response = $this->descopeSDK->management->user->generateEnchantedLinkForTestUser(
+ "testuser1",
+ "http://example.com/redirect",
+ $loginOptions
+ );
+ $this->assertArrayHasKey('link', $response);
+ $this->assertArrayHasKey('pendingRef', $response);
}
public function testSearchAllUsers()
@@ -199,7 +209,7 @@ public function testSearchAllUsers()
[],
[],
10,
- 1,
+ 0,
false,
false,
[],
@@ -208,109 +218,30 @@ public function testSearchAllUsers()
["+14152464801"],
[]
);
- print_r($response);
- }
-
- public function testGetProviderToken()
- {
- $response = $this->descopeSDK->management->user->getProviderToken("gaokevin1", "google");
- print_r($response);
+ $this->assertArrayHasKey('users', $response);
}
public function testActivateUser()
{
- $response = $this->descopeSDK->management->user->activate("gaokevin1");
- print_r($response);
+ $response = $this->descopeSDK->management->user->activate("testuser1");
+ $this->assertTrue(true);
}
public function testDeactivateUser()
{
$response = $this->descopeSDK->management->user->deactivate("testuser1");
- print_r($response);
- }
-
- public function testUpdateLoginId()
- {
- $response = $this->descopeSDK->management->user->updateLoginId("testuser1", "newtestuser1");
- print_r($response);
- }
-
- public function testUpdateEmail()
- {
- $response = $this->descopeSDK->management->user->updateEmail("testuser1", "newtestuser1@example.com", true);
- print_r($response);
- }
-
- public function testUpdatePhone()
- {
- $response = $this->descopeSDK->management->user->updatePhone("testuser1", "+14152464801", true);
- print_r($response);
- }
-
- public function testUpdateDisplayName()
- {
- $response = $this->descopeSDK->management->user->updateDisplayName("testuser1", "Updated Display Name", "Updated Given Name", "Updated Middle Name", "Updated Family Name");
- print_r($response);
- }
-
- public function testUpdatePicture()
- {
- $response = $this->descopeSDK->management->user->updatePicture("testuser1", "http://example.com/newpicture.jpg");
- print_r($response);
- }
-
- public function testUpdateCustomAttribute()
- {
- $response = $this->descopeSDK->management->user->updateCustomAttribute("testuser1", "customAttr1", "newvalue1");
- print_r($response);
- }
-
- public function testSetRoles()
- {
- $response = $this->descopeSDK->management->user->setRoles("testuser1", ["user"]);
- print_r($response);
+ $this->assertTrue(true);
}
- public function testAddRoles()
- {
- $response = $this->descopeSDK->management->user->addRoles("testuser1", ["admin"]);
- print_r($response);
- }
-
- public function testRemoveRoles()
- {
- $response = $this->descopeSDK->management->user->removeRoles("testuser1", ["admin"]);
- print_r($response);
- }
-
- public function testSetTenants()
- {
- $tenants = [
- new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBA", ["Tenant Admin"]),
- new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBB", ["Tenant User"])
- ];
- $response = $this->descopeSDK->management->user->setTenants("testuser1", $tenants);
- print_r($response);
- }
-
- public function testAddTenants()
- {
- $tenants = [
- new AssociatedTenant("T2SrweL5J2y8YOh8DyDbGpZXejBC", ["Tenant Viewer"])
- ];
- $response = $this->descopeSDK->management->user->addTenants("testuser1", $tenants);
- print_r($response);
- }
-
- public function testRemoveTenants()
+ public function testDeleteUser()
{
- $response = $this->descopeSDK->management->user->removeTenants("testuser1", ["T2SrweL5J2y8YOh8DyDbGpZXejBB"]);
- print_r($response);
+ $this->descopeSDK->management->user->delete("testuser1");
+ $this->assertTrue(true);
}
- public function testUpdateDisplay()
+ public function testDeleteAllTestUsers()
{
- $response = $this->descopeSDK->management->user->updateDisplay("testuser1", "Updated Display");
- print_r($response);
+ $this->descopeSDK->management->user->deleteAllTestUsers();
+ $this->assertTrue(true);
}
}