Reputation: 943
I have built applications using Firebase and React, and the procedure is pretty seamless.
Lately I have been required to use AWS Cognito, and it seems a bit of a pain to set up as the docs are not clear.
Firstly, how can I do user authentication using Cognito? I set up a user pool, with the following app client settings:
Now, I add the authorizer to my API as follows:
Now my question is, how do I use this with my frontend to sign in a user and make authenticated API calls?
There seem to be two different toolkits available:
It is not clear at all for a beginner what to use, and how to get authentication working. Ideally I would use it like I do for firebase, and just have my frontend make an authentication call using the email and password, and in turn receiving a token of some sort (on success only) that can then be used to then make signed API calls.
Can someone please help with code examples?
Upvotes: 7
Views: 11195
Reputation: 714
sorry for the confusion.
AWS Cognito Userpools act as an Identity Provider. It supports all User management (Sign Up, Sign In, Password reset, User deletion, etc). Cognito also supports Federated Identity (E.g., A User who already has an Google/Facebook account can sign in). In this case, Cognito talks to Google/Facebook using OAuth.
When I was learning about Cognito/JWT tokens, I created a simple JS/HTML to understand how it works. Since you asked for code, you can refer it - https://github.com/ryandam9/Cognito-tokens.
As per your screen shot, you already configured a Userpool - sls-notes-backend. Say, you configured the mandatory attribute as Email.
Step 0 - Initialize You get both userPoolId and appId when you create the user pool.
poolData = {
UserPoolId: userPoolId,
ClientId: appId
};
userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
Step 1 - Signup a User using Email and Password - Say your UI already captured these details from the User and user clicked on 'Sign Up' button.
/**
* Signup a User
* @param e
*/
function addUser(e) {
signupMessage.style.display = 'none';
signupMessage.className = '';
e.preventDefault();
let name = document.getElementById('name').value.trim();
let email = document.getElementById('signup-email').value.trim();
let password = document.getElementById('signup-password').value.trim();
if (name.length === 0 || email === 0 || password === 0) {
return;
}
let attributeList = [
new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
Name: 'given_name', Value: name
}),
];
userPool.signUp(email, password, attributeList, null, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
// Show a text box to enter Confirmation code
document.getElementById('signup-btn').style.display = 'none';
document.getElementById('code-block').style.display = 'block';
document.getElementById('confirm-user-btn').style.display = 'inline-block';
});
}
If the signup is successful (It is valid Email and the Email does not yet exist in Userpools, a Confirmation code is sent to the Email provided. Next step is to, allow the user to enter the code and confirm his identity.
Step 3 - Confirm User
/**
* Confirm the user by taking the Confirmation code.
* @param e
*/
function confirmUser(e) {
e.preventDefault();
let verificationCode = document.getElementById('code').value;
cognitoUser.confirmRegistration(verificationCode, true, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
signupMessage.innerText = result;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-success';
});
}
If the User enters correct code, his identity is confirmed. At this point, An entry is made to the Userpool for this user. It looks like this.
Step 4 - Authentication (Sign In) At this point, User registration is done. Its time to allow him to login. Please ignore the unnecessary code in the code below (the code that fetches and prints credentials, decoding part). If the authentication is successful, Cognito returns two types of Tokens to the application - ID Token and Access Token. These are valid only for this session and for this user only. More details here - https://ryandam.net/aws/19-cognito-userpools/index.html#0
/**
* Signin user with Email and Password
* @param e
*/
function authenticateUser(e) {
e.preventDefault();
let email = document.getElementById('signin-email').value;
let password = document.getElementById('signin-password').value;
if (email.length === 0 || password === 0 || userPool === null || userPool === undefined) {
signinMessage.innerText = 'Fill in all fields!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
return;
}
let authenticationData = {
Username: email,
Password: password,
};
let authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
let userData = {
Username: email,
Pool: userPool
};
let cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
signinMessage.innerText = 'Authentication Success!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-success';
document.getElementById('token-section').style.display = 'block';
document.getElementById('signin-btn').style.display = 'none';
// Decode ID Token
let idToken = result.idToken.jwtToken;
document.getElementById('id-token').innerText = idToken;
document.getElementById('decoded-id-token').appendChild(parseIdToken(idToken));
// Decode Access Token
let accessToken = result.getAccessToken().getJwtToken();
document.getElementById('access-token').innerText = accessToken;
document.getElementById('decoded-access-token').appendChild(parseAccessToken(accessToken));
let cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function (err, result) {
if (result) {
// Set the region where your identity pool exists (us-east-1, eu-west-1)
AWS.config.region = region;
AWS.config.update({region: region});
logins = {};
let key = 'cognito-idp.us-east-2.amazonaws.com/' + userPoolId;
logins[key] = result.getIdToken().getJwtToken();
// Add the User's Id Token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: identityPoolId,
Logins: logins,
});
// Make the call to obtain credentials
AWS.config.credentials.get(function () {
// Credentials will be available when this function is called.
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
});
if (s3BucketName.length > 0)
listS3Bucket(s3BucketName);
}
});
}
},
onFailure: function (err) {
signinMessage.innerText = err;
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
}
}
);
}
Step 5 - Invoking the API Endpoint you already created - Since you've already created an Authorizer using the Userpool and you're using Authorization as the header, you can invoke the End point from JS by passing the ID token as Authorization header. What happens is that, the token is validated by the authorizer. Since it is valid the user is able to invoke the API.**
I do not have JS code, you can test your API from CLI/Postman something like this:
Note AWS Amplify seems to be a wrapper for Cognito and other services. For instance, Amplify sets up User pool for you when you invoke its CLI commands. You can refer this code if you want to see how Amplify works with Flutter - https://github.com/ryandam9/Auth-flutter-aws-amplify.
I am still learning. I tried to be as accurate as possible.
Upvotes: 13