How to process with AWS-cognito NEW_PASSWORD_REQUIRED Challenge - php

When I use the below code:
$result = $this->client->adminInitiateAuth([
'AuthFlow' => 'ADMIN_NO_SRP_AUTH',
'ClientId' => $this->client_id,
'UserPoolId' => $this->userpool_id,
'AuthParameters' => [
'USERNAME' => $username,
'PASSWORD' => $password,
],
]);
I am getting a response with session and challengeName :NEW_PASSWORD_REQUIRED. From this how to generate the AccessToken in AWS-cognito?

You can use the respondToAuthChallenge method to set the user's new password and log them in. It should also return the accessToken for you.
You could do something like this:
$result = $this->client->respondToAuthChallenge([
'ChallengeName' => 'NEW_PASSWORD_REQUIRED',
'ClientId' => $this->client_id,
'ChallengeResponses' => [
'USERNAME' => $username,
'NEW_PASSWORD' => $password,
],
'Session' => $session,
]);

You need to respond this challenge (respondToAuthChallenge) using the session returned by adminInitiateAuth method. This session is a key to respond because the user at this time not is logged yet and is valid for 3 minutes. After that, you will reveive (if the session is valid) the RefreshToken, AccessToken and IdToken.

Related

AWS Cognito PHP respondToAuthChallenge NEW_PASSWORD_REQUIRED User Attributes Missing

I'm trying to use respondToAuthChallenge with NEW_PASSWORD_REQUIRED to change the user's password. I keep getting "Invalid attributes given, given_name is missing" errors. I've tried adding the user's attributes to the call, but I can't seem to find any documentation on how that data should be formatted within the call.
Here is the original code...
$QUERY = $COG_CLIENT->respondToAuthChallenge([
'ChallengeName' => 'NEW_PASSWORD_REQUIRED',
'ClientId' => $COG_CLIENT_ID,
'ChallengeResponses' => [
'USERNAME' => $USER_EMAIL,
'NEW_PASSWORD' => $USER_NEW_PASS,
'SECRET_HASH' => $SEC_HASH,
],
'Session' => $COG_USER_SESSION,
]);
Then I tried adding the user attributes like so...
$QUERY = $COG_CLIENT->respondToAuthChallenge([
'ChallengeName' => 'NEW_PASSWORD_REQUIRED',
'ClientId' => $COG_CLIENT_ID,
'ChallengeResponses' => [
'USERNAME' => $USER_EMAIL,
'NEW_PASSWORD' => $USER_NEW_PASS,
'SECRET_HASH' => $SEC_HASH,
'UserAttributes' => '{"given_name":"Bob"}',
],
'Session' => $COG_USER_SESSION,
]);
But I'm still getting the same error. If I format the UserAttributes value as an array, I get an error saying that a string is expected.
Does anyone know how I should be passing the user attributes? I'm super lost on this one.
Ah, I think I figured it out after hours of trying many different variations... this is what finally ended up working.
$QUERY = $COG_CLIENT->respondToAuthChallenge([
'ChallengeName' => 'NEW_PASSWORD_REQUIRED',
'ClientId' => $COG_CLIENT_ID,
'ChallengeResponses' => [
'USERNAME' => $USER_EMAIL,
'NEW_PASSWORD' => $USER_NEW_PASS,
'SECRET_HASH' => $SEC_HASH,
'userAttributes.given_name' => 'Bob',
],
'Session' => $COG_USER_SESSION,
]);
I can't believe this isn't documented somewhere.

How do I change AD user password by using Adldap2-laravel package?

