From efab5975987bb2b765619c4e40be91fde4822da0 Mon Sep 17 00:00:00 2001 From: Amit Date: Wed, 29 May 2024 17:12:38 +0530 Subject: [PATCH 01/21] chore: fixes in progress --- src/Guards/CognitoSessionGuard.php | 15 +++-- src/Guards/Traits/BaseCognitoGuard.php | 83 +++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/Guards/CognitoSessionGuard.php b/src/Guards/CognitoSessionGuard.php index 3912bd3..7f84100 100644 --- a/src/Guards/CognitoSessionGuard.php +++ b/src/Guards/CognitoSessionGuard.php @@ -316,13 +316,13 @@ public function attempt(array $credentials = [], $remember = false) /** - * Logout the user, thus invalidating the token. + * Logout the user, thus invalidating the session. * * @param bool $forceForever * * @return void */ - public function logout($forceForever = false) + public function logout(bool $forceForever = false) { $this->invalidate($forceForever); $this->user = null; @@ -338,6 +338,9 @@ public function logout($forceForever = false) */ public function invalidate($forceForever = false) { + //Return Value + $returnValue = null; + try { //Get authentication token from session $session = $this->getSession(); @@ -368,20 +371,22 @@ public function invalidate($forceForever = false) } //End if //Remove the token from application storage - return $session->invalidate(); + $returnValue = $session->invalidate(); } else { //Remove the token from application storage - return $session->invalidate(); + $returnValue = $session->invalidate(); } //End if } else { //Remove the token from application storage - return $session->invalidate(); + $returnValue = $session->invalidate(); } //End if } catch (Exception $e) { if ($forceForever) { return $session->invalidate(); } throw $e; } //try-catch ends + + return $returnValue; } //Function ends diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index 988cc0c..31dc00d 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -11,6 +11,15 @@ namespace Ellaisys\Cognito\Guards\Traits; +use Aws\Result as AwsResult; + + +use Ellaisys\Cognito\AwsCognito; +use Illuminate\Contracts\Auth\Authenticatable; +use Ellaisys\Cognito\AwsCognitoClient; +use Ellaisys\Cognito\AwsCognitoClientInterface; +use Ellaisys\Cognito\AwsCognitoClientManager; + /** * Trait Base Cognito Guard */ @@ -36,4 +45,76 @@ public function getRemoteUserData(string $username) { return $this->client->getUser($username); } //Function ends -} //Trait ends \ No newline at end of file + + /** + * Validate the user credentials with AWS Cognito + * + * @return \Ellaisys\Cognito\AwsCognitoClient + */ + protected function hasValidCredentials($credentials) { + try { + //Authenticate the user with AWS Cognito + $result = $this->client->authenticate($credentials['email'], $credentials['password']); + + //Check if the result is an instance of AwsResult + if (!empty($result) && $result instanceof AwsResult) { + //Set value into class param + $this->awsResult = $result; + + //Check in case of any challenge + if (isset($result['ChallengeName'])) { + $this->challengeName = $result['ChallengeName']; + $this->challengeData = $this->handleCognitoChallenge($result, $credentials['email']); + } //End if + } //End if + + return $result; + } catch (Exception $e) { + throw $e; + } //End try-catch + + } //Function ends + + + /** + * handle Cognito Challenge + */ + protected function handleCognitoChallenge(AwsResult $result, string $username) { + + //Return value + $returnValue = null; + + //Set challenge into class param + $this->challengeName = $result['ChallengeName']; + + switch ($result['ChallengeName']) { + case 'SOFTWARE_TOKEN_MFA': + $returnValue = [ + 'status' => $result['ChallengeName'], + 'session_token' => $result['Session'], + 'username' => $username, + 'user' => serialize($user) + ]; + break; + + case 'SMS_MFA': + $returnValue = [ + 'status' => $result['ChallengeName'], + 'session_token' => $result['Session'], + 'challenge_params' => $result['ChallengeParameters'], + 'username' => $username, + 'user' => serialize($user) + ]; + break; + + default: + if (in_array($result['ChallengeName'], config('cognito.forced_challenge_names'))) { + $returnValue = $result['ChallengeName']; + } //End if + break; + } //End switch + + return $returnValue; + } //Function ends + +} //Trait ends From 7832f1909e607ea4141b83c88a2426f094374abd Mon Sep 17 00:00:00 2001 From: Amit Date: Fri, 14 Jun 2024 02:29:37 +0530 Subject: [PATCH 02/21] refact: optimize the code --- config/cognito.php | 26 ++- src/Auth/AuthenticatesUsers.php | 21 +- src/AwsCognitoClaim.php | 51 +++- src/Guards/CognitoSessionGuard.php | 22 +- src/Guards/CognitoTokenGuard.php | 152 +++++------- src/Guards/Traits/BaseCognitoGuard.php | 243 +++++++++++++++++++- src/Providers/AwsCognitoServiceProvider.php | 12 +- 7 files changed, 380 insertions(+), 147 deletions(-) diff --git a/config/cognito.php b/config/cognito.php index 153ed44..2296042 100644 --- a/config/cognito.php +++ b/config/cognito.php @@ -55,6 +55,12 @@ | This option controls the default cognito fields that shall be needed to be | updated. The array value is a mapping with DB model or Request data. | + | DO NOT change the parameters on the left side of the array. They map to + | the AWS Cognito User Pool fields. + | + | The right side of the array is the DB model field, and you can set the + | value to null if you do not want to update the field. + | */ 'cognito_user_fields' => [ 'name' => 'name', @@ -64,12 +70,26 @@ 'nickname' => null, 'preferred_username' => null, 'email' => 'email', //Do Not set this parameter to null - 'phone_number' => 'phone', + 'phone_number' => null, 'gender' => null, 'birthdate' => null, 'locale' => null ], + + /* + |-------------------------------------------------------------------------- + | Cognito Subject UUID + |-------------------------------------------------------------------------- + | + | This option controls the default cognito subject UUID that shall be needed + | to be updated. The value has a mapping with DB model for identification of + | the local user. + | + */ + 'user_subject_uuid' => env('AWS_COGNITO_USER_SUBJECT_UUID', 'sub'), + + /* |-------------------------------------------------------------------------- | Cognito New User @@ -104,7 +124,7 @@ |-------------------------------------------------------------------------- | | This option controls the cognito MFA configuration for the assigned user. - | + | | | MFA_NONE, MFA_ENABLED | @@ -137,7 +157,7 @@ | */ 'add_missing_local_user' => env('AWS_COGNITO_ADD_LOCAL_USER', false), - 'delete_user' => env('AWS_COGNITO_DELETE_USER', false), + 'delete_user' => env('AWS_COGNITO_DELETE_USER', false), // Package configurations 'sso_user_model' => env('AWS_COGNITO_USER_MODEL', 'App\Models\User'), diff --git a/src/Auth/AuthenticatesUsers.php b/src/Auth/AuthenticatesUsers.php index 6bb72e2..de12fc7 100644 --- a/src/Auth/AuthenticatesUsers.php +++ b/src/Auth/AuthenticatesUsers.php @@ -92,30 +92,11 @@ protected function attemptLogin(Collection $request, string $guard='web', string throw new ValidationException($validator); } //End if - //Get the configuration fields - $userFields = config('cognito.cognito_user_fields'); - - //Get key fields - $keyUsername = $userFields['email']; - $keyPassword = 'password'; - $rememberMe = $request->has('remember')?$request['remember']:false; - - //Generate credentials array - $credentials = [ - $keyUsername => $request[$paramUsername], - $keyPassword => $request[$paramPassword] - ]; - //Authenticate User - $claim = Auth::guard($guard)->attempt($credentials, $rememberMe); + $claim = Auth::guard($guard)->attempt($request, $paramUsername, $paramPassword); } catch (NoLocalUserException $e) { Log::error('AuthenticatesUsers:attemptLogin:NoLocalUserException'); - $user = $this->createLocalUser($credentials, $keyPassword); - if ($user) { - return $user; - } //End if - return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); } catch (CognitoIdentityProviderException $e) { Log::error('AuthenticatesUsers:attemptLogin:CognitoIdentityProviderException'); diff --git a/src/AwsCognitoClaim.php b/src/AwsCognitoClaim.php index 4e093d3..615371f 100644 --- a/src/AwsCognitoClaim.php +++ b/src/AwsCognitoClaim.php @@ -52,6 +52,12 @@ class AwsCognitoClaim public $sub; + /** + * @var object + */ + public $tokenDecode; + + /** * Create a new JSON Web Token. * @@ -59,8 +65,7 @@ class AwsCognitoClaim * * @return void */ - public function __construct(AwsResult $result, Authenticatable $user=null, string $username) - { + public function __construct(AwsResult $result, Authenticatable $user=null) { try { $authResult = $result['AuthenticationResult']; if (!is_array($authResult)) { @@ -72,9 +77,13 @@ public function __construct(AwsResult $result, Authenticatable $user=null, strin $this->token = (string) (new AwsCognitoTokenValidator)->check($token); $this->data = $authResult; - $this->username = $username; + + //Decode the token + $decodedToken = (array) (new AwsCognitoTokenValidator)->decode($token); + $this->username = $decodedToken['username']; $this->user = $user; - $this->sub = $user['id']; + $this->sub = $decodedToken['sub']; + $this->tokenDecode = $decodedToken; } catch(Exception $e) { throw $e; @@ -87,7 +96,7 @@ public function __construct(AwsResult $result, Authenticatable $user=null, strin * * @return string */ - public function getToken() + public function getToken(): string { return $this->token; } //Function ends @@ -115,6 +124,27 @@ public function getUser() } //Function ends + /** + * Set the User. + * + */ + public function setUser(Authenticatable $user) + { + $this->user = $user; + } //Function ends + + + /** + * Get the Username. + * + * @return \string + */ + public function getUsername(): string + { + return $this->username; + } //Function ends + + /** * Get the Sub Data. * @@ -126,6 +156,17 @@ public function getSub() } //Function ends + /** + * Get the Decoded Token Data. + * + * @return mixed + */ + public function getDecodeToken() + { + return $this->tokenDecode; + } //Function ends + + /** * Get the token when casting to string. * diff --git a/src/Guards/CognitoSessionGuard.php b/src/Guards/CognitoSessionGuard.php index 7f84100..086addb 100644 --- a/src/Guards/CognitoSessionGuard.php +++ b/src/Guards/CognitoSessionGuard.php @@ -47,8 +47,8 @@ class CognitoSessionGuard extends SessionGuard implements StatefulGuard /** * Username key - * - * @var \string + * + * @var \string */ protected $keyUsername; @@ -65,6 +65,14 @@ class CognitoSessionGuard extends SessionGuard implements StatefulGuard * @var \Ellaisys\Cognito\AwsCognito */ protected $cognito; + + + /** + * The AwsCognito Claim token + * + * @var \Ellaisys\Cognito\AwsCognitoClaim|null + */ + protected $claim; /** @@ -80,19 +88,19 @@ class CognitoSessionGuard extends SessionGuard implements StatefulGuard /** - * @var Challenge Data based on + * @var Challenge Data based on the challenge */ protected $challengeData; /** * CognitoSessionGuard constructor. - * + * * @param string $name * @param AwsCognitoClient $client * @param UserProvider $provider * @param Session $session - * @param null|Request $request + * @param Request $request */ public function __construct( @@ -101,7 +109,7 @@ public function __construct( AwsCognitoClient $client, UserProvider $provider, Session $session, - ?Request $request = null, + Request $request, string $keyUsername = 'email' ) { $this->cognito = $cognito; @@ -180,7 +188,7 @@ public function attempt(array $credentials = [], $remember = false) { try { //Fire event for authenticating - $this->fireAttemptEvent($credentials, $remember); + $this->fireAttemptEvent($credentials, $credentials['remember']); //Get user from presisting store $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index 96ead76..e3dc2d2 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -14,6 +14,7 @@ use Aws\Result as AwsResult; use Illuminate\Http\Request; use Illuminate\Auth\TokenGuard; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\Authenticatable; @@ -27,6 +28,7 @@ use Exception; use Ellaisys\Cognito\Exceptions\NoLocalUserException; +use Ellaisys\Cognito\Exceptions\InvalidUserException; use Ellaisys\Cognito\Exceptions\InvalidUserModelException; use Ellaisys\Cognito\Exceptions\AwsCognitoException; use Symfony\Component\HttpKernel\Exception\HttpException; @@ -39,8 +41,8 @@ class CognitoTokenGuard extends TokenGuard /** * Username key - * - * @var \string + * + * @var \string */ protected $keyUsername; @@ -61,15 +63,33 @@ class CognitoTokenGuard extends TokenGuard /** * The AwsCognito Claim token - * + * * @var \Ellaisys\Cognito\AwsCognitoClaim|null */ protected $claim; + + + /** + * @var Authentication Challenge + */ + protected $challengeName; + + + /** + * @var AwsResult + */ + protected $awsResult; + + + /** + * @var Challenge Data based on the challenge + */ + protected $challengeData; /** * CognitoTokenGuard constructor. - * + * * @param $callback * @param AwsCognitoClient $client * @param Request $request @@ -77,10 +97,10 @@ class CognitoTokenGuard extends TokenGuard */ public function __construct( AwsCognito $cognito, - AwsCognitoClient $client, - Request $request, + AwsCognitoClient $client, + Request $request, UserProvider $provider = null, - string $keyUsername + string $keyUsername = null ) { $this->cognito = $cognito; $this->client = $client; @@ -90,84 +110,6 @@ public function __construct( } - /** - * @param mixed $user - * @param array $credentials - * @return bool - * @throws InvalidUserModelException - */ - protected function hasValidCredentials($user, array $credentials, bool $remember = false) - { - /** @var Result $response */ - $result = $this->client->authenticate($credentials[$this->keyUsername], $credentials['password']); - - //Result of type AWS Result - if (!empty($result) && $result instanceof AwsResult) { - - //Check in case of any challenge - if (isset($result['ChallengeName'])) { - switch ($result['ChallengeName']) { - case 'SOFTWARE_TOKEN_MFA': - $this->claim = [ - 'status' => $result['ChallengeName'], - 'session' => $result['Session'], - 'username' => $credentials[$this->keyUsername], - 'user' => serialize($user) - ]; - break; - - case 'SMS_MFA': - $this->claim = [ - 'status' => $result['ChallengeName'], - 'session' => $result['Session'], - 'challenge_params' => $result['ChallengeParameters'], - 'username' => $credentials[$this->keyUsername], - 'user' => serialize($user) - ]; - break; - - default: - if (in_array($result['ChallengeName'], config('cognito.forced_challenge_names'))) { - //Check for forced action on challenge status - if (config('cognito.force_password_change_api')) { - $this->claim = [ - 'session' => $result['Session'], - 'username' => $credentials[$this->keyUsername], - 'status' => $result['ChallengeName'] - ]; - } else { - if (config('cognito.force_password_auto_update_api')) { - //Force set password same as authenticated with challenge state - $this->client->confirmPassword($credentials[$this->keyUsername], $credentials['password'], $result['Session']); - - //Get the result object again - $result = $this->client->authenticate($credentials[$this->keyUsername], $credentials['password']); - - //Create claim token - $this->claim = new AwsCognitoClaim($result, $user, $credentials[$this->keyUsername]); - - if (empty($result)) { - return false; - } //End if - } else { - $this->claim = null; - } //End if - } //End if - } //End if - break; - } //End switch - } else { //Create Claim for confirmed users - //Create claim token - $this->claim = new AwsCognitoClaim($result, $user, $credentials[$this->keyUsername]); - } //End if - - return ($this->claim)?true:false; - } else { - return false; - } //End if - } //Function ends - - /** * Attempt to authenticate a user using the given credentials. * @@ -176,26 +118,34 @@ protected function hasValidCredentials($user, array $credentials, bool $remember * @throws * @return bool */ - public function attempt(array $credentials = [], bool $remember = false) + public function attempt(Collection $request, string $paramUsername='email', string $paramPassword='password', bool $remember = false) { + $returnValue = null; try { - $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); + //Build the payload + $payloadCognito = $this->buildCognitoPayload($request, $paramUsername, $paramPassword); - //Check if the user exists in local data store - if (!($user instanceof Authenticatable)) { - throw new NoLocalUserException(); - } //End if + //Check if the payload has valid AWS credentials + $responseCognito = collect($this->hasValidAWSCredentials($payloadCognito)); + if ($responseCognito && $this->claim) { + $credentials = collect([ + config('cognito.user_subject_uuid') => $this->claim->getSub() + ]); - if ($this->hasValidCredentials($user, $credentials)) { - return $this->login($user); - } //End if + //Check if the user exists + $this->lastAttempted = $user = $this->hasValidLocalCredentials($credentials); - return false; + //Login the user into the token guard + $returnValue = $this->login($user); + } else { + throw new InvalidUserException('Invalid AWS Cognito Credentials'); + } //End if } catch (NoLocalUserException $e) { Log::error('CognitoTokenGuard:attempt:NoLocalUserException:'); throw $e; } catch (CognitoIdentityProviderException $e) { Log::error('CognitoTokenGuard:attempt:CognitoIdentityProviderException:'.$e->getAwsErrorCode()); + $returnValue = $e->getAwsErrorCode(); //Set proper route if (!empty($e->getAwsErrorCode())) { @@ -214,7 +164,7 @@ public function attempt(array $credentials = [], bool $remember = false) break; } //End switch - return response()->json([ + $returnValue = response()->json([ 'error' => $errorCode, 'message' => $e->getAwsErrorMessage(), 'aws_error_code' => $e->getAwsErrorCode(), @@ -222,7 +172,7 @@ public function attempt(array $credentials = [], bool $remember = false) ], 400); } //End if - return $e->getAwsErrorCode(); + return $returnValue; } catch (AwsCognitoException $e) { Log::error('CognitoTokenGuard:attempt:AwsCognitoException:'. $e->getMessage()); throw $e; @@ -230,6 +180,8 @@ public function attempt(array $credentials = [], bool $remember = false) Log::error('CognitoTokenGuard:attempt:Exception:'.$e->getMessage()); throw $e; } //Try-catch ends + + return $returnValue; } //Function ends @@ -246,6 +198,8 @@ private function login($user) //Save the claim if it matches the Cognito Claim if ($this->claim instanceof AwsCognitoClaim) { + //Set User + $this->claim->setUser($user); //Set Token $this->setToken(); @@ -422,7 +376,7 @@ public function getUser (string $identifier) { /** * Attempt MFA based Authentication */ - public function attemptMFA(array $challenge = [], Authenticatable $user, bool $remember=false) { + public function attemptMFA(array $challenge=[], Authenticatable $user=null, bool $remember=false) { try { $response = $this->attemptBaseMFA($challenge, $user, $remember); //Result of type AWS Result @@ -431,7 +385,7 @@ public function attemptMFA(array $challenge = [], Authenticatable $user, bool $r //Handle the response as Aws Cognito Claim if ($response instanceof AwsCognitoClaim) { $this->claim = $response; - return $this->login($user); + return $this->login($user); } //End if //Handle if the object is a Aws Cognito Result diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index 31dc00d..de061c6 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -13,12 +13,16 @@ use Aws\Result as AwsResult; +use Illuminate\Support\Collection; +use Illuminate\Contracts\Auth\Authenticatable; use Ellaisys\Cognito\AwsCognito; -use Illuminate\Contracts\Auth\Authenticatable; +use Ellaisys\Cognito\AwsCognitoClaim; use Ellaisys\Cognito\AwsCognitoClient; use Ellaisys\Cognito\AwsCognitoClientInterface; use Ellaisys\Cognito\AwsCognitoClientManager; +use Ellaisys\Cognito\Exceptions\NoLocalUserException; +use Ellaisys\Cognito\Validators\AwsCognitoTokenValidator; /** * Trait Base Cognito Guard @@ -28,7 +32,7 @@ trait BaseCognitoGuard /** * Get the AWS Cognito object - * + * * @return \Ellaisys\Cognito\AwsCognito */ public function cognito() { @@ -38,7 +42,7 @@ public function cognito() { /** * Get the User Information from AWS Cognito - * + * * @return mixed */ public function getRemoteUserData(string $username) { @@ -46,12 +50,50 @@ public function getRemoteUserData(string $username) { } //Function ends + /** + * Set the User Information into the local DB + * + * @return mixed + */ + public function setLocalUserData(array $credentials) { + try { + //Get username key in the credentials + $keyUsername = config('cognito.cognito_user_fields.email', 'email'); + + //Get user from AWS Cognito + $remoteUser = $this->getRemoteUserData($credentials[$keyUsername]); + if (!empty($remoteUser)) { + //Get user from presisting store + $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); + } else { + throw new Exception('User not found in AWS Cognito'); + } + + + if (config('cognito.add_missing_local_user', false)) { + //Create user object from AWS Cognito + $user = []; + + //Create user into local DB + $this->provider->createUser($user); + } else { + return null; + } //End if + + } catch (Exception $e) { + throw new NoLocalUserException(); + } //End try-catch + + return $this->client->getUser($username); + } //Function ends + + /** * Validate the user credentials with AWS Cognito * * @return \Ellaisys\Cognito\AwsCognitoClient */ - protected function hasValidCredentials($credentials) { + protected function hasValidAWSCredentials(Collection $credentials) { try { //Authenticate the user with AWS Cognito $result = $this->client->authenticate($credentials['email'], $credentials['password']); @@ -60,11 +102,25 @@ protected function hasValidCredentials($credentials) { if (!empty($result) && $result instanceof AwsResult) { //Set value into class param $this->awsResult = $result; + + // //Handle Local User Object + // if (empty($user)) { + // if (config('cognito.add_missing_local_user')) { + // $user = $this->createLocalUser($result['UserAttributes']); + // } else { + // throw new NoLocalUserException(); + // } //End if + // } //End if //Check in case of any challenge if (isset($result['ChallengeName'])) { $this->challengeName = $result['ChallengeName']; $this->challengeData = $this->handleCognitoChallenge($result, $credentials['email']); + } elseif (isset($result['AuthenticationResult'])) { + //Create claim token + $this->claim = new AwsCognitoClaim($result, null); + } else { + $result = null; } //End if } //End if @@ -93,7 +149,7 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { 'status' => $result['ChallengeName'], 'session_token' => $result['Session'], 'username' => $username, - 'user' => serialize($user) + 'user' => serialize($this->user) ]; break; @@ -103,7 +159,7 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { 'session_token' => $result['Session'], 'challenge_params' => $result['ChallengeParameters'], 'username' => $username, - 'user' => serialize($user) + 'user' => serialize($this->user) ]; break; @@ -117,4 +173,179 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { return $returnValue; } //Function ends + + /** + * Build the payload array. + * + * @param \Illuminate\Http\Request $request + * @param string $paramUsername + * @param string $paramPassword + * @return array + */ + final public function buildCognitoPayload(Collection $request, $paramUsername='email', $paramPassword='password', bool $isCredential=false): Collection + { + $payload = []; + + try { + //Check if the request is an instance of Collection + if (!($request instanceof Collection)) { + throw new Exception('Request must be an instance of Collection'); + } //End if + + //Check if the request is empty + if ($request->isEmpty()) { + throw new Exception('Request must not be empty'); + } //End if + + //Get key fields + if ($isCredential) { + $rememberMe = $request->has('remember')?$request['remember']:false; + $payload = array_merge($payload, ['remember' => $rememberMe]); + } //End if + + //Get the configuration fields + $userFields = array_filter(config('cognito.cognito_user_fields'), function($value) { + return !empty($value); + }); + + if ($userFields) { + //Iterate all the keys in the request + $request->each(function($value, $key) use ($userFields, $paramUsername, $paramPassword, &$payload, $isCredential) { + switch ($key) { + case $paramUsername: + $payload = array_merge($payload, ['email' => $value]); + break; + + case $paramPassword: + $payload = array_merge($payload, ['password' => $value]); + break; + + default: + if ($isCredential && array_key_exists($key, $userFields)) { + $payload = array_merge($payload, [$key => $value]); + } + break; + } //Switch ends + }); + } //End if + } catch (Exception $e) { + throw $e; + } //End try-catch + + return collect($payload); + } //Function ends + + + /** + * Validate the user credentials with Local Data Store + * + * @return \Illuminate\Contracts\Auth\Authenticatable + */ + protected function hasValidLocalCredentials(Collection $credentials): Authenticatable { + try { + $user = $this->provider->retrieveByCredentials($credentials->toArray()); + + //Check if the user is not empty + if (empty($user) && !($user instanceof Authenticatable)) { + if (config('cognito.add_missing_local_user')) { + //Fetch user data from cognito + $userRemote = $this->getRemoteUserData($this->claim->getUsername()); + if (empty($userRemote)) { + throw new Exception('User not found in AWS Cognito'); + } //End if + + //Create user object from cognito data + $payloadUser = $this->buildLocalUserPayload(collect($userRemote['UserAttributes'])); + + //Create user into local DB + if ($this->createLocalUser($payloadUser->toArray())) { + $user = $this->provider->retrieveByCredentials($credentials->toArray()); + } //End if + } else { + throw new NoLocalUserException(); + } //End if + } //End if + + return $user; + } catch (NoLocalUserException | Exception $e) { + throw $e; + } //End try-catch + } //Function ends + + + /** + * Build the payload for Local DB + * + * @param \Illuminate\Http\Request $request + * @param string $paramUsername + * @param string $paramPassword + * @return array + */ + final public function buildLocalUserPayload(Collection $request): Collection + { + $payload = []; + + try { + //Check if the request is an instance of Collection + if (!($request instanceof Collection)) { + throw new Exception('Request must be an instance of Collection'); + } //End if + + //Check if the request is empty + if ($request->isEmpty()) { + throw new Exception('Request must not be empty'); + } //End if + + //Get the configuration fields + $userFields = array_filter(config('cognito.cognito_user_fields'), function($value) { + return !empty($value); + }); + + if ($userFields) { + //Iterate all the keys in the request + $request->each(function($value) use ($userFields, &$payload) { + if (array_key_exists($value['Name'], $userFields)) { + $payload = array_merge($payload, [$userFields[$value['Name']] => $value['Value']]); + } //End if + + //Add user subject if exists + if ($value['Name'] == 'sub') { + $payload = array_merge($payload, [config('cognito.user_subject_uuid') => $value['Value']]); + } //End if + }); + } //End if + + } catch (Exception $e) { + throw $e; + } //End try-catch + + return collect($payload); + } //Function ends + + + /** + * Create a local user if one does not exist. + * + * @param array $credentials + * @return mixed + */ + final public function createLocalUser(array $dataUser, string $keyPassword='password') + { + $user = null; + if (config('cognito.add_missing_local_user')) { + //Get user model from configuration + $userModel = config('cognito.sso_user_model'); + + //Remove password from credentials if exists + if (array_key_exists($keyPassword, $dataUser)) { + unset($dataUser[$keyPassword]); + } //End if + + //Create user into local DB, if not exists + $user = $userModel::updateOrCreate($dataUser); + } //End if + + return $user; + } //Function ends + } //Trait ends diff --git a/src/Providers/AwsCognitoServiceProvider.php b/src/Providers/AwsCognitoServiceProvider.php index f5082f1..c353e14 100644 --- a/src/Providers/AwsCognitoServiceProvider.php +++ b/src/Providers/AwsCognitoServiceProvider.php @@ -165,7 +165,7 @@ protected function registerCognitoFacades() ); }); - $this->app->singleton('ellaisys.aws.cognito', function (Application $app, array $config) { + $this->app->singleton('ellaisys.aws.cognito', function (Application $app) { return new AwsCognito( $app['ellaisys.aws.cognito.manager'], $app['ellaisys.aws.cognito.parser'] @@ -189,7 +189,7 @@ protected function registerCognitoFacades() */ protected function registerCognitoProvider() { - $this->app->singleton(AwsCognitoClient::class, function (Application $app) { + $this->app->singleton(AwsCognitoClient::class, function () { $aws_config = [ 'region' => config('cognito.region'), 'version' => config('cognito.version') @@ -202,15 +202,13 @@ protected function registerCognitoProvider() } //End if //Instancite the AWS Cognito Client - $client = new AwsCognitoClient( + return new AwsCognitoClient( new CognitoIdentityProviderClient($aws_config), config('cognito.app_client_id'), config('cognito.app_client_secret'), config('cognito.user_pool_id'), config('cognito.app_client_secret_allow', true) ); - - return $client; }); $this->app->singleton(AwsCognitoUserPool::class, function (Application $app) { @@ -230,7 +228,7 @@ protected function extendWebAuthGuard() $guard = new CognitoSessionGuard( $name, $app['ellaisys.aws.cognito'], - $client = $app->make(AwsCognitoClient::class), + $app->make(AwsCognitoClient::class), $app['auth']->createUserProvider($config['provider']), $app['session.store'], $app['request'] @@ -256,7 +254,7 @@ protected function extendApiAuthGuard() $guard = new CognitoTokenGuard( $app['ellaisys.aws.cognito'], - $client = $app->make(AwsCognitoClient::class), + $app->make(AwsCognitoClient::class), $app['request'], Auth::createUserProvider($config['provider']), config('cognito.cognito_user_fields.email', 'email') From 3a5201bc40f130fa4933c415f7b7fb5fdec55a4a Mon Sep 17 00:00:00 2001 From: Amit Date: Fri, 14 Jun 2024 14:54:00 +0530 Subject: [PATCH 03/21] fix: fix the code for reset password and mfa challenge --- src/Guards/CognitoTokenGuard.php | 26 +++++++++++++++---------- src/Guards/Traits/BaseCognitoGuard.php | 27 ++++++++++++++------------ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index e3dc2d2..a2e4f60 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -127,16 +127,22 @@ public function attempt(Collection $request, string $paramUsername='email', stri //Check if the payload has valid AWS credentials $responseCognito = collect($this->hasValidAWSCredentials($payloadCognito)); - if ($responseCognito && $this->claim) { - $credentials = collect([ - config('cognito.user_subject_uuid') => $this->claim->getSub() - ]); - - //Check if the user exists - $this->lastAttempted = $user = $this->hasValidLocalCredentials($credentials); - - //Login the user into the token guard - $returnValue = $this->login($user); + if ($responseCognito) { + if ($this->claim) { + $credentials = collect([ + config('cognito.user_subject_uuid') => $this->claim->getSub() + ]); + + //Check if the user exists + $this->lastAttempted = $user = $this->hasValidLocalCredentials($credentials); + + //Login the user into the token guard + $returnValue = $this->login($user); + } elseif ($this->challengeName) { + $returnValue = $this->challengeData; + } else { + throw new InvalidUserException('Invalid AWS Cognito Credentials'); + } //End if } else { throw new InvalidUserException('Invalid AWS Cognito Credentials'); } //End if diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index de061c6..f6578c7 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -95,6 +95,12 @@ public function setLocalUserData(array $credentials) { */ protected function hasValidAWSCredentials(Collection $credentials) { try { + //Reset global variables + $this->challengeName = null; + $this->challengeData = null; + $this->claim = null; + $this->awsResult = null; + //Authenticate the user with AWS Cognito $result = $this->client->authenticate($credentials['email'], $credentials['password']); @@ -103,15 +109,6 @@ protected function hasValidAWSCredentials(Collection $credentials) { //Set value into class param $this->awsResult = $result; - // //Handle Local User Object - // if (empty($user)) { - // if (config('cognito.add_missing_local_user')) { - // $user = $this->createLocalUser($result['UserAttributes']); - // } else { - // throw new NoLocalUserException(); - // } //End if - // } //End if - //Check in case of any challenge if (isset($result['ChallengeName'])) { $this->challengeName = $result['ChallengeName']; @@ -139,9 +136,6 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { //Return value $returnValue = null; - - //Set challenge into class param - $this->challengeName = $result['ChallengeName']; switch ($result['ChallengeName']) { case 'SOFTWARE_TOKEN_MFA': @@ -163,6 +157,15 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { ]; break; + case 'SELECT_MFA_TYPE': + $returnValue = [ + 'status' => $result['ChallengeName'], + 'session_token' => $result['Session'], + 'challenge_params' => $result['ChallengeParameters'], + 'username' => $username + ]; + break; + default: if (in_array($result['ChallengeName'], config('cognito.forced_challenge_names'))) { $returnValue = $result['ChallengeName']; From b404633d00b10b15c67c0c17df92d6c201e85862 Mon Sep 17 00:00:00 2001 From: Amit Date: Tue, 18 Jun 2024 20:14:36 +0530 Subject: [PATCH 04/21] chore: update the session guard code --- src/Auth/AuthenticatesUsers.php | 14 +- src/Guards/CognitoSessionGuard.php | 300 ++++++++++++------------- src/Guards/CognitoTokenGuard.php | 5 +- src/Guards/Traits/BaseCognitoGuard.php | 5 + 4 files changed, 163 insertions(+), 161 deletions(-) diff --git a/src/Auth/AuthenticatesUsers.php b/src/Auth/AuthenticatesUsers.php index de12fc7..d8707cd 100644 --- a/src/Auth/AuthenticatesUsers.php +++ b/src/Auth/AuthenticatesUsers.php @@ -75,9 +75,16 @@ protected function getAdminListGroupsForUser(string $username) * * @return mixed */ - protected function attemptLogin(Collection $request, string $guard='web', string $paramUsername='email', string $paramPassword='password', bool $isJsonResponse=false) + protected function attemptLogin(Request|Collection $request, string $guard='web', string $paramUsername='email', string $paramPassword='password', bool $isJsonResponse=false) { try { + $returnValue = null; + + //Convert request to collection + if ($request instanceof Request) { + $request = collect($request->all()); + } //End if + //Get the password policy $passwordPolicy = app()->make(AwsCognitoUserPool::class)->getPasswordPolicy(true); @@ -93,8 +100,7 @@ protected function attemptLogin(Collection $request, string $guard='web', string } //End if //Authenticate User - $claim = Auth::guard($guard)->attempt($request, $paramUsername, $paramPassword); - + $returnValue = Auth::guard($guard)->attempt($request->toArray(), false, $paramUsername, $paramPassword); } catch (NoLocalUserException $e) { Log::error('AuthenticatesUsers:attemptLogin:NoLocalUserException'); return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); @@ -106,7 +112,7 @@ protected function attemptLogin(Collection $request, string $guard='web', string return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); } //Try-catch ends - return $claim; + return $returnValue; } //Function ends diff --git a/src/Guards/CognitoSessionGuard.php b/src/Guards/CognitoSessionGuard.php index 086addb..80988d6 100644 --- a/src/Guards/CognitoSessionGuard.php +++ b/src/Guards/CognitoSessionGuard.php @@ -13,6 +13,7 @@ use Aws\Result as AwsResult; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Auth\SessionGuard; @@ -69,7 +70,7 @@ class CognitoSessionGuard extends SessionGuard implements StatefulGuard /** * The AwsCognito Claim token - * + * * @var \Ellaisys\Cognito\AwsCognitoClaim|null */ protected $claim; @@ -121,60 +122,6 @@ public function __construct( } - /** - * @param mixed $user - * @param array $credentials - * @return bool - * @throws InvalidUserModelException - */ - protected function hasValidCredentials($user, $credentials) - { - $result = $this->client->authenticate($credentials['email'], $credentials['password']); - - if (!empty($result) && $result instanceof AwsResult) { - //Set value into class param - $this->awsResult = $result; - - //Check in case of any challenge - if (isset($result['ChallengeName'])) { - - //Set challenge into class param - $this->challengeName = $result['ChallengeName']; - switch ($result['ChallengeName']) { - case 'SOFTWARE_TOKEN_MFA': - $this->challengeData = [ - 'status' => $result['ChallengeName'], - 'session_token' => $result['Session'], - 'username' => $credentials[$this->keyUsername], - 'user' => serialize($user) - ]; - break; - - case 'SMS_MFA': - $this->challengeData = [ - 'status' => $result['ChallengeName'], - 'session_token' => $result['Session'], - 'challenge_params' => $result['ChallengeParameters'], - 'username' => $credentials[$this->keyUsername], - 'user' => serialize($user) - ]; - break; - - default: - if (in_array($result['ChallengeName'], config('cognito.forced_challenge_names'))) { - $this->challengeName = $result['ChallengeName']; - } //End if - break; - } //End switch - } //End if - - return ($user instanceof Authenticatable)?true:false; - } //End if - - return false; - } //Function ends - - /** * Attempt to authenticate an existing user using the credentials * using Cognito @@ -184,102 +131,163 @@ protected function hasValidCredentials($user, $credentials) * @throws * @return bool */ - public function attempt(array $credentials = [], $remember = false) + public function attempt(array $credentials = [], $remember = false, string $paramUsername='email', string $paramPassword='password') { try { + $returnValue = false; + $user = null; + + //convert to collection + $request = collect($credentials); + + //Build the payload + $payloadCognito = $this->buildCognitoPayload($request, $paramUsername, $paramPassword); + //Fire event for authenticating - $this->fireAttemptEvent($credentials, $credentials['remember']); + $this->fireAttemptEvent($request->toArray(), $remember); - //Get user from presisting store - $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); + //Check if the payload has valid AWS credentials + $responseCognito = collect($this->hasValidAWSCredentials($payloadCognito)); + if ($responseCognito && (!empty($this->claim))) { + //Process the claim + if ($user = $this->processAWSClaim()) { + //Login user into the session + $this->login($user, $remember); - //Check if the user exists in local data store - if (empty($user) && !($user instanceof Authenticatable)) { - throw new NoLocalUserException(); - } //End if + //Fire successful attempt + $this->fireLoginEvent($user, true); - //Authenticate with cognito - if ($this->hasValidCredentials($user, $credentials)) { - if (!empty($this->challengeName)) { - switch ($this->challengeName) { - case 'SOFTWARE_TOKEN_MFA': - case 'SMS_MFA': - //Get Session and store details - $session = $this->getSession(); - $session->invalidate(); - $session->put($this->challengeData['session_token'], json_decode(json_encode($this->challengeData), true)); - - return redirect(route(config('cognito.force_mfa_code_route_name'), [ - 'session_token' => $this->challengeData['session_token'], - 'status' => $this->challengeData['status'], - ])) - ->with('success', true) - ->with('force', true) - ->with('messaage', $this->challengeName); - break; - - case AwsCognitoClient::NEW_PASSWORD_CHALLENGE: - case AwsCognitoClient::RESET_REQUIRED_PASSWORD: - $this->login($user, $remember); - - if (config('cognito.force_password_change_web', false)) { - return redirect(route(config('cognito.force_redirect_route_name'))) - ->with('success', true) - ->with('force', true) - ->with('messaage', $this->challengeName); - } //End if - break; - - default: - if (in_array($this->challengeName, config('cognito.forced_challenge_names'))) { - $this->challengeName = $result['ChallengeName']; - } //End if - break; - } //End switch - } else { - //Create Claim for confirmed users and store into session - if (!empty($this->awsResult)) { - //Create claim token - $claim = new AwsCognitoClaim($this->awsResult, $user, $credentials[$this->keyUsername]); - - //Get Session and store details - $session = $this->getSession(); - $session->invalidate(); - $session->put('claim', json_decode(json_encode($claim), true)); - - $this->login($user, $remember); - - //Fire successful attempt - $this->fireValidatedEvent($user); - $this->fireAuthenticatedEvent($user); - } else { - throw new HttpException(400, 'ERROR_AWS_COGNITO'); - } //End if + $returnValue = true; } //End if - - return true; + } elseif ($responseCognito && $this->challengeName) { + //Handle the challenge + $returnValue = $this->handleAWSChallenge(); + } else { + throw new HttpException(400, 'ERROR_AWS_COGNITO'); } //End if + } catch (CognitoIdentityProviderException $e) { + Log::error('CognitoSessionGuard:attempt:CognitoIdentityProviderException:'.$e->getAwsErrorCode()); - //Fire failed attempt - $this->fireFailedEvent($user, $credentials); + //Handle the exception + $returnValue = $this->handleCognitoException($e); + } catch (NoLocalUserException | AwsCognitoException | Exception $e) { + $exceptionClass = basename(str_replace('\\', DIRECTORY_SEPARATOR, get_class($e))); + $exceptionCode = $e->getCode(); + $exceptionMessage = $e->getMessage().':(code:'.$exceptionCode.', line:'.$e->getLine().')'; + if ($e instanceof CognitoIdentityProviderException) { + $exceptionCode = $e->getAwsErrorCode(); + $exceptionMessage = $e->getAwsErrorMessage().':'.$exceptionCode; + } //End if + Log::error('CognitoSessionGuard:attempt:'.$exceptionClass.':'.$exceptionMessage); - return false; - } catch (NoLocalUserException $e) { - Log::error('CognitoSessionGuard:attempt:NoLocalUserException:'.$e->getMessage()); + //Find SQL Exception + if (strpos($e->getMessage(), 'SQLSTATE') !== false) { + throw new DBConnectionException(); + } //End if //Fire failed attempt - $this->fireFailedEvent($user, $credentials); + if (!$returnValue) { + $this->fireFailedEvent($user, $request->toArray()); + } //End if throw $e; - } catch (CognitoIdentityProviderException $e) { - Log::error('CognitoSessionGuard:attempt:CognitoIdentityProviderException:'.$e->getAwsErrorCode()); + } //Try-catch ends + + return $returnValue; + } //Function ends - //Fire failed attempt - $this->fireFailedEvent($user, $credentials); + /** + * Process the AWS Claim and Authenticate the user with the local database + * + * @return Authenticatable + */ + private function processAWSClaim(): Authenticatable { + $credentials = collect([ + config('cognito.user_subject_uuid') => $this->claim->getSub() + ]); + + //Check if the user exists + $this->lastAttempted = $user = $this->hasValidLocalCredentials($credentials); + if (!empty($user) && ($user instanceof Authenticatable)) { + + //Save the user data into the claim + $this->claim->setUser($user); + + //Get Session and store details + $session = $this->getSession(); + $session->invalidate(); + $session->put('claim', json_decode(json_encode($this->claim), true)); + + //Fire successful attempt + $this->fireValidatedEvent($user); + $this->fireAuthenticatedEvent($user); + + return $user; + } else { + throw new NoLocalUserException(); + } //End if + } //Function ends + + + /** + * Handle the AWS Challenge + * + * @return mixed + */ + private function handleAWSChallenge() { + $returnValue = null; + + switch ($this->challengeName) { + case 'SOFTWARE_TOKEN_MFA': + case 'SMS_MFA': + //Get Session and store details + $session = $this->getSession(); + $session->invalidate(); + $session->put($this->challengeData['session_token'], json_decode(json_encode($this->challengeData), true)); + + $returnValue = redirect(route(config('cognito.force_mfa_code_route_name'), [ + 'session_token' => $this->challengeData['session_token'], + 'status' => $this->challengeData['status'], + ])) + ->with('success', true) + ->with('force', true) + ->with('messaage', $this->challengeName); + break; + + case AwsCognitoClient::NEW_PASSWORD_CHALLENGE: + case AwsCognitoClient::RESET_REQUIRED_PASSWORD: + $this->login($user, $remember); + + if (config('cognito.force_password_change_web', false)) { + $returnValue = redirect(route(config('cognito.force_redirect_route_name'))) + ->with('success', true) + ->with('force', true) + ->with('messaage', $this->challengeName); + } //End if + break; + + default: + if (in_array($this->challengeName, config('cognito.forced_challenge_names'))) { + $this->challengeName = $result['ChallengeName']; + } //End if + break; + } //End switch + + return $returnValue; + } //Funtion ends + + + /** + * Handle the AWS Cognito Exception + * + * @param CognitoIdentityProviderException $e + * @return mixed + */ + private function handleCognitoException(CognitoIdentityProviderException $e) { + if ($e instanceof CognitoIdentityProviderException) { //Set proper route if (!empty($e->getAwsErrorCode())) { - // sonarignore:start switch ($e->getAwsErrorCode()) { case 'PasswordResetRequiredException': return redirect(route('cognito.form.reset.password.code')) @@ -294,32 +302,12 @@ public function attempt(array $credentials = [], $remember = false) throw $e; break; } //End switch - // sonarignore:end } //End if return $e->getAwsErrorCode(); - } catch (AwsCognitoException $e) { - Log::error('CognitoSessionGuard:attempt:AwsCognitoException:'.$e->getMessage()); - - //Fire failed attempt - $this->fireFailedEvent($user, $credentials); - - throw $e; - } catch (Exception $e) { - Log::error('CognitoSessionGuard:attempt:Exception:'.$e->getMessage()); - - //Fire failed attempt - if (!empty($user)) { - $this->fireFailedEvent($user, $credentials); - } //End if - - //Find SQL Exception - if (strpos($e->getMessage(), 'SQLSTATE') !== false) { - throw new DBConnectionException(); - } //End if - - throw $e; - } //Try-catch ends + } else { + return $e->getAwsErrorCode(); + } //End if } //Function ends diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index a2e4f60..dc62268 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -118,10 +118,13 @@ public function __construct( * @throws * @return bool */ - public function attempt(Collection $request, string $paramUsername='email', string $paramPassword='password', bool $remember = false) + public function attempt(array $request = [], $remember = false, string $paramUsername='email', string $paramPassword='password') { $returnValue = null; try { + //convert to collection + $request = collect($request); + //Build the payload $payloadCognito = $this->buildCognitoPayload($request, $paramUsername, $paramPassword); diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index f6578c7..b8c6c83 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -14,6 +14,7 @@ use Aws\Result as AwsResult; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Illuminate\Contracts\Auth\Authenticatable; use Ellaisys\Cognito\AwsCognito; @@ -123,6 +124,7 @@ protected function hasValidAWSCredentials(Collection $credentials) { return $result; } catch (Exception $e) { + Log::error($e->getMessage()); throw $e; } //End try-catch @@ -232,6 +234,7 @@ final public function buildCognitoPayload(Collection $request, $paramUsername='e }); } //End if } catch (Exception $e) { + Log::error($e->getMessage()); throw $e; } //End try-catch @@ -271,6 +274,7 @@ protected function hasValidLocalCredentials(Collection $credentials): Authentica return $user; } catch (NoLocalUserException | Exception $e) { + Log::error($e->getMessage()); throw $e; } //End try-catch } //Function ends @@ -319,6 +323,7 @@ final public function buildLocalUserPayload(Collection $request): Collection } //End if } catch (Exception $e) { + Log::error($e->getMessage()); throw $e; } //End try-catch From d365ac4debd0b2e3d03578529b12168ef454f92f Mon Sep 17 00:00:00 2001 From: Amit Date: Mon, 24 Jun 2024 14:39:03 +0530 Subject: [PATCH 05/21] fix: update the code for MFA challenge --- src/Auth/AuthenticatesUsers.php | 4 +- src/Guards/CognitoSessionGuard.php | 45 ++++++--------- src/Guards/CognitoTokenGuard.php | 79 ++++++++++++++++++-------- src/Guards/Traits/BaseCognitoGuard.php | 13 +---- src/Guards/Traits/CognitoMFA.php | 36 +++++++----- 5 files changed, 96 insertions(+), 81 deletions(-) diff --git a/src/Auth/AuthenticatesUsers.php b/src/Auth/AuthenticatesUsers.php index d8707cd..22b2afe 100644 --- a/src/Auth/AuthenticatesUsers.php +++ b/src/Auth/AuthenticatesUsers.php @@ -151,7 +151,6 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe $sessionToken = request()->session()->get($challenge['session']); $username = $sessionToken['username']; $challenge['username'] = $username; - $user = unserialize($sessionToken['user']); } else{ throw new HttpException(400, 'ERROR_AWS_COGNITO_SESSION_MFA_CODE'); } //End if @@ -161,7 +160,6 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe $challengeData = Auth::guard($guard)->getChallengeData($challenge['session']); $username = $challengeData['username']; $challenge['username'] = $username; - $user = unserialize($challengeData['user']); break; default: @@ -170,7 +168,7 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe } //End switch //Authenticate User - $claim = Auth::guard($guard)->attemptMFA($challenge, $user); + $claim = Auth::guard($guard)->attemptMFA($challenge); } catch (NoLocalUserException $e) { Log::error('AuthenticatesUsers:attemptLoginMFA:NoLocalUserException'); diff --git a/src/Guards/CognitoSessionGuard.php b/src/Guards/CognitoSessionGuard.php index 80988d6..68f13e3 100644 --- a/src/Guards/CognitoSessionGuard.php +++ b/src/Guards/CognitoSessionGuard.php @@ -389,46 +389,33 @@ public function invalidate($forceForever = false) /** * Attempt MFA based Authentication */ - public function attemptMFA(array $challenge, Authenticatable $user, bool $remember=false) { + public function attemptMFA(array $challenge=[], bool $remember=false) { + $returnValue = false; try { - $claim = null; - - $response = $this->attemptBaseMFA($challenge, $user, $remember); - //Result of type AWS Result - if (!empty($response)) { - - //Handle the response as Aws Cognito Claim - if ($response instanceof AwsCognitoClaim) { - $claim = $response; - - //Get Session and store details - $session = $this->getSession(); - $session->forget($challenge['session']); - $session->put('claim', json_decode(json_encode($claim), true)); - + //Login with MFA Challenge + $responseCognito = $this->attemptBaseMFA($challenge, $remember); + if ($responseCognito && (!empty($this->claim))) { + //Process the claim + if ($user = $this->processAWSClaim()) { //Login user into the session $this->login($user, $remember); //Fire successful attempt - $this->fireValidatedEvent($user); - $this->fireAuthenticatedEvent($user); - - return true; - } //End if - - //Handle if the object is a Aws Cognito Result - if ($response instanceof AwsResult) { - //Check in case of any challenge - // if (isset($response['ChallengeName'])) { - - // } else { + $this->fireLoginEvent($user, true); - // } //End if + $returnValue = true; } //End if + } elseif ($responseCognito && $this->challengeName) { + //Handle the challenge + $returnValue = $this->handleAWSChallenge(); + } else { + throw new HttpException(400, 'ERROR_AWS_COGNITO'); } //End if } catch(Exception $e) { throw $e; } //Try-catch ends + + return $returnValue; } //Function ends } //Class ends diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index dc62268..b0d1bb6 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -142,6 +142,12 @@ public function attempt(array $request = [], $remember = false, string $paramUse //Login the user into the token guard $returnValue = $this->login($user); } elseif ($this->challengeName) { + //Get the key + $key = $this->challengeData['session_token']; + + //Save the challenge data + $this->setChallengeData($key); + $returnValue = $this->challengeData; } else { throw new InvalidUserException('Invalid AWS Cognito Credentials'); @@ -201,7 +207,7 @@ public function attempt(array $request = [], $remember = false, string $paramUse * * @return claim */ - private function login($user) + private function login(Authenticatable $user) { if (!empty($this->claim)) { @@ -212,11 +218,6 @@ private function login($user) //Set Token $this->setToken(); - } else { - $key = $this->claim['session']; - - //Save the challenge data - $this->setChallengeData($key, $user); } //End if //Set user @@ -272,9 +273,9 @@ public function getChallengeData(string $key) * * @return $this */ - public function setChallengeData(string $key, $user) + public function setChallengeData(string $key) { - $this->cognito->setChallengeData($key, $this->claim); + $this->cognito->setChallengeData($key, $this->challengeData); return $this; } //Function ends @@ -384,30 +385,60 @@ public function getUser (string $identifier) { /** * Attempt MFA based Authentication + * + * @param array $challenge + * @param bool $remember + * + * @throws + * + * @return bool */ - public function attemptMFA(array $challenge=[], Authenticatable $user=null, bool $remember=false) { + public function attemptMFA(array $challenge=[], bool $remember=false) { + $returnValue = null; try { - $response = $this->attemptBaseMFA($challenge, $user, $remember); - //Result of type AWS Result - if (!empty($response)) { - - //Handle the response as Aws Cognito Claim - if ($response instanceof AwsCognitoClaim) { - $this->claim = $response; - return $this->login($user); - } //End if + $responseCognito = $this->attemptBaseMFA($challenge, $remember); + if ($responseCognito) { + if ($this->claim) { + $credentials = collect([ + config('cognito.user_subject_uuid') => $this->claim->getSub() + ]); - //Handle if the object is a Aws Cognito Result - if ($response instanceof AwsResult) { - //Check in case of any challenge - if (isset($response['ChallengeName'])) { - //TODO: Handle challenge in MFA login - } //End if + //Check if the user exists + $this->lastAttempted = $user = $this->hasValidLocalCredentials($credentials); + + //Login the user into the token guard + $returnValue = $this->login($user); + } elseif ($this->challengeName) { + $returnValue = $this->challengeData; + } else { + throw new InvalidUserException('Invalid AWS Cognito Credentials'); } //End if + } else { + throw new InvalidUserException('Invalid AWS Cognito Credentials'); } //End if + + // //Result of type AWS Result + // if (!empty($response)) { + + // //Handle the response as Aws Cognito Claim + // if ($response instanceof AwsCognitoClaim) { + // $this->claim = $response; + // return $this->login($user); + // } //End if + + // //Handle if the object is a Aws Cognito Result + // if ($response instanceof AwsResult) { + // //Check in case of any challenge + // if (isset($response['ChallengeName'])) { + // //TODO: Handle challenge in MFA login + // } //End if + // } //End if + // } //End if } catch(Exception $e) { throw $e; } //Try-catch ends + + return $returnValue; } //Function ends } //Class ends diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index b8c6c83..0c3ab39 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -127,7 +127,6 @@ protected function hasValidAWSCredentials(Collection $credentials) { Log::error($e->getMessage()); throw $e; } //End try-catch - } //Function ends @@ -144,21 +143,11 @@ protected function handleCognitoChallenge(AwsResult $result, string $username) { $returnValue = [ 'status' => $result['ChallengeName'], 'session_token' => $result['Session'], - 'username' => $username, - 'user' => serialize($this->user) + 'username' => $username ]; break; case 'SMS_MFA': - $returnValue = [ - 'status' => $result['ChallengeName'], - 'session_token' => $result['Session'], - 'challenge_params' => $result['ChallengeParameters'], - 'username' => $username, - 'user' => serialize($this->user) - ]; - break; - case 'SELECT_MFA_TYPE': $returnValue = [ 'status' => $result['ChallengeName'], diff --git a/src/Guards/Traits/CognitoMFA.php b/src/Guards/Traits/CognitoMFA.php index 86c854c..100f953 100644 --- a/src/Guards/Traits/CognitoMFA.php +++ b/src/Guards/Traits/CognitoMFA.php @@ -34,31 +34,41 @@ trait CognitoMFA /** * Attempt MFA based Authentication */ - public function attemptBaseMFA(array $challenge = [], Authenticatable $user, bool $remember=false) { + public function attemptBaseMFA(array $challenge = [], bool $remember=false) { try { - $claim = null; + //Reset global variables + $this->challengeName = null; + $this->challengeData = null; + $this->claim = null; + $this->awsResult = null; $challengeName = $challenge['challenge_name']; $session = $challenge['session']; $challengeValue = $challenge['mfa_code']; $username = $challenge['username']; + //Attempt MFA Challenge $result = $this->client->authMFAChallenge($challengeName, $session, $challengeValue, $username); - //Result of type AWS Result + + //Check if the result is an instance of AwsResult if (!empty($result) && $result instanceof AwsResult) { + //Set value into class param + $this->awsResult = $result; + //Check in case of any challenge - if (isset($result['ChallengeName'])) { - return $result; - } else { + if (isset($result['ChallengeName'])) { + $this->challengeName = $result['ChallengeName']; + $this->challengeData = $this->handleCognitoChallenge($result, $username); + } elseif (isset($result['AuthenticationResult'])) { //Create claim token - return new AwsCognitoClaim($result, $user, $username); + $this->claim = new AwsCognitoClaim($result, null); + } else { + throw new HttpException(400, 'ERROR_AWS_COGNITO_MFA_CODE_NOT_PROPER'); } //End if - } else { - throw new HttpException(400, 'ERROR_AWS_COGNITO_MFA_CODE_NOT_PROPER'); } //End if - } catch(CognitoIdentityProviderException $e) { - throw $e; - } catch(Exception $e) { + + return $result; + } catch(CognitoIdentityProviderException | Exception $e) { throw $e; } //Try-catch ends } //Function ends @@ -66,7 +76,7 @@ public function attemptBaseMFA(array $challenge = [], Authenticatable $user, boo /** * Associate the MFA Software Token - * + * * @param string $appName (optional) * * @return array From 994f4edba1aa6ba68e5ed16714894fd12369934f Mon Sep 17 00:00:00 2001 From: Amit Date: Tue, 25 Jun 2024 17:28:49 +0530 Subject: [PATCH 06/21] doc: update the readme and changelog --- CHANGELOG.md | 5 +++++ README.md | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e61a75d..fac227c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +Release 40 (tag v1.3.0) + - Feat: Issue #50, Architecture change to map the local and cognito users with sub (SubjectId) + - Fix: Issue #86, SSO enabled the user is now created for both guards + - Fix: Code optimization + Release 39 (tag v1.2.5) - Fix: AWS JWT Token validation timeout - Fix: Non declared variable references diff --git a/README.md b/README.md index 933354b..779991e 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ The idea of this package, and some of the code, is based on the package from Pod We decided to use it and contribute it to the community as a package, that encourages standarised use and a RAD tool for authentication using AWS Cognito. ## Features -- [Registration and Confirmation E-Mail (Sign Up)](#registering-users) **Updated** (#9 feature added) +- [Registration and Confirmation E-Mail (Sign Up)](#registering-users) - Forced password change at first login (configurable) - [Login (Sign In)](#user-authentication) -- Token Validation for all Session and Token Guard Requests **New** +- Token Validation for all Session and Token Guard Requests - Remember Me Cookie -- Single Sign On +- Single Sign On **Updated** (Fix: Issue #86) - Forgot Password (Resend - configurable) - User Deletion - Edit User Attributes @@ -41,6 +41,7 @@ We decided to use it and contribute it to the community as a package, that encou - [Forced Logout (Sign Out) - Revoke the RefreshToken from AWS](#signout-remove-access-token) - [MFA Implementation for Session and Token Guards](./README_MFA.md) - [Password validation based on Cognito Configuration](#password-validation-based-of-cognito-configuration) +- [Mapping Cognito User using Subject UUID](#mapping-cognito-user-using-subject-uuid) **NEW** ## Compatability @@ -197,7 +198,6 @@ At the current state you need to have those 4 form fields defined in here. Those With our package and AWS Cognito we provide you a simple way to use Single Sign-Ons. For configuration options take a look at the config [cognito.php](/config/cognito.php). - When you want SSO enabled and a user tries to login into your application, the package checks if the user exists in your AWS Cognito pool. If the user exists, he will be created automatically in your database provided the `add_missing_local_user` is to `true`, and is logged in simultaneously. That's what we use the fields `sso_user_model` and `cognito_user_fields` for. In `sso_user_model` you define the class of your user model. In most cases this will simply be _App\Models\User_. @@ -626,6 +626,20 @@ This library fetches the password policy from the cognito pool configurations. T >[!IMPORTANT] >In case of special characters, we are supporting all except the pipe character **|** for now. +## Mapping Cognito User using Subject UUID + +The library maps the Cognito user subject UUID with the local repository. Everytime a new user is created in cognito, the sub UUID is mapped with the local user table with an user specified column name. + +The column in the local BD is identified with the config parameter `user_subject_uuid` with the default value set to `sub`. + +However, to customize the column name in the local DB user table, you may do that with below setting fields to your `.env` file + +```php + + AWS_COGNITO_USER_SUBJECT_UUID="sub" + +``` + We are working on making sure that pipe character is handled soon. ## Changelog From 6ec91e28edc0ca0fe7746224a2227367caa03ac0 Mon Sep 17 00:00:00 2001 From: Amit Date: Tue, 25 Jun 2024 17:29:16 +0530 Subject: [PATCH 07/21] chore: update the config description --- config/cognito.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/cognito.php b/config/cognito.php index 2296042..96fee6d 100644 --- a/config/cognito.php +++ b/config/cognito.php @@ -83,8 +83,8 @@ |-------------------------------------------------------------------------- | | This option controls the default cognito subject UUID that shall be needed - | to be updated. The value has a mapping with DB model for identification of - | the local user. + | to be updated based on your local DB schema. This value is the attribute + | in the local DB Model that maps with Cognito user subject UUID. | */ 'user_subject_uuid' => env('AWS_COGNITO_USER_SUBJECT_UUID', 'sub'), From 909df1605cb171db658ffbbc4d9013583a274e52 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 15:25:18 +0530 Subject: [PATCH 08/21] Fix: Handle NoLocalUserExeption --- src/Auth/AuthenticatesUsers.php | 76 +++++++++---------------- src/Exceptions/NoLocalUserException.php | 29 ++-------- src/Guards/CognitoTokenGuard.php | 2 +- src/Guards/Traits/BaseCognitoGuard.php | 5 +- 4 files changed, 35 insertions(+), 77 deletions(-) diff --git a/src/Auth/AuthenticatesUsers.php b/src/Auth/AuthenticatesUsers.php index 22b2afe..d78be54 100644 --- a/src/Auth/AuthenticatesUsers.php +++ b/src/Auth/AuthenticatesUsers.php @@ -101,15 +101,25 @@ protected function attemptLogin(Request|Collection $request, string $guard='web' //Authenticate User $returnValue = Auth::guard($guard)->attempt($request->toArray(), false, $paramUsername, $paramPassword); - } catch (NoLocalUserException $e) { - Log::error('AuthenticatesUsers:attemptLogin:NoLocalUserException'); - return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); - } catch (CognitoIdentityProviderException $e) { - Log::error('AuthenticatesUsers:attemptLogin:CognitoIdentityProviderException'); - return $this->sendFailedCognitoResponse($e, $isJsonResponse, $paramUsername); - } catch (Exception $e) { - Log::error('AuthenticatesUsers:attemptLogin:Exception'); - return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); + } catch (NoLocalUserException | CognitoIdentityProviderException | Exception $e) { + $exceptionClass = basename(str_replace('\\', DIRECTORY_SEPARATOR, get_class($e))); + $exceptionCode = $e->getCode(); + $exceptionMessage = $e->getMessage().':(code:'.$exceptionCode.', line:'.$e->getLine().')'; + if ($e instanceof CognitoIdentityProviderException) { + $exceptionCode = $e->getAwsErrorCode(); + $exceptionMessage = $e->getAwsErrorMessage().':'.$exceptionCode; + } //End if + Log::error('AuthenticatesUsers:attemptLogin:'.$exceptionClass.':'.$exceptionMessage); + + if ($e instanceof ValidationException) { + throw $e; + } //End if + + if ($e instanceof CognitoIdentityProviderException) { + $this->sendFailedCognitoResponse($e, $isJsonResponse, $paramUsername); + } + + $returnValue = $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); } //Try-catch ends return $returnValue; @@ -143,7 +153,6 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe $challenge = $request->only(['challenge_name', 'session', 'mfa_code'])->toArray(); //Fetch user details - $user = null; switch ($guard) { case 'web': //Web if (request()->session()->has($challenge['session'])) { @@ -163,7 +172,6 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe break; default: - $user = null; break; } //End switch @@ -171,17 +179,10 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe $claim = Auth::guard($guard)->attemptMFA($challenge); } catch (NoLocalUserException $e) { Log::error('AuthenticatesUsers:attemptLoginMFA:NoLocalUserException'); - - $response = $this->createLocalUser($user->toArray()); - if ($response) { - return $response; - } //End if - return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramUsername); } catch (CognitoIdentityProviderException $e) { Log::error('AuthenticatesUsers:attemptLoginMFA:CognitoIdentityProviderException'); return $this->sendFailedLoginResponse($request, $e, $isJsonResponse, $paramName); - } catch (Exception $e) { Log::error('AuthenticatesUsers:attemptLoginMFA:Exception'); Log::error($e); @@ -201,32 +202,6 @@ protected function attemptLoginMFA($request, string $guard='web', bool $isJsonRe } //Function ends - /** - * Create a local user if one does not exist. - * - * @param array $credentials - * @return mixed - */ - protected function createLocalUser(array $dataUser, string $keyPassword='password') - { - $user = null; - if (config('cognito.add_missing_local_user')) { - //Get user model from configuration - $userModel = config('cognito.sso_user_model'); - - //Remove password from credentials if exists - if (array_key_exists($keyPassword, $dataUser)) { - unset($dataUser[$keyPassword]); - } //End if - - //Create user - $user = $userModel::create($dataUser); - } //End if - - return $user; - } //Function ends - - /** * Handle Failed Cognito Exception * @@ -248,33 +223,34 @@ private function sendFailedCognitoResponse(CognitoIdentityProviderException $exc */ private function sendFailedLoginResponse($request, $exception=null, bool $isJsonResponse=false, string $paramName='email') { - $errorCode = 'cognito.validation.auth.failed'; + $errorCode = 400; + $errorMessageCode = 'cognito.validation.auth.failed'; $message = 'FailedLoginResponse'; if (!empty($exception)) { if ($exception instanceof CognitoIdentityProviderException) { - $errorCode = $exception->getAwsErrorCode(); + $errorMessageCode = $exception->getAwsErrorCode(); $message = $exception->getAwsErrorMessage(); } elseif ($exception instanceof ValidationException) { throw $exception; } else { + $errorCode = $exception->getStatusCode(); $message = $exception->getMessage(); } //End if } //End if if ($isJsonResponse) { return response()->json([ - 'error' => $errorCode, + 'error' => $errorMessageCode, 'message' => $message - ], 400); + ], $errorCode); } else { return redirect() ->back() ->withErrors([ + 'error' => $errorMessageCode, $paramName => $message, ]); } //End if - - throw new HttpException(400, $message); } //Function ends diff --git a/src/Exceptions/NoLocalUserException.php b/src/Exceptions/NoLocalUserException.php index 6db2b81..35edfc7 100644 --- a/src/Exceptions/NoLocalUserException.php +++ b/src/Exceptions/NoLocalUserException.php @@ -5,31 +5,12 @@ use Throwable; use Exception; -use Illuminate\Database\Eloquent\ModelNotFoundException; +use Symfony\Component\HttpKernel\Exception\HttpException; -class NoLocalUserException extends Exception +class NoLocalUserException extends HttpException { - /** - * Report the exception. - * - * @return void - */ - public function report() + public function __construct(string $message = 'User does not exist locally.', Throwable $previous = null, int $code = 0, array $headers = []) { - throw new ModelNotFoundException(); + parent::__construct(401, $message, $previous, $headers, $code); } - - - /** - * Render the exception into an HTTP response. - * - * @param \Illuminate\Http\Request $request - * @param \Throwable $exception - * @return \Illuminate\Http\Response - */ - public function render($request, Throwable $exception) - { - return parent::render($request, $exception); - } - -} //Class ends \ No newline at end of file +} //Class ends diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index b0d1bb6..8ebab1c 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -156,7 +156,7 @@ public function attempt(array $request = [], $remember = false, string $paramUse throw new InvalidUserException('Invalid AWS Cognito Credentials'); } //End if } catch (NoLocalUserException $e) { - Log::error('CognitoTokenGuard:attempt:NoLocalUserException:'); + Log::error('CognitoTokenGuard:attempt:NoLocalUserException:'.$e->getMessage()); throw $e; } catch (CognitoIdentityProviderException $e) { Log::error('CognitoTokenGuard:attempt:CognitoIdentityProviderException:'.$e->getAwsErrorCode()); diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index 0c3ab39..1570fd4 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -70,7 +70,7 @@ public function setLocalUserData(array $credentials) { throw new Exception('User not found in AWS Cognito'); } - + //Check if the user is not empty if (config('cognito.add_missing_local_user', false)) { //Create user object from AWS Cognito $user = []; @@ -82,6 +82,7 @@ public function setLocalUserData(array $credentials) { } //End if } catch (Exception $e) { + Log::debug('BaseCognitoGuard:setLocalUserData:Exception:'); throw new NoLocalUserException(); } //End try-catch @@ -263,7 +264,7 @@ protected function hasValidLocalCredentials(Collection $credentials): Authentica return $user; } catch (NoLocalUserException | Exception $e) { - Log::error($e->getMessage()); + Log::debug('BaseCognitoGuard:setLocalUserData:Exception'); throw $e; } //End try-catch } //Function ends From e32ace312750684c8c777558593495e699cd1b14 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 15:37:32 +0530 Subject: [PATCH 09/21] fix: validation error to be converted to error --- src/Auth/AuthenticatesUsers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Auth/AuthenticatesUsers.php b/src/Auth/AuthenticatesUsers.php index d78be54..40cd7be 100644 --- a/src/Auth/AuthenticatesUsers.php +++ b/src/Auth/AuthenticatesUsers.php @@ -53,7 +53,7 @@ protected function getAdminListGroupsForUser(string $username) foreach ($groups as $key => &$value) { unset($value['UserPoolId']); unset($value['RoleArn']); - } //Loop ends + } //Loop ends } //End if } //End if } catch(Exception $e) { @@ -95,7 +95,7 @@ protected function attemptLogin(Request|Collection $request, string $guard='web' 'regex' => 'Must contain atleast ' . $passwordPolicy['message'] ]); if ($validator->fails()) { - Log::info($validator->errors()); + Log::error($validator->errors()); throw new ValidationException($validator); } //End if From e1c568e5a84801d7e0c8d88bbd868863a23d0559 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 16:16:04 +0530 Subject: [PATCH 10/21] chore: update the key words in package json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6beb71c..e6a9000 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "ellaisys/aws-cognito", "description": "AWS Cognito package that allows Auth and other related features using the AWS SDK for PHP", - "keywords": ["php", "laravel", "aws", "cognito", "auth", "authentication", "oauth", "user pool", "ellaisys"], + "keywords": ["php", "laravel", "aws", "cognito", "auth", "authentication", "oauth", "user pool", "ellaisys", "mfa", "multi-factor authentication", "2fa", "two-factor authentication", "password", "reset", "forgot", "change", "update", "email", "phone", "sms", "email verification", "phone verification", "sms verification", "email confirmation", "phone confirmation", "sms confirmation", "email code", "phone code", "sms code", "email token", "phone token", "sms token", "email password", "phone password", "sms password", "email recovery", "phone recovery", "sms recovery", "email reset", "phone reset", "sms reset", "email forgot", "phone forgot", "sms forgot", "email change", "phone change", "sms change", "email update", "phone update", "sms update", "email verify", "phone verify", "sms verify", "email confirm", "phone confirm", "sms confirm", "email code verification", "phone code verification", "sms code verification", "email token verification", "phone token verification", "sms token verification", "email password reset", "phone password reset", "sms password reset", "email recovery password", "phone recovery password", "sms recovery password", "email reset password", "phone reset password", "sms reset password", "email forgot password", "phone forgot password", "sms forgot password", "email change password", "phone change password", "sms change password", "email update password", "phone update password", "sms update password", "email verify code", "phone verify code", "sms verify code", "email confirm code", "phone confirm code", "sms confirm code", "email code verify", "phone code verify", "sms code verify", "email token verify", "phone token verify", "sms token verify", "email password reset code", "phone password reset code", "sms password reset code", "email recovery password code", "phone recovery password code", "sms recovery password code", "email reset password code", "phone reset password code", "sms reset password code", "email forgot password code", "phone forgot password code", "sms forgot password code", "email change password code", "phone change password code", "sms change password code", "email update password code", "phone update password code", "sms update password code", "email verify code verification", "phone verify code verification", "sms verify code verification", "email confirm code verification", "phone confirm code verification", "sms confirm code verification", "email code verify verification", "phone code verify verification", "sms"], "type": "library", "license": "MIT", "homepage": "https://ellaisys.github.io/aws-cognito/", From 56f30b106202185f09df12630c0f0b680cc5d6c2 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 16:29:38 +0530 Subject: [PATCH 11/21] fix: sonar cloud optimization --- src/Exceptions/InvalidUserException.php | 2 +- src/Guards/Traits/BaseCognitoGuard.php | 151 ++++++++++-------------- 2 files changed, 62 insertions(+), 91 deletions(-) diff --git a/src/Exceptions/InvalidUserException.php b/src/Exceptions/InvalidUserException.php index 35e1838..cd3e672 100644 --- a/src/Exceptions/InvalidUserException.php +++ b/src/Exceptions/InvalidUserException.php @@ -10,7 +10,7 @@ class InvalidUserException extends HttpException { - public function __construct(string $message = '', \Throwable $previous = null, int $code = 0, array $headers = []) + public function __construct(string $message = 'Invalid Cognito User', \Throwable $previous = null, int $code = 0, array $headers = []) { parent::__construct(400, $message, $previous, $headers, $code); } diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index 1570fd4..e5a358c 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -23,6 +23,7 @@ use Ellaisys\Cognito\AwsCognitoClientInterface; use Ellaisys\Cognito\AwsCognitoClientManager; use Ellaisys\Cognito\Exceptions\NoLocalUserException; +use Ellaisys\Cognito\Exceptions\InvalidUserException; use Ellaisys\Cognito\Validators\AwsCognitoTokenValidator; /** @@ -67,8 +68,8 @@ public function setLocalUserData(array $credentials) { //Get user from presisting store $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); } else { - throw new Exception('User not found in AWS Cognito'); - } + throw new InvalidUserException('User not found in AWS Cognito'); + } //End if //Check if the user is not empty if (config('cognito.add_missing_local_user', false)) { @@ -81,9 +82,9 @@ public function setLocalUserData(array $credentials) { return null; } //End if - } catch (Exception $e) { + } catch (InvalidUserException | Exception $e) { Log::debug('BaseCognitoGuard:setLocalUserData:Exception:'); - throw new NoLocalUserException(); + throw $e; } //End try-catch return $this->client->getUser($username); @@ -96,38 +97,33 @@ public function setLocalUserData(array $credentials) { * @return \Ellaisys\Cognito\AwsCognitoClient */ protected function hasValidAWSCredentials(Collection $credentials) { - try { - //Reset global variables - $this->challengeName = null; - $this->challengeData = null; - $this->claim = null; - $this->awsResult = null; - - //Authenticate the user with AWS Cognito - $result = $this->client->authenticate($credentials['email'], $credentials['password']); - - //Check if the result is an instance of AwsResult - if (!empty($result) && $result instanceof AwsResult) { - //Set value into class param - $this->awsResult = $result; - - //Check in case of any challenge - if (isset($result['ChallengeName'])) { - $this->challengeName = $result['ChallengeName']; - $this->challengeData = $this->handleCognitoChallenge($result, $credentials['email']); - } elseif (isset($result['AuthenticationResult'])) { - //Create claim token - $this->claim = new AwsCognitoClaim($result, null); - } else { - $result = null; - } //End if + //Reset global variables + $this->challengeName = null; + $this->challengeData = null; + $this->claim = null; + $this->awsResult = null; + + //Authenticate the user with AWS Cognito + $result = $this->client->authenticate($credentials['email'], $credentials['password']); + + //Check if the result is an instance of AwsResult + if (!empty($result) && $result instanceof AwsResult) { + //Set value into class param + $this->awsResult = $result; + + //Check in case of any challenge + if (isset($result['ChallengeName'])) { + $this->challengeName = $result['ChallengeName']; + $this->challengeData = $this->handleCognitoChallenge($result, $credentials['email']); + } elseif (isset($result['AuthenticationResult'])) { + //Create claim token + $this->claim = new AwsCognitoClaim($result, null); + } else { + $result = null; } //End if - - return $result; - } catch (Exception $e) { - Log::error($e->getMessage()); - throw $e; - } //End try-catch + } //End if + + return $result; } //Function ends @@ -181,52 +177,37 @@ final public function buildCognitoPayload(Collection $request, $paramUsername='e { $payload = []; - try { - //Check if the request is an instance of Collection - if (!($request instanceof Collection)) { - throw new Exception('Request must be an instance of Collection'); - } //End if - - //Check if the request is empty - if ($request->isEmpty()) { - throw new Exception('Request must not be empty'); - } //End if - - //Get key fields - if ($isCredential) { - $rememberMe = $request->has('remember')?$request['remember']:false; - $payload = array_merge($payload, ['remember' => $rememberMe]); - } //End if - - //Get the configuration fields - $userFields = array_filter(config('cognito.cognito_user_fields'), function($value) { - return !empty($value); + //Get key fields + if ($isCredential) { + $rememberMe = $request->has('remember')?$request['remember']:false; + $payload = array_merge($payload, ['remember' => $rememberMe]); + } //End if + + //Get the configuration fields + $userFields = array_filter(config('cognito.cognito_user_fields'), function($value) { + return !empty($value); + }); + + if ($userFields) { + //Iterate all the keys in the request + $request->each(function($value, $key) use ($userFields, $paramUsername, $paramPassword, &$payload, $isCredential) { + switch ($key) { + case $paramUsername: + $payload = array_merge($payload, ['email' => $value]); + break; + + case $paramPassword: + $payload = array_merge($payload, ['password' => $value]); + break; + + default: + if ($isCredential && array_key_exists($key, $userFields)) { + $payload = array_merge($payload, [$key => $value]); + } + break; + } //Switch ends }); - - if ($userFields) { - //Iterate all the keys in the request - $request->each(function($value, $key) use ($userFields, $paramUsername, $paramPassword, &$payload, $isCredential) { - switch ($key) { - case $paramUsername: - $payload = array_merge($payload, ['email' => $value]); - break; - - case $paramPassword: - $payload = array_merge($payload, ['password' => $value]); - break; - - default: - if ($isCredential && array_key_exists($key, $userFields)) { - $payload = array_merge($payload, [$key => $value]); - } - break; - } //Switch ends - }); - } //End if - } catch (Exception $e) { - Log::error($e->getMessage()); - throw $e; - } //End try-catch + } //End if return collect($payload); } //Function ends @@ -283,16 +264,6 @@ final public function buildLocalUserPayload(Collection $request): Collection $payload = []; try { - //Check if the request is an instance of Collection - if (!($request instanceof Collection)) { - throw new Exception('Request must be an instance of Collection'); - } //End if - - //Check if the request is empty - if ($request->isEmpty()) { - throw new Exception('Request must not be empty'); - } //End if - //Get the configuration fields $userFields = array_filter(config('cognito.cognito_user_fields'), function($value) { return !empty($value); From 7082724d9eb8dfe718e34a4c578ecf345f680c68 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 16:31:56 +0530 Subject: [PATCH 12/21] fix: sonarcloud findings --- src/Guards/Traits/BaseCognitoGuard.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index e5a358c..9cea620 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -22,6 +22,8 @@ use Ellaisys\Cognito\AwsCognitoClient; use Ellaisys\Cognito\AwsCognitoClientInterface; use Ellaisys\Cognito\AwsCognitoClientManager; + +use Exception; use Ellaisys\Cognito\Exceptions\NoLocalUserException; use Ellaisys\Cognito\Exceptions\InvalidUserException; use Ellaisys\Cognito\Validators\AwsCognitoTokenValidator; From 4522925bb12400e3b5eeeb1081fe05d57aa3c165 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 17:20:15 +0530 Subject: [PATCH 13/21] fix: sonarcloud fixes --- src/Exceptions/AwsCognitoException.php | 28 +++++++++-------------- src/Exceptions/NoLocalUserException.php | 10 +++++++++ src/Guards/CognitoSessionGuard.php | 2 +- src/Guards/CognitoTokenGuard.php | 30 +++++-------------------- 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/Exceptions/AwsCognitoException.php b/src/Exceptions/AwsCognitoException.php index 0c0aad7..0bf60d9 100644 --- a/src/Exceptions/AwsCognitoException.php +++ b/src/Exceptions/AwsCognitoException.php @@ -7,29 +7,21 @@ use Symfony\Component\HttpKernel\Exception\HttpException; -class AwsCognitoException extends Exception +class AwsCognitoException extends HttpException { /** - * Report the exception. + * Create a new exception instance. * - * @return void - */ - public function report($message="AWS Cognito Error", $code=null, Throwable $previous=null) - { - throw new HttpException(400, $message, $previous, [], $code); - } - - - /** - * Render the exception into an HTTP response. + * @param string $message + * @param int $code + * @param \Throwable $previous + * @param array $headers * - * @param \Illuminate\Http\Request $request - * @param \Throwable $exception - * @return \Illuminate\Http\Response + * @return void */ - public function render($request, Throwable $exception) + public function __construct($message="AWS Cognito Error", $code=null, Throwable $previous=null, array $headers=[]) { - return parent::render($request, $exception); + parent::__construct(400, $message, $previous, $headers, $code); } -} //Class ends \ No newline at end of file +} //Class ends diff --git a/src/Exceptions/NoLocalUserException.php b/src/Exceptions/NoLocalUserException.php index 35edfc7..7a88106 100644 --- a/src/Exceptions/NoLocalUserException.php +++ b/src/Exceptions/NoLocalUserException.php @@ -9,6 +9,16 @@ class NoLocalUserException extends HttpException { + /** + * Create a new exception instance. + * + * @param string $message + * @param \Throwable $previous + * @param int $code + * @param array $headers + * + * @return void + */ public function __construct(string $message = 'User does not exist locally.', Throwable $previous = null, int $code = 0, array $headers = []) { parent::__construct(401, $message, $previous, $headers, $code); diff --git a/src/Guards/CognitoSessionGuard.php b/src/Guards/CognitoSessionGuard.php index 68f13e3..e4536bf 100644 --- a/src/Guards/CognitoSessionGuard.php +++ b/src/Guards/CognitoSessionGuard.php @@ -163,7 +163,7 @@ public function attempt(array $credentials = [], $remember = false, string $para //Handle the challenge $returnValue = $this->handleAWSChallenge(); } else { - throw new HttpException(400, 'ERROR_AWS_COGNITO'); + throw new AwsCognitoException('ERROR_AWS_COGNITO'); } //End if } catch (CognitoIdentityProviderException $e) { Log::error('CognitoSessionGuard:attempt:CognitoIdentityProviderException:'.$e->getAwsErrorCode()); diff --git a/src/Guards/CognitoTokenGuard.php b/src/Guards/CognitoTokenGuard.php index 8ebab1c..afa13b9 100644 --- a/src/Guards/CognitoTokenGuard.php +++ b/src/Guards/CognitoTokenGuard.php @@ -150,10 +150,10 @@ public function attempt(array $request = [], $remember = false, string $paramUse $returnValue = $this->challengeData; } else { - throw new InvalidUserException('Invalid AWS Cognito Credentials'); + throw new AwsCognitoException(); } //End if } else { - throw new InvalidUserException('Invalid AWS Cognito Credentials'); + throw new InvalidUserException(); } //End if } catch (NoLocalUserException $e) { Log::error('CognitoTokenGuard:attempt:NoLocalUserException:'.$e->getMessage()); @@ -188,7 +188,7 @@ public function attempt(array $request = [], $remember = false, string $paramUse } //End if return $returnValue; - } catch (AwsCognitoException $e) { + } catch (AwsCognitoException | InvalidUserException $e) { Log::error('CognitoTokenGuard:attempt:AwsCognitoException:'. $e->getMessage()); throw $e; } catch (Exception $e) { @@ -411,30 +411,12 @@ public function attemptMFA(array $challenge=[], bool $remember=false) { } elseif ($this->challengeName) { $returnValue = $this->challengeData; } else { - throw new InvalidUserException('Invalid AWS Cognito Credentials'); + throw new AwsCognitoException(); } //End if } else { - throw new InvalidUserException('Invalid AWS Cognito Credentials'); + throw new InvalidUserException(); } //End if - - // //Result of type AWS Result - // if (!empty($response)) { - - // //Handle the response as Aws Cognito Claim - // if ($response instanceof AwsCognitoClaim) { - // $this->claim = $response; - // return $this->login($user); - // } //End if - - // //Handle if the object is a Aws Cognito Result - // if ($response instanceof AwsResult) { - // //Check in case of any challenge - // if (isset($response['ChallengeName'])) { - // //TODO: Handle challenge in MFA login - // } //End if - // } //End if - // } //End if - } catch(Exception $e) { + } catch(AwsCognitoException | InvalidUserException | Exception $e) { throw $e; } //Try-catch ends From 466387c2b102761a41fe67ba55ec9131415caddf Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 17:23:18 +0530 Subject: [PATCH 14/21] fix: sonarcloud changes --- src/Guards/Traits/BaseCognitoGuard.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Guards/Traits/BaseCognitoGuard.php b/src/Guards/Traits/BaseCognitoGuard.php index 9cea620..b7f3cd1 100644 --- a/src/Guards/Traits/BaseCognitoGuard.php +++ b/src/Guards/Traits/BaseCognitoGuard.php @@ -230,7 +230,7 @@ protected function hasValidLocalCredentials(Collection $credentials): Authentica //Fetch user data from cognito $userRemote = $this->getRemoteUserData($this->claim->getUsername()); if (empty($userRemote)) { - throw new Exception('User not found in AWS Cognito'); + throw new InvalidUserException(); } //End if //Create user object from cognito data @@ -246,7 +246,7 @@ protected function hasValidLocalCredentials(Collection $credentials): Authentica } //End if return $user; - } catch (NoLocalUserException | Exception $e) { + } catch (InvalidUserException | NoLocalUserException | Exception $e) { Log::debug('BaseCognitoGuard:setLocalUserData:Exception'); throw $e; } //End try-catch From 4fbbe1831abda69883ea8c345b9e5de5f1ce8a05 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 18:05:14 +0530 Subject: [PATCH 15/21] doc: add sonarcloud metrics --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 779991e..17a4991 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,11 @@ AWS Cognito package using the AWS SDK for PHP [![GitHub Contributors](https://img.shields.io/github/contributors-anon/ellaisys/aws-cognito?style=flat&logo=github&logoColor=whitesmoke&label=Contributors)](CONTRIBUTING.md)  [![APM](https://img.shields.io/packagist/l/ellaisys/aws-cognito?style=flat-square&logo=github&logoColor=whitesmoke&label=License)](LICENSE.md) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ellaisys_aws-cognito&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ellaisys_aws-cognito) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ellaisys_aws-cognito&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=ellaisys_aws-cognito) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ellaisys_aws-cognito&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ellaisys_aws-cognito) + + This package provides a simple way to use AWS Cognito authentication in Laravel for Web and API Auth Drivers. The idea of this package, and some of the code, is based on the package from Pod-Point which you can find here: [Pod-Point/laravel-cognito-auth](https://github.com/Pod-Point/laravel-cognito-auth), [black-bits/laravel-cognito-auth](https://github.com/black-bits/laravel-cognito-auth) and [tymondesigns/jwt-auth](https://github.com/tymondesigns/jwt-auth). From b070ff91e026dba1224f00592a76f0e6e5477d31 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 27 Jun 2024 18:33:34 +0530 Subject: [PATCH 16/21] fix: sonarcloud fixes --- src/Traits/AwsCognitoClientAdminAction.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Traits/AwsCognitoClientAdminAction.php b/src/Traits/AwsCognitoClientAdminAction.php index 1241fef..1403e12 100644 --- a/src/Traits/AwsCognitoClientAdminAction.php +++ b/src/Traits/AwsCognitoClientAdminAction.php @@ -13,6 +13,10 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; +use Aws\CognitoIdentityProvider\CognitoIdentityProviderClient; +use Aws\CognitoIdentityProvider\Exception\InvalidPasswordException; +use Aws\CognitoIdentityProvider\Exception\NotAuthorizedException ; +use Aws\CognitoIdentityProvider\Exception\CognitoIdentityProviderException; /** * WS Cognito Client for AWS Admin Users @@ -79,7 +83,7 @@ public function adminDisableUser(string $username) /** - * Signs out a user from all devices. It also invalidates all refresh tokens that Amazon Cognito has + * Signs out a user from all devices. It also invalidates all refresh tokens that Amazon Cognito has * issued to a user. The user's current access and ID tokens remain valid until they expire. * * @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-cognito-idp-2016-04-18.html#adminuserglobalsignout From 6566b5ae30182f2462471fd603dc3ae97efa1ece Mon Sep 17 00:00:00 2001 From: Amit Dhongde <24870900+amitdhongde@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:40:12 +0530 Subject: [PATCH 17/21] Create build.yml --- .github/workflows/build.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5f896a6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,20 @@ +name: Build +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] +jobs: + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 930b778b7ea53e842562f5b5bd55c45ff3e13ff1 Mon Sep 17 00:00:00 2001 From: Amit Dhongde <24870900+amitdhongde@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:41:37 +0530 Subject: [PATCH 18/21] Create sonar-project.properties --- sonar-project.properties | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 sonar-project.properties diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..7da17ed --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=ellaisys_aws-cognito +sonar.organization=ellaisys + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=aws-cognito +#sonar.projectVersion=1.0 + + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +#sonar.sources=. + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 From fe720b8015292bce4172a1cbc9655b334da458d3 Mon Sep 17 00:00:00 2001 From: Amit Date: Wed, 10 Jul 2024 19:00:35 +0530 Subject: [PATCH 19/21] chore: update sonar properties --- sonar-project.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 7da17ed..7e60662 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -2,12 +2,12 @@ sonar.projectKey=ellaisys_aws-cognito sonar.organization=ellaisys # This is the name and version displayed in the SonarCloud UI. -#sonar.projectName=aws-cognito -#sonar.projectVersion=1.0 +sonar.projectName=aws-cognito +sonar.projectVersion=1.3.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. #sonar.sources=. # Encoding of the source code. Default is default system encoding -#sonar.sourceEncoding=UTF-8 +sonar.sourceEncoding=UTF-8 From 54ef7095d63133e7bedadaca15c593cfa16852b2 Mon Sep 17 00:00:00 2001 From: Amit Date: Wed, 10 Jul 2024 19:07:42 +0530 Subject: [PATCH 20/21] fix: sonarcloud change --- src/Providers/StorageProvider.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Providers/StorageProvider.php b/src/Providers/StorageProvider.php index d2e543e..0afa71e 100644 --- a/src/Providers/StorageProvider.php +++ b/src/Providers/StorageProvider.php @@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Cache; use Psr\SimpleCache\CacheInterface as PsrCacheInterface; +use BadMethodCallException; class StorageProvider { @@ -71,7 +72,7 @@ public function add($key, $value, $duration=3600) { // If the laravel version is 5.8 or higher then convert minutes to seconds. if ($this->laravelVersion !== null - && is_int($minutes) + && is_int($duration) && version_compare($this->laravelVersion, '5.8', '<') ) { $duration = ($duration/60); @@ -209,4 +210,4 @@ protected function determineTagSupport() } } //Function ends -} //Class ends \ No newline at end of file +} //Class ends From 1a4c2eac5cc5453d7693ca90c07e754fc536eade Mon Sep 17 00:00:00 2001 From: Amit Date: Wed, 10 Jul 2024 19:12:22 +0530 Subject: [PATCH 21/21] fix: change for sonarcloud --- src/Traits/AwsCognitoClientAdminAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Traits/AwsCognitoClientAdminAction.php b/src/Traits/AwsCognitoClientAdminAction.php index 1403e12..e48bb87 100644 --- a/src/Traits/AwsCognitoClientAdminAction.php +++ b/src/Traits/AwsCognitoClientAdminAction.php @@ -166,4 +166,4 @@ public function describeUserPool() return true; } //Function ends -} //Trait ends \ No newline at end of file +} //Trait ends