AWS cognito how to forgot password with lambda - amazon-web-services

I need implement logic for forgot password with aws lambda. I have cognito user pool with users and i need implement lambda for forgot password. How can i do it? Thanks in advance.

You can use AWS SDK to invoke the Cognito-forgot password from LAMBDA. you can refer to this documentation. You may have to provide the User pool Client and Username of the user along with the request.
Note: Your Lambda execution policy should have cognito-idp:ForgotPassword
Here is the Documentation for implementing with Nodejs.
const AWS = require('aws-sdk');
export.handler = async (event) => {
const params = {
ClientId: 'USER POOL CLIENT ID',
Username: 'USERNAME'
}
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const response = cognitoidentityserviceprovider.forgotPassword(params).promise();
return response;
}

Related

Can I use the context param of lambda function for this?

I have 2 react native apps that are connected to a single aws backend. And I have deployed a post-confirmation lambda function so that I can save items (Users etc.) into the dynamo db storage. I want to know how do I adjust my lambda function so that If someone signup from the user side of my app it save an item in the user table of dynamo db and if someone signup from the driver side of the app it saves an item in the driver table of dynamo db. Can I use context param to check whether from which side of the app someone is signing up?
You can use clientMetaData to pass parameters to the PostConfirmation Lambda function.
await Auth.signUp({
username,
password,
clientMetadata: {
isDriver: false,
},
});
and in the PostConfirmation Lambda
const AWS = require("aws-sdk");
exports.handler = async (event, context, callback) => {
AWS.config.region = event.region;
// you can access like this
// event.request.clientMetadata.isDriver
context.succeed(event);
};

Autetificate users in AWS Api Gateway with Cognito

I want to build a rest api using Aws Rest Api Gateway. This will be the new version of a already in production api (hosted on private servers).
On the current version of the api we use oauth2 with grant type password for authentication. This means that a client send his username and pass to a ".../access_token" endpoint from where it gets his token. With this token he can then call the other endpoints.
On the new api version I'm using the AWS Api Gateway with Authorizer. I want to provide acces to resources based on the username & passwords fields
I've created a user pool and added my users there. How do i get authenticated using only api endpoints?
I cannot use Oauth "client credentials" flow since is machine to machine and client secret will have to be exposed.
On Authorization code or Implicit grant i have to ask the user to login on AWS / custom ui and get redirected. So i cannot use these in an Api.
What i'm missing ?
I understand that you need to authenticate your users without using a browser. An idea would be to create a login endpoint, where users will give their username and password and get back a token. You should implement this endpoint yourself. From this question:
aws cognito-idp admin-initiate-auth --region {your-aws-region} --cli-input-json file://auth.json
Where auth.json is:
{
"UserPoolId": "{your-user-pool-id}",
"ClientId": "{your-client-id}",
"AuthFlow": "ADMIN_NO_SRP_AUTH",
"AuthParameters": {
"USERNAME": "admin#example.com",
"PASSWORD": "password123"
}
}
This will give access, id and refresh tokens (the same way as the authorization grant type) to your users. They should be able to use the access token to access resources and the refresh token against the Token endpoint to renew access tokens.
This isn't the common way to authenticate an API and may have some security implications.
I solved this issue by creating custom Lambda in NodeJS 16x with exposed URL, that does Basic Authentication on the Cognito side with stored app client id, user pool id, secret. I attach the code here, but you still need to create lambda layer with Cognito SDK, configure IAM yourself.
const AWS = require('aws-sdk');
const {
CognitoIdentityProviderClient,
AdminInitiateAuthCommand,
} = require("/opt/nodejs/node16/node_modules/#aws-sdk/client-cognito-identity-provider");
const client = new CognitoIdentityProviderClient({ region: "eu-central-1" });
exports.handler = async (event, context, callback) => {
let username = event.queryStringParameters.username;
let password = event.queryStringParameters.password;
let app_client_id = process.env.app_client_id;
let app_client_secret = process.env.app_client_secret;
let user_pool_id = process.env.user_pool_id;
let hash = await getHash(username, app_client_id, app_client_secret);
let auth = {
"UserPoolId": user_pool_id,
"ClientId": app_client_id,
"AuthFlow": "ADMIN_NO_SRP_AUTH",
"AuthParameters": {
"USERNAME": username,
"PASSWORD": password,
"SECRET_HASH": hash
}
};
let cognito_response = await requestToken(auth);
var lambda_response;
if (cognito_response.startsWith("Error:")){
lambda_response = {
statusCode: 401,
body: JSON.stringify(cognito_response) + "\n input: username = " + username + " password = " + password,
};
}
else {
lambda_response = {
statusCode: 200,
body: JSON.stringify("AccessToken = " + cognito_response),
};
}
return lambda_response;
};
async function getHash(username, app_client_id, app_client_secret){
const { createHmac } = await import('node:crypto');
let msg = new TextEncoder().encode(username+app_client_id);
let key = new TextEncoder().encode(app_client_secret);
const hash = createHmac('sha256', key) // TODO should be separate function
.update(msg)
.digest('base64');
return hash;
}
async function requestToken(auth) {
const command = new AdminInitiateAuthCommand(auth);
var authResponse;
try {
authResponse = await client.send(command);
} catch (error) {
return "Error: " + error;
}
return authResponse.AuthenticationResult.AccessToken;
}

AWS Cognito Error: No Cognito Identity pool provided for unauthenticated access