I would like to change the password of a user in AD since there are no attribute for password in AD.
Currently running laravel framework with Adldap2-laravel package in order to manage ADLDAP operations.
Here's my ldap_auth.php
<?php
return [
'connection' => env('LDAP_CONNECTION', 'default'),
'provider' => Adldap\Laravel\Auth\DatabaseUserProvider::class,
'model' => App\User::class,
'rules' => [
Adldap\Laravel\Validation\Rules\DenyTrashed::class,
],
'scopes' => [
Adldap\Laravel\Scopes\UidScope::class
],
'identifiers' => [
'ldap' => [
'locate_users_by' => 'uid',
'bind_users_by' => 'dn',
],
'database' => [
'guid_column' => 'objectguid',
'username_column' => 'username',
],
'windows' => [
'locate_users_by' => 'samaccountname',
'server_key' => 'AUTH_USER',
],
],
'passwords' => [
'sync' => env('LDAP_PASSWORD_SYNC', false),
'column' => 'password',
],
'login_fallback' => env('LDAP_LOGIN_FALLBACK', false),
'sync_attributes' => [
'username' => 'uid',
'password' => 'userPassword',
'name' => 'cn',
'role' => 'l',
'category' => 'businessCategory',
'telephone_number' => 'telephoneNumber',
'email' => 'mail'
],
'logging' => [
'enabled' => env('LDAP_LOGGING', true),
'events' => [
\Adldap\Laravel\Events\Importing::class => \Adldap\Laravel\Listeners\LogImport::class,
\Adldap\Laravel\Events\Synchronized::class => \Adldap\Laravel\Listeners\LogSynchronized::class,
\Adldap\Laravel\Events\Synchronizing::class => \Adldap\Laravel\Listeners\LogSynchronizing::class,
\Adldap\Laravel\Events\Authenticated::class => \Adldap\Laravel\Listeners\LogAuthenticated::class,
\Adldap\Laravel\Events\Authenticating::class => \Adldap\Laravel\Listeners\LogAuthentication::class,
\Adldap\Laravel\Events\AuthenticationFailed::class => \Adldap\Laravel\Listeners\LogAuthenticationFailure::class,
\Adldap\Laravel\Events\AuthenticationRejected::class => \Adldap\Laravel\Listeners\LogAuthenticationRejection::class,
\Adldap\Laravel\Events\AuthenticationSuccessful::class => \Adldap\Laravel\Listeners\LogAuthenticationSuccess::class,
\Adldap\Laravel\Events\DiscoveredWithCredentials::class => \Adldap\Laravel\Listeners\LogDiscovery::class,
\Adldap\Laravel\Events\AuthenticatedWithWindows::class => \Adldap\Laravel\Listeners\LogWindowsAuth::class,
\Adldap\Laravel\Events\AuthenticatedModelTrashed::class => \Adldap\Laravel\Listeners\LogTrashedModel::class,
],
],
];
Here is my LdapController.php where I include function to reset password
public function resetPassword(Request $req)
{
$req->validate([
'userid' => 'required',
'password' => 'required|min:6|confirmed'
]);
$userLdap = Adldap::search()->where('uid', $req->userid)->firstOrFail();
$newPassword = "{SHA}" . base64_encode(pack("H*", sha1($req->password)));
$res = $userLdap->update([
'userpassword' => $newPassword
]);
//Force change AD Password
// $adPassword = str_replace("\n", "", shell_exec("echo -n '\"" . $req->password . "\"' | recode latin1..utf-16le/base64"));
// $provider = Adldap\Models\User::connect('ad');
// $dn = $provider->search()->where('cn', $req->userid)->get();
// $res = $dn->setPassword($adPassword);
if ($res) {
return back()->withSuccess('<strong>Success!</strong> Your password has been changed');
} else {
return back()->withErrors('<strong>Failed!</strong> Your password was unable to changed');
}
}
Unfortunately $res = $dn->setPassword($adPassword); returns error 'Method Adldap\Query\Collection::setPassword does not exist.'
I found an example here when I searched Google for "Adldap2-laravel change password".
$user = Adldap::users()->find('jdoe');
if ($user instanceof Adldap\Models\User) {
$oldPassword = 'password123';
$newPassword = 'correcthorsebatterystaple';
$user->changePassword($oldPassword, $newPassword);
}
If you want to reset the password, then it seems like this should work:
$user->setPassword("correcthorsebatterystaple");
$user->save();
If you want to know what's going on underneath, or how it can be done without Adldap2-laravel:
The attribute is unicodePwd. You can either "change" the password, or "reset" it.
Changing the password requires knowing the old password. This is what a user would do themselves.
Resetting a password requires the "Reset password" permission on the account, which is usually given to administrative accounts.
The documentation for unicodePwd tells you how to do both. For a "change", you send a delete instruction with the old password and an add instruction with the new one, all in the same request.
For a reset, you send a single replace instruction.
In both cases, the passwords have to be sent in a specific format.
The PHP documentation for 'ldap_modify_batch` shows an example of how to change a password.
On the documentation page for ldap_mod_replace, there is a comment that shows you how to reset a password.

