From e2ea284284cdb5bc4a0c734a704a250356471e3b Mon Sep 17 00:00:00 2001
From: Kevin J Gao <32936811+gaokevin1@users.noreply.github.com>
Date: Fri, 27 Dec 2024 15:17:02 -0800
Subject: [PATCH] added MD5 support

---
 src/SDK/Configuration/SDKConfig.php           |  7 ++++
 src/SDK/DescopeSDK.php                        | 31 +++++++-------
 .../Management/Password/UserPasswordMD5.php   | 41 +++++++++++++++++++
 src/SDK/Token/Extractor.php                   |  2 +-
 src/tests/Management/UserPwdTest.php          | 14 +++++++
 5 files changed, 79 insertions(+), 16 deletions(-)
 create mode 100644 src/SDK/Management/Password/UserPasswordMD5.php

diff --git a/src/SDK/Configuration/SDKConfig.php b/src/SDK/Configuration/SDKConfig.php
index 4877187..67396ea 100644
--- a/src/SDK/Configuration/SDKConfig.php
+++ b/src/SDK/Configuration/SDKConfig.php
@@ -47,6 +47,7 @@ private function fetchJWKSets(): array
     {
         try {
             $url = EndpointsV2::getPublicKeyPath() . '/' . $this->projectId;
+<<<<<<< Updated upstream
             $response = $this->client->request('GET', $url);
             $jwkSets = json_decode($response->getBody(), true);
 
@@ -54,6 +55,12 @@ private function fetchJWKSets(): array
                 throw new \Exception('Invalid JWK response');
             }
 
+=======
+            
+            // Fetch JWK public key from Descope API
+            $res = $this->client->request('GET', $url);
+            $jwkSets = json_decode($res->getBody(), true);
+>>>>>>> Stashed changes
             return $jwkSets;
         } catch (RequestException $e) {
             throw new \Exception('Failed to fetch JWK KeySet: ' . $e->getMessage());
diff --git a/src/SDK/DescopeSDK.php b/src/SDK/DescopeSDK.php
index 4a48677..2bf29c2 100644
--- a/src/SDK/DescopeSDK.php
+++ b/src/SDK/DescopeSDK.php
@@ -13,6 +13,7 @@
 use Descope\SDK\Auth\Management\Audit;
 use Descope\SDK\EndpointsV1;
 use Descope\SDK\EndpointsV2;
+use Descope\SDK\Exception\AuthException;
 
 use Descope\SDK\Management\MgmtV1;
 
@@ -62,10 +63,10 @@ public function __construct(array $config)
       */
     public function verify($sessionToken = null): bool
     {
-        $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME] ?? null;
+        $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::$SESSION_COOKIE_NAME] ?? null;
 
         if (!$sessionToken) {
-            throw new \InvalidArgumentException('Session token is required.');
+            throw new \InvalidArgumentException('Session token cannot be null or empty.');
         }
 
         $verifier = new Verifier($this->config, $this->api);