var normalAuth = async (username, password) => {
try{
var user = await Auth.signIn(username, password)
console.log(user)
if(user.challengeName === "NEW_PASSWORD_REQUIRED"){
console.log("assigning new password!")
user = await Auth.completeNewPassword(user, "password") // give the user a new password. this would NOT be included in any kind of production code, its only here as a work around
console.log(user)
} // work around. users created in the console are only given temporary passwords, and as such have no authorization
// setting a new password will fix this
console.log(await Auth.currentCredentials()) // THIS THROWS AN ERROR?!?!
setAuthState(false) // auth is no longer in progress
}
catch(error){
console.error(error)
}
}
I'm trying to grab the tokens from my Amplify user after authenticating but I'm receiving the error
No Cognito Identity pool provided for unauthenticated access
This is pretty strange because I'm not using an identity pool at all. My baseline assumption was that I shouldn't need to provide an identity pool ID at all in my amplify config.
config looks like this
var amplifyConfig = {
Auth: {
mandatorySignIn: false,
region: "us-east-1",
userPoolId: "mypoolid",
userPoolWebClientId: "myclientid"
}
}
The really weird part is that the tokens are actually there in user after I authenticate. It's only when I try and retrieve them this way specifically that an error is thrown.
Any idea what's going on here?

AWS - Confirm User Cognito

I'm trying to register a user in a Cognito UserPool but I'm having issue with the Pre-signup trigger.
I've configured a lambda like in the docs example
exports.handler = (event, context, callback) => {
event.response.autoConfirmUser=true;
callback(null, event);
};
Using CloudWatch logs I can see that the autoConfirmUser attribute is correctly set to true, but the user isn't confirmed.
Here is the account in the UserPool:
Any idea how to fix this ?
Are you calling
context.done(error, event);
like in the examples provided?

AWS API Gateway / Cognito Userpools / Lambdas not able to pass caller credentials

I'm working on an AWS API Gateway implementation with a Lambda backend. I use the API Gateway integration with the Cognito Userpools (fairly new) instead of building a custom authorizer using Lambda (which was the recommended way before it was integrated).
I've created a proof of concept (javascript) that authenticates a user with Cognito and then makes a call to the API Gateway with those credentials. So, basically, the end call to the API Gateway is with the JWT token that I received from Cognito (result.idToken.jwtToken) in the Authorization header. This all works and I can validate that only with this token you can access the API.
All working fine, but now I want to get access to the Cognito identity in my Lambda; for instance the identy id or the name or email. I have read how to map all the parameters, but I'm actually just using the standard 'Method Request Passthrough' template in the integration request. I log all the parameters in the lambda and all the 'cognito' parameters are empty.
I've looked through many similar questions and they all propose to enable the 'Invoke with caller credentials' checkbox on the integration request. That makes perfect sense.
However, this checkbox can only be enabled if you are using AWS_IAM as authorization and not if you have selected your cognito UserPool. So it is just not possible to select it and is actually disabled.
Does anybody know what to do in this case? Is this still work in progress, or is there a reason why you can't enable this and get the cognito credentials in your Lambda?
Many thanks.
If you need to log the user information in your backend, you can use $context.authorizer.claims.sub and $context.authorizer.claims.email to get the sub and email for your Cognito user pool.
Here is the documentation about Use Amazon Cognito Your User Pool in API Gateway
For anyone else still struggling to obtain the IdentityId in a Lambda Function invoked via API-Gateway with a Cognito User Pool Authorizer, I finally was able to use the jwtToken passed into the Authorization header to get the IdentityId by using the following code in my JavaScript Lambda Function:
const IDENTITY_POOL_ID = "us-west-2:7y812k8a-1w26-8dk4-84iw-2kdi849sku72"
const USER_POOL_ID = "cognito-idp.us-west-2.amazonaws.com/us-west-2_an976DxVk"
const { CognitoIdentityClient } = require("#aws-sdk/client-cognito-identity");
const { fromCognitoIdentityPool } = require("#aws-sdk/credential-provider-cognito-identity");
exports.handler = async (event,context) => {
const cognitoidentity = new CognitoIdentityClient({
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient(),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[USER_POOL_ID]:event.headers.Authorization
}
}),
});
var credentials = await cognitoidentity.config.credentials()
var identity_ID = credentials.identityId
console.log( identity_ID)
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body:JSON.stringify(identity_ID)
};
return response;
}
After a Cognito User has signed in to my application, I can use the Auth directive of aws-amplify and fetch() in my React-Native app to invoke the lambda function shown above by sending a request to my API-Gateway trigger (authenticated with a Cognito User Pool Authorizer) by calling the following code:
import { Auth } from 'aws-amplify';
var APIGatewayEndpointURL = 'https://5lstgsolr2.execute-api.us-west-2.amazonaws.com/default/-'
var response = {}
async function getIdentityId () {
var session = await Auth.currentSession()
var IdToken = await session.getIdToken()
var jwtToken = await IdToken.getJwtToken()
var payload = {}
await fetch(APIGatewayEndpointURL, {method:"POST", body:JSON.stringify(payload), headers:{Authorization:jwtToken}})
.then(async(result) => {
response = await result.json()
console.log(response)
})
}
More info on how to Authenticate using aws-amplify can be found here https://docs.amplify.aws/ui/auth/authenticator/q/framework/react-native/#using-withauthenticator-hoc