AWS Cognito adminSetUserPassword returns "User does not exist." when user is definitely in pool

When calling the following code it returns an exception
$this->client->adminSetUserPassword([
'Password' => $password,
'Permanent' => true,
'UserPoolId' => $this->poolId,
'Username' => $email,
]);
-errorCode: "UserNotFoundException"
-errorMessage: "User does not exist."
I'm using similar admin level requests elsewhere as follows...
$result = $this->client->adminDeleteUser([
'UserPoolId' => $this->poolId,
'Username' => $email,
]);
$result = $this->client->adminDisableUser([
'UserPoolId' => $this->poolId,
'Username' => $email,
]);
$response = $this->client->adminInitiateAuth([
'AuthFlow' => 'ADMIN_NO_SRP_AUTH',
'AuthParameters' => [
'USERNAME' => $email,
'PASSWORD' => $password,
'SECRET_HASH' => $this->cognitoSecretHash($email),
],
'ClientId' => $this->clientId,
'UserPoolId' => $this->poolId,
]);
All these methods and others work correctly with the user, but the new adminSetUserPassword method seems to fail, despite the user definitely being in my user pool.

How to log in to AWS cognito user pool using PHP SDK

I am trying to log in the user to the AWS Cognito user pool using PHP SDK. I am following this tutorial, https://sanderknape.com/2017/02/getting-started-with-aws-cognito/. But I am getting the error.
Here is my code:
$credentials = array(
'key' => env('AWS_IAM_KEY', ''),
'secret' => env('AWS_IAM_SECRET', '')
);
//2014-06-30
$client = CognitoIdentityClient::factory(array('region' => env('AWS_REGION',''), 'version' => 'latest', $credentials));
$result = $client->adminInitiateAuth([
'AuthFlow' => 'ADMIN_NO_SRP_AUTH',
'ClientId' => COGNITO_APP_CLIENT_ID,
'UserPoolId' => COGNITO_USER_POOL_ID,
'AuthParameters' => [
'USERNAME' => "name",
'PASSWORD' => 'password',
],
]);
$accessToken = $result->get('AuthenticationResult')['AccessToken'];
When I run the code, I got this error:
InvalidArgumentException
Operation not found: AdminInitiateAuth
It is saying the AdminInitiiateAuth does not exist. But I am correctly following the tutorial. What is missing in my code?
The problem here is that CognitoIdentityClient does not contain the adminInitiateAuth functionality.
You will need to use the CognitoIdentityProviderClient

Laravel testing is it possible to have followingRedirects after the assertSessionHas

I am testing in laravel and saw yesterday that you can't have followingRedirects before assertSessionHas. So I wanted to know if it is possible to have it after the assertSessionHas without repeating the whole test code. I have now this a temporary solution:
$response = $this->post('/signup', [
'username' => 'Testing',
'email' => 'testing#test.com',
'password' => 'secret',
'password_confirmation' => 'secret',
]);
$this->assertDatabaseHas('users', ['username' => 'Testing', 'email' => 'testing#test.com']);
$response->assertSessionHas('success', 'Your account has been created!');
$code = $this->followRedirects($response)->getStatusCode();
$this->assertEquals(200, $code);
But I want to know if you are able to change the response later on with the followingRedirects instead of using it this way.
When sending a post request in Laravel using PHPUnit, you don't need to pass the csrf_token(), it is included automatically.
I would refactor your code like this:
$this->post('/signup', [
'username' => 'Testing',
'email' => 'testing#test.com',
'password' => 'secret',
'password_confirmation' => 'secret',
])->assertStatus(200)
->assertSessionHas('success', 'Your account has been created!');
$user = User::latest()->first();
$this->assertEquals('Testing', $user->name);
$this->assertEquals('testing#test.com', $user->email);

Categories