@@ -81,10 +82,10 @@ public function verify($sessionToken = null): bool
      */
     public function refreshSession($refreshToken = null): array
     {
-        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::$REFRESH_COOKIE_NAME] ?? null;
 
         if (empty($refreshToken)) {
-            throw new AuthException('Refresh token cannot be null or empty.');
+            throw new \InvalidArgumentException('Refresh token cannot be null or empty.');
         }
 
         try {
@@ -111,11 +112,11 @@ public function refreshSession($refreshToken = null): array
      */
     public function verifyAndRefreshSession($sessionToken = null, $refreshToken = null): array
     {
-        $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME] ?? null;
-        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+        $sessionToken = $sessionToken ?? $_COOKIE[EndpointsV1::$SESSION_COOKIE_NAME] ?? null;
+        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::$REFRESH_COOKIE_NAME] ?? null;
 
         if (empty($sessionToken) || empty($refreshToken)) {
-            throw new AuthException(400, 'Session or refresh token cannot be null or empty.');
+            throw new \InvalidArgumentException('Session or refresh token cannot be null or empty.');
         }
         
         try {
@@ -135,10 +136,10 @@ public function verifyAndRefreshSession($sessionToken = null, $refreshToken = nu
      */
     public function getClaims($token = null): array
     {
-        $token = $token ?? $_COOKIE[EndpointsV1::SESSION_COOKIE_NAME] ?? null;
+        $token = $token ?? $_COOKIE[EndpointsV1::$SESSION_COOKIE_NAME] ?? null;
 
         if (!$token) {
-            throw new \InvalidArgumentException('Token is required.');
+            throw new \InvalidArgumentException('Session token cannot be null or empty.');
         }
 
         $extractor = new Extractor($this->config);
@@ -154,10 +155,10 @@ public function getClaims($token = null): array
      */
     public function getUserDetails(string $refreshToken = null): array
     {
-        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::$REFRESH_COOKIE_NAME] ?? null;
 
         if (!$refreshToken) {
-            throw new \InvalidArgumentException('Refresh token is required.');
+            throw new \InvalidArgumentException('Refresh token cannot be null or empty.');
         }
 
         try {
@@ -182,10 +183,10 @@ public function getUserDetails(string $refreshToken = null): array
      */
     public function logout(string $refreshToken = null): void
     {
-        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::$REFRESH_COOKIE_NAME] ?? null;
 
         if (!$refreshToken) {
-            throw new \InvalidArgumentException('Refresh token is required.');
+            throw new \InvalidArgumentException('Refresh token cannot be null or empty.');
         }
 
         try {
@@ -212,10 +213,10 @@ public function logout(string $refreshToken = null): void
      */
     public function logoutAll(string $refreshToken = null): void
     {
-        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::REFRESH_COOKIE_NAME] ?? null;
+        $refreshToken = $refreshToken ?? $_COOKIE[EndpointsV1::$REFRESH_COOKIE_NAME] ?? null;
 
         if (!$refreshToken) {
-            throw new \InvalidArgumentException('Refresh token is required.');
+            throw new \InvalidArgumentException('Refresh token cannot be null or empty.');
         }
 
         try {
diff --git a/src/SDK/Management/Password/UserPasswordMD5.php b/src/SDK/Management/Password/UserPasswordMD5.php
new file mode 100644
index 0000000..a83ca3e
--- /dev/null
+++ b/src/SDK/Management/Password/UserPasswordMD5.php
@@ -0,0 +1,41 @@
+<?php
+// phpcs:ignoreFile
+
+declare(strict_types=1);
+
+namespace Descope\SDK\Management\Password;
+
+/**
+ * Class UserPasswordMD5
+ * 
+ * Represents a user password hashed using the MD5 hashing scheme.
+ * 
+ */
+class UserPasswordMD5
+{
+    public string $hash;
+
+    /**
+     * Constructor to initialize MD5 password details.
+     *
+     * @param string $hash The MD5 hash in plaintext format.
+     */
+    public function __construct(string $hash)
+    {
+        $this->hash = $hash;
+    }
+
+    /**
+     * Convert object data to an array format.
+     *
+     * @return array The password data as an associative array.
+     */
+    public function toArray(): array
+    {
+        return [
+            'md5' => [
+                'hash' => $this->hash,
+            ],
+        ];
+    }
+}
\ No newline at end of file
diff --git a/src/SDK/Token/Extractor.php b/src/SDK/Token/Extractor.php
index 3dc3edb..ad76a74 100644
--- a/src/SDK/Token/Extractor.php
+++ b/src/SDK/Token/Extractor.php
@@ -67,7 +67,7 @@ public function parseToken(string $sessionToken): array
 
     /**
      * Validate a JWT using the provided JWK Set.
-    */
+     */
     public function validateJWT(string $sessionToken): array
     {
         $useRefreshedKey = false;
diff --git a/src/tests/Management/UserPwdTest.php b/src/tests/Management/UserPwdTest.php
index ec6eeb6..aa3ace7 100644
--- a/src/tests/Management/UserPwdTest.php
+++ b/src/tests/Management/UserPwdTest.php
@@ -8,6 +8,7 @@
 use Descope\SDK\Management\Password\UserPasswordFirebase;
 use Descope\SDK\Management\Password\UserPasswordPbkdf2;
 use Descope\SDK\Management\Password\UserPasswordDjango;
+use Descope\SDK\Management\Password\UserPasswordMD5;
 
 class UserPwdTest extends TestCase
 {
@@ -81,6 +82,19 @@ public function testUserPasswordDjango()
         $this->assertEquals($expectedArray, $userPasswordDjango->toArray());
     }
 
+    public function testUserPasswordMD5()
+    {
+        $md5Hash = 'pbkdf2_sha256$30000$hashvalue';
+        $userPasswordMD5 = new UserPasswordMD5($md5Hash);
+        $expectedArray = [
+            'md5' => [
+                'hash' => $md5Hash,
+            ],
+        ];
+
+        $this->assertEquals($expectedArray, $userPasswordMD5->toArray());
+    }
+
     public function testUserPasswordWithCleartext()
     {
         $cleartextPassword = 'mypassword';