From 92c9964018c17f6945bd8c580fa1afccff20ec44 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 15 May 2025 04:26:21 +0200 Subject: [PATCH 01/19] Adding the avatar handling --- README.md | 36 +++++- ...delPhpApi-Settings-PasswordComplexity.html | 122 ++++++++++++++++-- docs/classes/ZitadelPhpApi-User-Avatar.html | 14 +- docs/js/searchIndex.js | 14 +- src/Settings/PasswordComplexity.php | 12 ++ src/User/Avatar.php | 13 +- 6 files changed, 183 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index bcd8f8b..d8306fd 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ try { } ``` -**Add user avatar** +**Add user avatar with path** ```php use ZitadelPhpApi\User\Avatar; @@ -65,7 +65,7 @@ $settings = [ // Add avatar $avatar = new Avatar($settings); -$avatar->setUserId('313871513763708931'); +$avatar->setUserId('319154205375856643'); $avatar->setImagePath('avatar_500x500.png'); try { $avatar->add(); @@ -74,6 +74,38 @@ try { } ``` +**Add user avatar with form** +```html +
+ + + +
+``` +And the php Code: +```php +use ZitadelPhpApi\User\Avatar; + +$settings = [ + "domain" => "", + "serviceUserToken" => "", + "userToken" => "" +]; + +// Add avatar +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + // Add avatar + $avatar = new Avatar($settings); + $avatar->setUserId('319154205375856643'); + $avatar->setImagePath($_FILES['file']['tmp_name']); + try { + $avatar->add(); + } catch (Exception $e) { + echo $e->getMessage(); + } +} +``` + **Remove user avatar** ```php use ZitadelPhpApi\User\Avatar; diff --git a/docs/classes/ZitadelPhpApi-Settings-PasswordComplexity.html b/docs/classes/ZitadelPhpApi-Settings-PasswordComplexity.html index eb761cc..314f471 100644 --- a/docs/classes/ZitadelPhpApi-Settings-PasswordComplexity.html +++ b/docs/classes/ZitadelPhpApi-Settings-PasswordComplexity.html @@ -172,6 +172,12 @@

 : string|null +
+ $rawPasswordSettings + +  : bool +
+
$requiresLowercase @@ -230,6 +236,13 @@

Get the minimum length of the password
+
+ getRawPasswoerdSettings() + +  : string +
+
Returns the raw password settings as a JSON string.
+
getResourceOwnerType() @@ -363,6 +376,42 @@

+ +
+

+ $rawPasswordSettings + + + + +

+ + + + + + private + bool + $rawPasswordSettings + + + + + + + +
+
+

+ getRawPasswoerdSettings() + + +

+ + +

Returns the raw password settings as a JSON string.

+ + + public + getRawPasswoerdSettings() : string + +
+
+ + + + + + + +
+
Return values
+ string + — +

The raw password settings as a JSON string.

+
+ +
+
@@ -735,7 +827,7 @@

@@ -778,7 +870,7 @@

@@ -821,7 +913,7 @@

@@ -864,7 +956,7 @@

@@ -907,7 +999,7 @@

@@ -959,7 +1051,7 @@

@@ -1107,6 +1199,7 @@

Parameters

-
Add the avatar to the user. Needs the userToken
+
Add the avatar to the user. Needs the `userToken` +! The user token must have the scope 'urn:zitadel:iam:org:project:id:zitadel:aud'
remove()  : void
-
Remove the user's avatar.
+
Remove the user's avatar with the `serviceUserToken`.
setImagePath() @@ -453,11 +454,12 @@

-

Add the avatar to the user. Needs the userToken

+

Add the avatar to the user. Needs the `userToken` +! The user token must have the scope 'urn:zitadel:iam:org:project:id:zitadel:aud'

public @@ -505,11 +507,11 @@

-

Remove the user's avatar.

+

Remove the user's avatar with the `serviceUserToken`.

public diff --git a/docs/js/searchIndex.js b/docs/js/searchIndex.js index 0b53645..d96d0fb 100644 --- a/docs/js/searchIndex.js +++ b/docs/js/searchIndex.js @@ -750,6 +750,11 @@ Search.appendIndex( "name": "getResourceOwnerType", "summary": "Returns\u0020the\u0020resource\u0020owner\u0020type.", "url": "classes/ZitadelPhpApi-Settings-PasswordComplexity.html#method_getResourceOwnerType" + }, { + "fqsen": "\\ZitadelPhpApi\\Settings\\PasswordComplexity\u003A\u003AgetRawPasswoerdSettings\u0028\u0029", + "name": "getRawPasswoerdSettings", + "summary": "Returns\u0020the\u0020raw\u0020password\u0020settings\u0020as\u0020a\u0020JSON\u0020string.", + "url": "classes/ZitadelPhpApi-Settings-PasswordComplexity.html#method_getRawPasswoerdSettings" }, { "fqsen": "\\ZitadelPhpApi\\Settings\\PasswordComplexity\u003A\u003AsendRequest\u0028\u0029", "name": "sendRequest", @@ -770,6 +775,11 @@ Search.appendIndex( "name": "minLength", "summary": "", "url": "classes/ZitadelPhpApi-Settings-PasswordComplexity.html#property_minLength" + }, { + "fqsen": "\\ZitadelPhpApi\\Settings\\PasswordComplexity\u003A\u003A\u0024rawPasswordSettings", + "name": "rawPasswordSettings", + "summary": "", + "url": "classes/ZitadelPhpApi-Settings-PasswordComplexity.html#property_rawPasswordSettings" }, { "fqsen": "\\ZitadelPhpApi\\Settings\\PasswordComplexity\u003A\u003A\u0024resourceOwnerType", "name": "resourceOwnerType", @@ -853,12 +863,12 @@ Search.appendIndex( }, { "fqsen": "\\ZitadelPhpApi\\User\\Avatar\u003A\u003Aadd\u0028\u0029", "name": "add", - "summary": "Add\u0020the\u0020avatar\u0020to\u0020the\u0020user.\u0020Needs\u0020the\u0020userToken", + "summary": "Add\u0020the\u0020avatar\u0020to\u0020the\u0020user.\u0020Needs\u0020the\u0020\u0060userToken\u0060\n\u0021\u0020The\u0020user\u0020token\u0020must\u0020have\u0020the\u0020scope\u0020\u0027urn\u003Azitadel\u003Aiam\u003Aorg\u003Aproject\u003Aid\u003Azitadel\u003Aaud\u0027", "url": "classes/ZitadelPhpApi-User-Avatar.html#method_add" }, { "fqsen": "\\ZitadelPhpApi\\User\\Avatar\u003A\u003Aremove\u0028\u0029", "name": "remove", - "summary": "Remove\u0020the\u0020user\u0027s\u0020avatar.", + "summary": "Remove\u0020the\u0020user\u0027s\u0020avatar\u0020with\u0020the\u0020\u0060serviceUserToken\u0060.", "url": "classes/ZitadelPhpApi-User-Avatar.html#method_remove" }, { "fqsen": "\\ZitadelPhpApi\\User\\Avatar\u003A\u003A\u0024settings", diff --git a/src/Settings/PasswordComplexity.php b/src/Settings/PasswordComplexity.php index bcd3620..284a019 100644 --- a/src/Settings/PasswordComplexity.php +++ b/src/Settings/PasswordComplexity.php @@ -12,6 +12,7 @@ class PasswordComplexity private array $settings; private ?string $orgId = null; private ?int $minLength; + private bool $rawPasswordSettings; private bool $requiresUppercase; private bool $requiresLowercase; private bool $requiresNumber; @@ -99,6 +100,16 @@ public function getResourceOwnerType(): string return $this->resourceOwnerType; } + /** + * Returns the raw password settings as a JSON string. + * + * @return string The raw password settings as a JSON string. + */ + public function getRawPasswoerdSettings(): string + { + return $this->rawPasswordSettings; + } + /** * Send a GET request to the ZITADEL API to retrieve the current password complexity settings. * @@ -132,6 +143,7 @@ public function sendRequest(): void throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); } else { $response = $response->settings; + $this->rawPasswordSettings = $response ?? ""; $this->minLength = $response->minLength ?? null; $this->requiresUppercase = $response->requiresUppercase ?? ""; $this->requiresLowercase = $response->requiresLowercase ?? ""; diff --git a/src/User/Avatar.php b/src/User/Avatar.php index a5a3094..336efed 100644 --- a/src/User/Avatar.php +++ b/src/User/Avatar.php @@ -73,7 +73,8 @@ public function setImagePath(string $avatar): void } /** - * Add the avatar to the user. Needs the userToken + * Add the avatar to the user. Needs the `userToken` + * ! The user token must have the scope 'urn:zitadel:iam:org:project:id:zitadel:aud' * * @throws Exception If there's an error * @return void @@ -89,6 +90,7 @@ public function add() CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_FOLLOWLOCATION => true, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $this->postData, @@ -98,9 +100,12 @@ public function add() "Authorization: Bearer $token" ), )); - - $response = json_decode(curl_exec($curl)); + + // $response = json_decode(curl_exec($curl)); + $response = curl_exec($curl); curl_close($curl); + + dump($response); exit; if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); @@ -108,7 +113,7 @@ public function add() } /** - * Remove the user's avatar. + * Remove the user's avatar with the `serviceUserToken`. * * @return void * @throws Exception If an error occurs during the request to remove the avatar from the server. From 3add746994f7462b7dc1ef14166853499c741cb7 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 15 May 2025 04:29:51 +0200 Subject: [PATCH 02/19] Add tag v1.1.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8306fd..d9c6782 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API +# Zitadel PHP API version 1.1.0 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From 21c90329de9c28930e6b2c999073a1c84ccf4f73 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 14:04:11 +0200 Subject: [PATCH 03/19] cleanup and change settings from private to protected --- src/Session/Create.php | 2 +- src/Session/Get.php | 2 +- src/Session/Terminate.php | 2 +- src/Session/Update.php | 2 +- src/Settings/ActiveIDP.php | 3 +- src/Settings/BasicInformation.php | 3 +- src/Settings/LoginSettings.php | 3 +- src/Settings/PasswordComplexity.php | 3 +- src/User/Account.php | 4 +-- src/User/Avatar.php | 6 ++-- src/User/Create.php | 3 +- src/User/Delete.php | 3 +- src/User/Edit.php | 3 +- src/User/Email.php | 46 ++++++++++++++++++++++++--- src/User/Get.php | 5 +-- src/User/IDP.php | 5 ++- src/User/Password.php | 4 ++- src/User/Phone.php | 7 ++-- src/User/SetupTwoFactorAuth/Email.php | 4 +-- src/User/SetupTwoFactorAuth/SMS.php | 4 +-- src/User/SetupTwoFactorAuth/TOTP.php | 6 ++-- 21 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/Session/Create.php b/src/Session/Create.php index 87d3cf3..686541d 100644 --- a/src/Session/Create.php +++ b/src/Session/Create.php @@ -12,7 +12,7 @@ */ class Create { - private array $settings; + protected array $settings; private string $sessionId; private string $sessionToken; private string $smsCode; diff --git a/src/Session/Get.php b/src/Session/Get.php index 1df74a6..e3c7d93 100644 --- a/src/Session/Get.php +++ b/src/Session/Get.php @@ -11,7 +11,7 @@ */ class Get { - private array $settings; + protected array $settings; private string $sessionId; private string $sessionToken; private string $creationDate; diff --git a/src/Session/Terminate.php b/src/Session/Terminate.php index d4dae03..ed30a09 100644 --- a/src/Session/Terminate.php +++ b/src/Session/Terminate.php @@ -9,7 +9,7 @@ */ class Terminate { - private array $settings; + protected array $settings; private string $sessionId; private string $sessionToken; diff --git a/src/Session/Update.php b/src/Session/Update.php index f4baa80..e828a22 100644 --- a/src/Session/Update.php +++ b/src/Session/Update.php @@ -12,7 +12,7 @@ */ class Update { - private array $settings; + protected array $settings; private string $sessionId; private string $smsCode; private string $emailCode; diff --git a/src/Settings/ActiveIDP.php b/src/Settings/ActiveIDP.php index 23a1fdf..166d89c 100644 --- a/src/Settings/ActiveIDP.php +++ b/src/Settings/ActiveIDP.php @@ -6,7 +6,7 @@ class ActiveIDP { - private array $settings; + protected array $settings; private ?string $orgId = null; private array $identityProviders; @@ -28,6 +28,7 @@ public function get(): array public function sendRequest(): void { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/settings/login/idps?ctx.orgId=" . $this->orgId, diff --git a/src/Settings/BasicInformation.php b/src/Settings/BasicInformation.php index 3c5465b..49b8881 100644 --- a/src/Settings/BasicInformation.php +++ b/src/Settings/BasicInformation.php @@ -6,7 +6,7 @@ class BasicInformation { - private array $settings; + protected array $settings; private string $defaultOrgId; private string $defaultLanguage; private array $supportedLanguages; @@ -34,6 +34,7 @@ public function getSupportedLanguages(): array public function sendRequest(): void { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/settings", diff --git a/src/Settings/LoginSettings.php b/src/Settings/LoginSettings.php index 6cecef7..0eba5a7 100644 --- a/src/Settings/LoginSettings.php +++ b/src/Settings/LoginSettings.php @@ -9,7 +9,7 @@ */ class LoginSettings { - private array $settings; + protected array $settings; private ?string $orgId = null; private bool $allowUsernamePassword; private bool $allowRegister; @@ -145,6 +145,7 @@ public function forceMfaLocalOnly(): bool public function sendRequest(): void { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/settings/login?ctx.orgId=" . $this->orgId, diff --git a/src/Settings/PasswordComplexity.php b/src/Settings/PasswordComplexity.php index 284a019..a207690 100644 --- a/src/Settings/PasswordComplexity.php +++ b/src/Settings/PasswordComplexity.php @@ -9,7 +9,7 @@ */ class PasswordComplexity { - private array $settings; + protected array $settings; private ?string $orgId = null; private ?int $minLength; private bool $rawPasswordSettings; @@ -119,6 +119,7 @@ public function getRawPasswoerdSettings(): string public function sendRequest(): void { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/settings/password/complexity?ctx.orgId=" . $this->orgId, diff --git a/src/User/Account.php b/src/User/Account.php index 79157b1..2ace3f0 100644 --- a/src/User/Account.php +++ b/src/User/Account.php @@ -9,7 +9,7 @@ */ class Account { - private array $settings; + protected array $settings; private int $userid; private string $action; @@ -94,8 +94,8 @@ public function unlock(): void private function request(): void { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); - curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/$this->action", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/Avatar.php b/src/User/Avatar.php index 336efed..ce2bd68 100644 --- a/src/User/Avatar.php +++ b/src/User/Avatar.php @@ -9,7 +9,7 @@ */ class Avatar { - private array $settings; + protected array $settings; private int $userid; private String $boundary; private String $postData; @@ -82,8 +82,8 @@ public function setImagePath(string $avatar): void public function add() { $token = $this->settings["userToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/assets/v1/users/me/avatar", CURLOPT_RETURNTRANSFER => true, @@ -121,8 +121,8 @@ public function add() public function remove() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); - curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/avatar", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/Create.php b/src/User/Create.php index 6963a61..cb90f7d 100644 --- a/src/User/Create.php +++ b/src/User/Create.php @@ -9,7 +9,7 @@ */ class Create { - private array $settings; + protected array $settings; private array $request; /** @@ -198,6 +198,7 @@ public function addIDPLink(int $idpId, string $userId, string $userName): void public function create() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/human", diff --git a/src/User/Delete.php b/src/User/Delete.php index 4148ae7..8e57278 100644 --- a/src/User/Delete.php +++ b/src/User/Delete.php @@ -12,7 +12,7 @@ */ class Delete { - private array $settings; + protected array $settings; private int $userid; /** @@ -45,6 +45,7 @@ public function setUserId(int $userid) public function delete() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid", diff --git a/src/User/Edit.php b/src/User/Edit.php index 22c68a4..e0fd033 100644 --- a/src/User/Edit.php +++ b/src/User/Edit.php @@ -10,7 +10,7 @@ */ class Edit { - private array $settings; + protected array $settings; private int $userid; private array $request; @@ -116,6 +116,7 @@ public function setGender(string $gender) public function edit() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/human/$this->userid", diff --git a/src/User/Email.php b/src/User/Email.php index 3ca9a5b..0afad3a 100644 --- a/src/User/Email.php +++ b/src/User/Email.php @@ -9,7 +9,7 @@ */ class Email { - private array $settings; + protected array $settings; private int $userid; private string $returnedVerificationCode; @@ -78,7 +78,7 @@ public function changeEmail(string $email) $response = json_decode(curl_exec($curl)); curl_close($curl); - + if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); } else { @@ -94,8 +94,9 @@ public function changeEmail(string $email) */ public function resendVerificationCode() { - $curl = curl_init(); $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/email/resend", CURLOPT_RETURNTRANSFER => true, @@ -124,6 +125,43 @@ public function resendVerificationCode() } } + /** + * Get a new Verification code + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function sendVerificationCode() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/email/send", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => '{}', + CURLOPT_HTTPHEADER => array( + "Content-Type: application/json", + "Accept: application/json", + "Authorization: Bearer $token" + ), + )); + + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } else { + $this->returnedVerificationCode = $response->verificationCode; + } + } + /** * Verifies an email address with a verification code * @@ -133,8 +171,8 @@ public function resendVerificationCode() public function verify(string $verifyCode): bool { $token = $this->settings["serviceUserToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/email/verify", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/Get.php b/src/User/Get.php index f5fecd4..adedecf 100644 --- a/src/User/Get.php +++ b/src/User/Get.php @@ -9,7 +9,7 @@ */ class Get { - private array $settings; + protected array $settings; private int $userid; private string $userState; private string $userName; @@ -33,7 +33,7 @@ class Get * * @param $settings array The settings array */ - public function __construct(array $settings) + public function __construct(array $settings = []) { $this->settings = $settings; } @@ -218,6 +218,7 @@ public function getRawUserData(): string public function fetch() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid", diff --git a/src/User/IDP.php b/src/User/IDP.php index df9ed4b..f98de7f 100644 --- a/src/User/IDP.php +++ b/src/User/IDP.php @@ -9,7 +9,7 @@ */ class IDP { - private array $settings; + protected array $settings; private int $userid; private string $idpId; private string $idpToken; @@ -212,6 +212,7 @@ public function getIdpProfile(): string public function startFlow() { $token = $this->settings["userToken"]; + $curl = curl_init(); $request = array( "idpId" => $this->idpId, @@ -256,6 +257,7 @@ public function startFlow() public function fetchIdpData() { $token = $this->settings["userToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/idp_intents/$this->idpIntentId", @@ -302,6 +304,7 @@ public function fetchIdpData() public function linkIdpToUser() { $token = $this->settings["serviceUserToken"]; + $request = array( "idpLink" => array( "idpId" => $this->idpId, diff --git a/src/User/Password.php b/src/User/Password.php index d2f11ec..94ad3fb 100644 --- a/src/User/Password.php +++ b/src/User/Password.php @@ -9,7 +9,7 @@ */ class Password { - private array $settings; + protected array $settings; private int $userid; private array $request; private string $verifyCode; @@ -90,6 +90,7 @@ public function getVerifyCode(): string public function change(): bool { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/password", @@ -127,6 +128,7 @@ public function change(): bool public function requestVerifyCode() { $token = $this->settings["serviceUserToken"]; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/password_reset", diff --git a/src/User/Phone.php b/src/User/Phone.php index e01dd50..bd90448 100644 --- a/src/User/Phone.php +++ b/src/User/Phone.php @@ -9,7 +9,7 @@ */ class Phone { - private array $settings; + protected array $settings; private int $userid; private string $returnedVerificationCode; @@ -92,8 +92,9 @@ public function changePhone(string $phone) */ public function resendVerificationCode() { - $curl = curl_init(); $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/phone/resend", CURLOPT_RETURNTRANSFER => true, @@ -127,8 +128,8 @@ public function resendVerificationCode() public function verify(string $verifyCode): bool { $token = $this->settings["serviceUserToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/phone/verify", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/SetupTwoFactorAuth/Email.php b/src/User/SetupTwoFactorAuth/Email.php index 83daf8e..dcb4af2 100644 --- a/src/User/SetupTwoFactorAuth/Email.php +++ b/src/User/SetupTwoFactorAuth/Email.php @@ -9,7 +9,7 @@ */ class Email { - private array $settings; + protected array $settings; private int $userid; /** @@ -65,8 +65,8 @@ public function remove() private function request(string $action) { $token = $this->settings["userToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/otp_email", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/SetupTwoFactorAuth/SMS.php b/src/User/SetupTwoFactorAuth/SMS.php index 3789f79..62aba48 100644 --- a/src/User/SetupTwoFactorAuth/SMS.php +++ b/src/User/SetupTwoFactorAuth/SMS.php @@ -9,7 +9,7 @@ */ class SMS { - private array $settings; + protected array $settings; private int $userid; /** Initialize the SMS OTP setup @@ -54,8 +54,8 @@ public function remove() private function request() { $token = $this->settings["userToken"]; + $curl = curl_init(); - curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/otp_sms", CURLOPT_RETURNTRANSFER => true, diff --git a/src/User/SetupTwoFactorAuth/TOTP.php b/src/User/SetupTwoFactorAuth/TOTP.php index 48b3933..4d00c2a 100644 --- a/src/User/SetupTwoFactorAuth/TOTP.php +++ b/src/User/SetupTwoFactorAuth/TOTP.php @@ -10,7 +10,7 @@ */ class TOTP { - private array $settings; + protected array $settings; private int $userid; private string $secret; private string $totpUri; @@ -65,8 +65,8 @@ public function getQRCode(): string public function start() { $token = $this->settings["userToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/totp", CURLOPT_RETURNTRANSFER => true, @@ -101,8 +101,8 @@ public function start() public function verify($verifyCode): bool { $token = $this->settings["userToken"]; - $curl = curl_init(); + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/totp/verify", CURLOPT_RETURNTRANSFER => true, From b083ec94768070ce6b7fa8a756daf814e5d7116d Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 14:05:27 +0200 Subject: [PATCH 04/19] Update the readme version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9c6782..9138fd3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.0 +# Zitadel PHP API version 1.1.1 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From da4cb963cc903067e122f1ce1aefe77ba6f62ca9 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 14:25:40 +0200 Subject: [PATCH 05/19] Smal changes get userid on create, set email on edit --- src/User/Create.php | 4 +++- src/User/Edit.php | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/User/Create.php b/src/User/Create.php index cb90f7d..2c45774 100644 --- a/src/User/Create.php +++ b/src/User/Create.php @@ -192,7 +192,7 @@ public function addIDPLink(int $idpId, string $userId, string $userName): void /** * Create the new user and sends the data to Zitadel * - * @return void + * @return string zitadel_user_id * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails */ public function create() @@ -222,5 +222,7 @@ public function create() if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); } + + return $response->userId; } } diff --git a/src/User/Edit.php b/src/User/Edit.php index e0fd033..c0a4285 100644 --- a/src/User/Edit.php +++ b/src/User/Edit.php @@ -106,6 +106,17 @@ public function setGender(string $gender) $this->request["profile"]["gender"] = "GENDER_UNSPECIFIED"; } } + + /** + * Change the email address + * + * @param $email string Email + * @return void + */ + public function setEmail(string $email) + { + $this->request["email"]["email"] = $email; + } /** * Change the user data and sends the data to Zitadel From cfd7476d781297efe9a4562438ea9541252d8546 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 14:27:12 +0200 Subject: [PATCH 06/19] Add new version on the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9138fd3..cdc4176 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.1 +# Zitadel PHP API version 1.1.2 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From 70542696a64552ccbb363d70bfb453cce7074b8e Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 15:29:41 +0200 Subject: [PATCH 07/19] Remoe a wrong setter --- src/User/Email.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/User/Email.php b/src/User/Email.php index 0afad3a..16a811e 100644 --- a/src/User/Email.php +++ b/src/User/Email.php @@ -157,8 +157,6 @@ public function sendVerificationCode() if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); - } else { - $this->returnedVerificationCode = $response->verificationCode; } } From 8106cd0455a2d6a67a231c08e12949ef4ac641f8 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 15:30:05 +0200 Subject: [PATCH 08/19] Update version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cdc4176..5862bf6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.2 +# Zitadel PHP API version 1.1.3 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From fa2c45a0ed14b90a45d72bb7bd7815c0e730f18c Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 29 Jul 2025 17:09:20 +0200 Subject: [PATCH 09/19] Remove a debug entry --- README.md | 2 +- src/User/Avatar.php | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5862bf6..6546208 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.3 +# Zitadel PHP API version 1.1.4 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). diff --git a/src/User/Avatar.php b/src/User/Avatar.php index ce2bd68..7041099 100644 --- a/src/User/Avatar.php +++ b/src/User/Avatar.php @@ -101,11 +101,8 @@ public function add() ), )); - // $response = json_decode(curl_exec($curl)); - $response = curl_exec($curl); + $response = json_decode(curl_exec($curl)); curl_close($curl); - - dump($response); exit; if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); From 2a08851bb997adad915aa049e195835c83fb8caf Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 31 Jul 2025 09:27:01 +0200 Subject: [PATCH 10/19] Fix a isVerified bug --- src/User/Create.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/User/Create.php b/src/User/Create.php index 2c45774..4803118 100644 --- a/src/User/Create.php +++ b/src/User/Create.php @@ -128,7 +128,9 @@ public function setGender(string $gender): void public function setEmail(string $email, bool $isVerified = false): void { $this->request["email"]["email"] = $email; - $this->request["email"]["isVerified"] = $isVerified; + if($isVerified) { + $this->request["email"]["isVerified"] = $isVerified; + } } /** @@ -141,7 +143,9 @@ public function setEmail(string $email, bool $isVerified = false): void public function setPhone(string $phone, bool $isVerified = false): void { $this->request["phone"]["phone"] = $phone; - $this->request["phone"]["isVerified"] = $isVerified; + if($isVerified) { + $this->request["phone"]["isVerified"] = $isVerified; + } } /** From 43956653ad7f9d7dccfbb21cdb3b56a4253b904f Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 31 Jul 2025 09:27:26 +0200 Subject: [PATCH 11/19] Update version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6546208..752e9b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.4 +# Zitadel PHP API version 1.1.5 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From e26f2b3e291c318a8e5b055f9adea648f8e6b955 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 31 Jul 2025 10:49:32 +0200 Subject: [PATCH 12/19] New function, typo fix --- src/Settings/PasswordComplexity.php | 10 +++---- src/User/Password.php | 42 ++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/Settings/PasswordComplexity.php b/src/Settings/PasswordComplexity.php index a207690..6f0587a 100644 --- a/src/Settings/PasswordComplexity.php +++ b/src/Settings/PasswordComplexity.php @@ -12,7 +12,7 @@ class PasswordComplexity protected array $settings; private ?string $orgId = null; private ?int $minLength; - private bool $rawPasswordSettings; + private array $rawPasswordSettings; private bool $requiresUppercase; private bool $requiresLowercase; private bool $requiresNumber; @@ -103,9 +103,9 @@ public function getResourceOwnerType(): string /** * Returns the raw password settings as a JSON string. * - * @return string The raw password settings as a JSON string. + * @return array The raw password settings as a JSON string. */ - public function getRawPasswoerdSettings(): string + public function getRawPasswordSettings(): array { return $this->rawPasswordSettings; } @@ -119,7 +119,7 @@ public function getRawPasswoerdSettings(): string public function sendRequest(): void { $token = $this->settings["serviceUserToken"]; - + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/settings/password/complexity?ctx.orgId=" . $this->orgId, @@ -144,7 +144,7 @@ public function sendRequest(): void throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); } else { $response = $response->settings; - $this->rawPasswordSettings = $response ?? ""; + $this->rawPasswordSettings = (array) $response ?? []; $this->minLength = $response->minLength ?? null; $this->requiresUppercase = $response->requiresUppercase ?? ""; $this->requiresLowercase = $response->requiresLowercase ?? ""; diff --git a/src/User/Password.php b/src/User/Password.php index 94ad3fb..bd1fb2f 100644 --- a/src/User/Password.php +++ b/src/User/Password.php @@ -128,7 +128,7 @@ public function change(): bool public function requestVerifyCode() { $token = $this->settings["serviceUserToken"]; - + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/password_reset", @@ -158,4 +158,44 @@ public function requestVerifyCode() $this->verifyCode = $response->verificationCode; } } + + /** + * Request reset link for password reset + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function sendResetLink() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/password_reset", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => "{ + \"sendLink\": { + \"notificationType\": \"NOTIFICATION_TYPE_Unspecified\" + } + }", + CURLOPT_HTTPHEADER => array( + "Content-Type: application/json", + "Accept: application/json", + "Authorization: Bearer $token" + ), + )); + + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + } } From a6319d1d174d0cd690c0d27ece218dba805ce8e7 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Thu, 31 Jul 2025 10:50:55 +0200 Subject: [PATCH 13/19] Update the readme version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 752e9b5..552b80c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.1.5 +# Zitadel PHP API version 1.2.0 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From 38116e78574c40f510223d11195d3c3852103a19 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Fri, 1 Aug 2025 15:53:23 +0200 Subject: [PATCH 14/19] Add new classes - Metadata nd Grants --- README.md | 2 +- src/User/Grants.php | 205 +++++++++++++++++++++++++++++++++++++++ src/User/Metadada.php | 217 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 src/User/Grants.php create mode 100644 src/User/Metadada.php diff --git a/README.md b/README.md index 552b80c..13c85dc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.2.0 +# Zitadel PHP API version 1.3.0 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). diff --git a/src/User/Grants.php b/src/User/Grants.php new file mode 100644 index 0000000..89f5a55 --- /dev/null +++ b/src/User/Grants.php @@ -0,0 +1,205 @@ +settings = $settings; + } + + /** + * Set the user id of the user + * + * @param $userid int The user id of the user + * @return void + */ + public function setUserId(int $userid) + { + $this->userid = $userid; + } + + /** + * Set the grant id of the users project + * + * @param $grantId string + * @return void + */ + public function setGrantId(string $grantId) + { + $this->grantId = $grantId; + } + + /** + * Set the project id for the request + * + * @param $projectId string + * @return void + */ + public function setProjectId(string $projectId) + { + $this->request["projectId"] = $projectId; + } + + /** + * Set the project grant id for the request + * Is needed if the user grant is for a granted project and the organization is not the owner of the project. + * + * @param $projectGrantId string + * @return void + */ + public function setProjectGrantId(string $projectGrantId) + { + $this->request["projectGrantId"] = $projectGrantId; + } + + /** + * Set the grant role + * + * @param $role string + * @return void + */ + public function setRoleKey(string $role) + { + $this->request["roleKeys"][] = $role; + } + + /** + * Set given roels to user in Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function grantRoles() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($this->request), + CURLOPT_HTTPHEADER => array( + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code) && $response->code != 6) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + if (isset($response->code) && $response->code == 6) { + $this->getGrantsByUserId(); + $this->updateGrantById(); + } + } + + /** + * Set given roels to user in Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function getGrantsByUserId() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/grants/_search", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => '{ + "queries": [ + { + "userIdQuery": { + "userId": "'.$this->userid.'" + } + } + ] + }', + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/json', + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + }else{ + foreach($response->result as $grant){ + if($grant->projectId == $this->request["projectId"]){ + $this->grantId = $response->result[0]->id; + } + } + } + } + + /** + * Set given roels to user in Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function updateGrantById() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants/$this->grantId", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'PUT', + CURLOPT_POSTFIELDS => json_encode($this->request), + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/json', + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code) && $response->code != 9) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + } +} \ No newline at end of file diff --git a/src/User/Metadada.php b/src/User/Metadada.php new file mode 100644 index 0000000..694e94b --- /dev/null +++ b/src/User/Metadada.php @@ -0,0 +1,217 @@ +settings = $settings; + } + + /** + * Set the user id of the user + * + * @param $userid int The user id of the user + * @return void + */ + public function setUserId(int $userid) + { + $this->userid = $userid; + } + + /** + * Returns the user metadata + * + * @return array + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function getMetadata() : array + { + return $this->metadata; + } + + /** + * Add Metadata to the user Profile (optional). The value will be automatically Base64 encoded. + * + * @param $key string Key + * @param $value string Value + * @return void + */ + public function addMetaData(string $key, string $value): void + { + $this->set_request["metadata"][] = [ + "key" => $key, + "value" => base64_encode($value) + ]; + } + + /** + * Add Metadata Key for deletion + * + * @param $key string Key + * @return void + */ + public function queryMetaDataKey(string $key): void + { + $this->delete_request["metadata"][] = [ + "key" => $key + ]; + } + + /** + * Add pagination to metadata list + * + * @param $offset int + * @param $limit int + * @param $asc bool + * @return void + */ + public function setPagination(int $offset = 0, int $limit = 0, bool $asc = false): void + { + if($limit > 0){ + $this->list_request["pagination"] = [ + "offset" => $offset, + "limit" => $limit, + "asc" => $asc + ]; + } + } + + /** + * Add filter to metadata list + * + * @param $key string + * @param $offset string + * @return void + */ + public function setFilters(string $key, string $method): void + { + $this->list_request["filters"][]["keyfilter"] = [ + "key" => $key, + "method" => $method, + ]; + } + + /** + * Requests the user metadata from Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function requestMetaData() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($this->list_request ?? ""), + CURLOPT_HTTPHEADER => array( + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + }else{ + $this->metadata = $response->metadata; + } + } + + /** + * Set & Change the user metadata in Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function setMetaData() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($this->set_request), + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/json', + 'Accept: application/json', + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + } + + /** + * Delete the user metadata from Zitadel + * + * @return void + * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails + */ + public function deleteMetaData() + { + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_POSTFIELDS => json_encode($this->delete_request), + CURLOPT_HTTPHEADER => array( + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code)) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + } +} \ No newline at end of file From 827ffdd9763d8e6c03cca00837bdc725841dce8b Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 5 Aug 2025 14:07:08 +0200 Subject: [PATCH 15/19] Update grants function and metadata --- src/User/Grants.php | 107 ++++++++++++++++++++++++++++-------------- src/User/Metadada.php | 50 ++++++++++++++------ 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/User/Grants.php b/src/User/Grants.php index 89f5a55..9bd117e 100644 --- a/src/User/Grants.php +++ b/src/User/Grants.php @@ -14,6 +14,8 @@ class Grants private int $userid; private string $grantId; private array $request; + private array $activeRoleKeys = []; + private array $removeRoleKeys = []; /** * Initialize the user data change. Important: To change the email address or the password, use the Email or Password Class! @@ -81,6 +83,17 @@ public function setRoleKey(string $role) $this->request["roleKeys"][] = $role; } + /** + * remove the grant role + * + * @param $role string + * @return void + */ + public function removeRoleKey(string $role) + { + $this->removeRoleKeys[] = $role; + } + /** * Set given roels to user in Zitadel * @@ -89,33 +102,51 @@ public function setRoleKey(string $role) */ public function grantRoles() { - $token = $this->settings["serviceUserToken"]; - - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => '', - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 0, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => 'POST', - CURLOPT_POSTFIELDS => json_encode($this->request), - CURLOPT_HTTPHEADER => array( - "Accept: application/json", - "Authorization: Bearer $token" - ) - )); - $response = json_decode(curl_exec($curl)); - curl_close($curl); + $roleKeyArray = []; - if (isset($response->code) && $response->code != 6) { - throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + // get all roles + $this->getGrantsByUserId(); + + // remove roles + foreach ($this->activeRoleKeys as $roleKey) { + if (!in_array($roleKey, $this->removeRoleKeys)) { + $roleKeyArray[] = $roleKey; + } } - if (isset($response->code) && $response->code == 6) { - $this->getGrantsByUserId(); - $this->updateGrantById(); + + // add new to existing roleKeys + $this->request["roleKeys"] = array_unique(array_merge($roleKeyArray, $this->request["roleKeys"] ?? [])); + + if (!empty($this->request["roleKeys"])) { + + // set roles for user + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($this->request), + CURLOPT_HTTPHEADER => array( + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code) && $response->code != 6) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + if (isset($response->code) && $response->code == 6) { + $this->updateGrantById(); + } } } @@ -128,9 +159,9 @@ public function grantRoles() public function getGrantsByUserId() { $token = $this->settings["serviceUserToken"]; - + $curl = curl_init(); - curl_setopt_array($curl, array( + curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/grants/_search", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', @@ -143,7 +174,7 @@ public function getGrantsByUserId() "queries": [ { "userIdQuery": { - "userId": "'.$this->userid.'" + "userId": "' . $this->userid . '" } } ] @@ -159,15 +190,21 @@ public function getGrantsByUserId() if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); - }else{ - foreach($response->result as $grant){ - if($grant->projectId == $this->request["projectId"]){ - $this->grantId = $response->result[0]->id; + } else { + if (isset($response->result)) { + foreach ($response->result as $grant) { + if ($grant->projectId == $this->request["projectId"]) { + $this->grantId = $grant->id; + + if (isset($grant->roleKeys)) { + $this->activeRoleKeys = $grant->roleKeys; + } + } } } } } - + /** * Set given roels to user in Zitadel * @@ -177,7 +214,7 @@ public function getGrantsByUserId() public function updateGrantById() { $token = $this->settings["serviceUserToken"]; - + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants/$this->grantId", @@ -202,4 +239,4 @@ public function updateGrantById() throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); } } -} \ No newline at end of file +} diff --git a/src/User/Metadada.php b/src/User/Metadada.php index 694e94b..125f402 100644 --- a/src/User/Metadada.php +++ b/src/User/Metadada.php @@ -43,7 +43,7 @@ public function setUserId(int $userid) * @return array * @throws Exception Returns an exception with an error code and a message if the communication with Zitadel fails */ - public function getMetadata() : array + public function getMetaData() : array { return $this->metadata; } @@ -71,9 +71,12 @@ public function addMetaData(string $key, string $value): void */ public function queryMetaDataKey(string $key): void { - $this->delete_request["metadata"][] = [ - "key" => $key - ]; + // API v2 + // $this->delete_request["metadata"][] = [ + // "key" => $key + // ]; + + $this->delete_request["keys"][] = $key; } /** @@ -87,7 +90,12 @@ public function queryMetaDataKey(string $key): void public function setPagination(int $offset = 0, int $limit = 0, bool $asc = false): void { if($limit > 0){ - $this->list_request["pagination"] = [ + // $this->list_request["pagination"] = [ + // "offset" => $offset, + // "limit" => $limit, + // "asc" => $asc + // ]; + $this->list_request["query"] = [ "offset" => $offset, "limit" => $limit, "asc" => $asc @@ -102,11 +110,19 @@ public function setPagination(int $offset = 0, int $limit = 0, bool $asc = false * @param $offset string * @return void */ - public function setFilters(string $key, string $method): void + public function setFilters(string $key, string $method = "TEXT_QUERY_METHOD_EQUALS"): void { - $this->list_request["filters"][]["keyfilter"] = [ - "key" => $key, - "method" => $method, + // $this->list_request["filters"][] = [ + // "keyFilter" => [ + // "key" => $key, + // "method" => $method, + // ] + // ]; + $this->list_request["queries"][] = [ + "keyQuery" => [ + "key" => $key, + "method" => $method, + ] ]; } @@ -119,10 +135,11 @@ public function setFilters(string $key, string $method): void public function requestMetaData() { $token = $this->settings["serviceUserToken"]; - + $curl = curl_init(); curl_setopt_array($curl, array( - CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + // CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/metadata/_search", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, @@ -130,7 +147,7 @@ public function requestMetaData() CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', - CURLOPT_POSTFIELDS => json_encode($this->list_request ?? ""), + CURLOPT_POSTFIELDS => (isset($this->list_request)) ? json_encode($this->list_request) : "{}", CURLOPT_HTTPHEADER => array( "Accept: application/json", "Authorization: Bearer $token" @@ -142,7 +159,8 @@ public function requestMetaData() if (isset($response->code)) { throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); }else{ - $this->metadata = $response->metadata; + // $this->metadata = $response->metadata; + $this->metadata = $response->result ?? []; } } @@ -158,7 +176,8 @@ public function setMetaData() $curl = curl_init(); curl_setopt_array($curl, array( - CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + // CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/metadata/_bulk", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, @@ -193,7 +212,8 @@ public function deleteMetaData() $curl = curl_init(); curl_setopt_array($curl, array( - CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + // CURLOPT_URL => $this->settings["domain"] . "/v2/users/$this->userid/metadata", + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/metadata/_bulk", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, From 1fbede126874e16eab114a2a411a408283cbbec6 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Tue, 5 Aug 2025 14:08:10 +0200 Subject: [PATCH 16/19] Update version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13c85dc..96ffcac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.3.0 +# Zitadel PHP API version 1.3.1 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). From 3d5d34268a57844a480e5d34f5b59d5cc983b168 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Wed, 6 Aug 2025 08:14:17 +0200 Subject: [PATCH 17/19] Fix a typo --- README.md | 2 +- src/User/{Metadada.php => Metadata.php} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/User/{Metadada.php => Metadata.php} (100%) diff --git a/README.md b/README.md index 96ffcac..87454cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.3.1 +# Zitadel PHP API version 1.3.2 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). diff --git a/src/User/Metadada.php b/src/User/Metadata.php similarity index 100% rename from src/User/Metadada.php rename to src/User/Metadata.php From b23824449ebec2f9ad20a0780c3388f755261961 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Fri, 3 Oct 2025 16:54:22 +0200 Subject: [PATCH 18/19] remove restriction for setting user roles in Zitadel --- README.md | 2 +- src/User/Grants.php | 57 +++++++++++++++++++++------------------------ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 87454cf..a5af8c4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zitadel PHP API version 1.3.2 +# Zitadel PHP API version 1.3.3 With this library you can easily communicate between your PHP projects and Zitadel over a serviceUser. The wrapper is based on the [Zitadel API](https://zitadel.com/docs/apis/introduction/). diff --git a/src/User/Grants.php b/src/User/Grants.php index 9bd117e..3918ad5 100644 --- a/src/User/Grants.php +++ b/src/User/Grants.php @@ -117,36 +117,33 @@ public function grantRoles() // add new to existing roleKeys $this->request["roleKeys"] = array_unique(array_merge($roleKeyArray, $this->request["roleKeys"] ?? [])); - if (!empty($this->request["roleKeys"])) { - - // set roles for user - $token = $this->settings["serviceUserToken"]; - - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => '', - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 0, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => 'POST', - CURLOPT_POSTFIELDS => json_encode($this->request), - CURLOPT_HTTPHEADER => array( - "Accept: application/json", - "Authorization: Bearer $token" - ) - )); - $response = json_decode(curl_exec($curl)); - curl_close($curl); - - if (isset($response->code) && $response->code != 6) { - throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); - } - if (isset($response->code) && $response->code == 6) { - $this->updateGrantById(); - } + // set roles for user + $token = $this->settings["serviceUserToken"]; + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $this->settings["domain"] . "/management/v1/users/$this->userid/grants", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($this->request), + CURLOPT_HTTPHEADER => array( + "Accept: application/json", + "Authorization: Bearer $token" + ) + )); + $response = json_decode(curl_exec($curl)); + curl_close($curl); + + if (isset($response->code) && $response->code != 6) { + throw new Exception("Error-Code: " . $response->code . " Message: " . $response->message); + } + if (isset($response->code) && $response->code == 6) { + $this->updateGrantById(); } } From ab887f6a40527bf03738d8b855cf5d30326bc450 Mon Sep 17 00:00:00 2001 From: Spelunky Date: Fri, 3 Oct 2025 17:01:25 +0200 Subject: [PATCH 19/19] v1.3.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5af8c4..c7376b6 100644 --- a/README.md +++ b/README.md @@ -133,4 +133,4 @@ try { ## License -Zitadel PHP API is released under the Apache 2.0 License. \ No newline at end of file +Zitadel PHP API is released under the Apache 2.0 License.