In this blog, we will guide you through the process on how to Create Secure User Authentication with AWS Cognito for cloud applications. From setting up a Cognito User Pool to integrating it into your app, we’ll cover essential steps like managing user sign-ins, session handling, and password recovery.
Having an AWS Certified Developer Associate Certification significantly enhances your ability to implement these solutions effectively, as it equips you with deep knowledge of AWS services and best practices for building secure, scalable applications.
By leveraging AWS Amplify, we will simplify the integration process, ensuring a seamless experience for developers/application owners while maintaining robust security features for end users.
Let’s dive into building a secure and scalable authentication system for your cloud applications!
What is AWS Cognito?
AWS Cognito is a managed service that provides authentication, authorization, and user management for your web and mobile apps. It simplifies the process of adding user sign-up, sign-in, and access control to applications. Key features of AWS Cognito include:
- User Pools: Manage user registration, authentication, and account recovery.
- Identity Pools: Provide temporary AWS credentials for users to access other AWS services.
- Federated Identities: Allow users to sign in through social identity providers like Facebook, Google, and Amazon.
Create Secure User Authentication with AWS Cognito for Cloud Applications
Follow the steps in the following phases to create a secure user authentication with AWS Cognito for cloud applications:
- Phase 1 – Create a User Pool
- Phase 2 – Integrate AWS Cognito into Your Cloud Application
- Phase 3 – Implement User Sign-in Functionality Using AWS Cognito and AWS Amplify
- Phase 4 – Implement User Sign-out and Session Management
- Phase 5 – Implement Forgot Password
Phase 1 – Create a User Pool
In this phase, let’s focus on creating a user pool.
Configuring Sign-in Experience
1. Sign in to the AWS Management Console and navigate to the Cognito service.
2. In the Configure sign-in experience tab, select Create user pool.
3. Choose the provider type. Options are:
- Cognito user pool (Default).
- Federated identity providers
4. Choose the sign-in attribute you need from users.
For example, the user name, email, or phone number.
5. If you chose the user name attribute, choose the User name requirements options
6. If you chose the Federated identity providers provider type, choose the third-party identity providers.
7. Select Next.
Configuring Security Requirements
1. In the Configure Security Requirements tab, choose the password policy mode.
2. Choose the Multi-Factor Authentication (MFA) enforcement. The Require MFA option is recommended as it provides a high level of security.
3. Choose the MFA method. Options are:
- Authenticator apps: Users will use a time-based one-time password from an app like Google Authenticator.
- SMS message: Users will receive an SMS with a code.
4. Choose the User account recovery option and select the verification method for user sign-up confirmation (for example, via email or SMS).
5. Select Next.
Configuring Sign-up Experience
1. In the Configure Sign-up Experience tab, select the Enable self-registration checkbox.
2. Select the Allow Cognito to automatically send messages to verify and confirm checkbox and choose the attributes to verify.
3. Select the dropdown and choose the required attributes.
4. You can add custom attributes if required.
5. Select Next.
Configuring Message Delivery
1. In the Configure message delivery tab, choose the email provider.
2. If you have an existing IAM role for SMS configuration, choose the Use an existing IAM role option.
3. If you do not have an existing IAM role for SMS configuration, choose the Create a new IAM role option and enter the name of your new IAM role.
4. Select Next.
Connecting Federated Identity Providers
1. If you chose Federated identity providers in step 2 in the Configure Security Requirements section, configure attribute mapping between provider responses and Cognito tokens for the providers you chose in step 2 in the Configure Security Requirements section.
2. Select Next.
Integrating Your Application
1. In the Integrate your app tab, enter a name for your user pool. Once you create a user pool name, you can’t change it.
NOTE: The hosted UI will automatically be enabled if you choose the third-party provider, like Google.
2. To use your hosted UI, choose a domain type where the OAuth 2.0 endpoint is to be created. Options are:
- Use a cognito domain
- Use a custom domain
3. Enter the domain prefix if you choose to use a cognito domain.
Creating Initial App Client
1. Select the app type. Options are:
- Public client
- Confidential client
- Other
2. Enter a client name.
3. Configure client or user secrets. Options are:
- Generate a client secret – Recommended
- Don’t generate a client secret
4. Enter your callback URL.
5. Configure advanced app client settings if required.
6. Choose the standard and custom attributes you can read and write.
7. Select Next.
8. In the Review and Create tab, review all configurations and select Create user pool.
Phase 2 – Integrate AWS Cognito into Your Cloud Application
Let’s integrate AWS Cognito into your cloud application to enable user registration. This phase includes setting up the front-end and back-end to allow users to sign up, and confirm their accounts.
Installing AWS Amplify
- Open your terminal and navigate to your project directory.
- Run the npm install aws-amplify command in bash to install AWS amplify in your application.
- Run the npm install -g @aws-amplify/cli command in bash to install AWS amplify CLI globally.
- Run the amplify init command in bash to initialize amplify in your project.
- Follow the prompts to set up your project.
- Choose your project environment, and then select AWS Cognito as the authentication provider.
Configuring AWS Amplify Authentication
- Run the following command to import AWS amplify and configure it in your project’s main file (e.g., `app.js` or `index.js`):
javascript
import Amplify from ‘aws-amplify’;
import awsconfig from ‘./aws-exports’;//This file is auto-generated by Amplify
Amplify.configure(awsconfig);
- Ensure your aws-exports.js file is correctly set up to reference your Cognito User Pool, App Client, and Region.
Implementing User Sign-up Form
- Create a sign-up form in your front end (React, Angular, Vue, etc.). This form will collect user details such as email, password, and phone number if required.
Example HTML/JSX form for react:
jsx
<form onSubmit={handleSignUp}>
<input
type=”text”
placeholder=”Email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type=”password”
placeholder=”Password”
onChange={(e) => setPassword(e.target.value)}
required
/>
<input
type=”text”
placeholder=”Phone Number”
onChange={(e) => setPhoneNumber(e.target.value)}
/>
<button type=”submit”>Sign Up</button>
</form>
- In react, use the useState hook to manage form data.
javascript
import React, { useState } from ‘react’;
const SignUpForm = () => {
const [email, setEmail] = useState(”);
const [password, setPassword] = useState(”);
const [phoneNumber, setPhoneNumber] = useState(”);
const handleSignUp = async (event) => {
event.preventDefault();
// Call AWS Cognito sign-up logic here (in the next step)
};
return (
<form onSubmit={handleSignUp}>
<input
type=”text”
placeholder=”Email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type=”password”
placeholder=”Password”
onChange={(e) => setPassword(e.target.value)}
required
/>
<input
type=”text”
placeholder=”Phone Number”
onChange={(e) => setPhoneNumber(e.target.value)}
/>
<button type=”submit”>Sign Up</button>
</form>
);
};
export default SignUpForm;
- Import AWS Amplify’s OAuth module to handle user sign-up.
- Import AWS Amplify’s Auth module.
javascript
import { Auth } from ‘aws-amplify’;
- Create the sign-up function that triggers user registration with AWS Cognito.
javascript
const handleSignUp = async (event) => {
event.preventDefault();
try {
const user = await Auth.signUp({
username: email,
password,
attributes: {
email,// Required
phone_number,// Optional, based on User Pool configuration
},
});
console.log(‘Sign-up successful:’, user);
// Redirect user to confirmation page or prompt for code input
}catch (error) {
console.error(‘Error during sign-up:’, error);
}
};
Handling Account Verification (Confirm Sign-up)
- Create a form for users to input the confirmation code they received.
jsx
<form onSubmit={handleConfirmation}>
<input type=”text” placeholder=”Confirmation Code” onChange={e => setConfirmationCode(e.target.value)} />
<button type=”submit”>Confirm Account</button>
</form>
- Implement the confirmation logic.
javascript
const handleConfirmation = async (event) => {
event.preventDefault();
try {
const result = await Auth.confirmSignUp(email, confirmationCode);
console.log(‘Account confirmed:’, result);
// Redirect user to login page after confirmation
}catch (error) {
console.error(‘Error confirming sign-up:’, error);
}
};
- Confirm the sign-up by calling the Auth.confirmSignUp() method, and passing in the email and confirmation code.
Handling Sign-up Errors (Optional)
- Implement error handling to display user-friendly messages for issues like:
- Weak password: Display a message that password policies were not met.
- Email already in use: Prompt the user that the email is already registered.
- Incorrect confirmation code: Inform the user if they input an invalid code.
“`javascript
catch (error) {
if (error.code === ‘UsernameExistsException’) {
console.error(‘This email is already registered.’);
}else if (error.code === ‘InvalidParameterException’) {
console.error(‘Invalid sign-up parameters.’);
} else {
console.error(‘Sign-up error:’, error);
}
}
Testing User Registration Flow
- Run the application and attempt to sign up a new user.
- Verify the following points:
- The user is registered in the Cognito User Pool.
- The confirmation code is sent to the user’s email or phone.
- The user can enter the confirmation code and activate their account.
Once verified, the user can sign in.
Phase 3 – Implement User Sign-in Functionality Using AWS Cognito and AWS Amplify
By following the steps in this section you can implement the user sign-in functionality using AWS Cognito and AWS Amplify. This includes allowing users to log in with their credentials securely and, if enabled, handle MFA.
- Create a user sign-in form where users can input their email (or username) and password.
Example HTML/JSX form for react:
Jsx
<form onSubmit={handleSignIn}>
<input
type=”text”
placeholder=”Email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type=”password”
placeholder=”Password”
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type=”submit”>Sign In</button>
</form>
Note: Email and Password are required fields.
- Use the useState hook in react to manage the email and password fields.
javascript
const handleSignIn = async (event) => {
event.preventDefault();
try {
const user = await Auth.signIn(email, password);
console.log(‘Sign-in successful:’, user);
// Redirect user to dashboard or other secure area of your app
}catch (error) {
console.error(‘Sign-in error:’, error);
// Display user-friendly error message
}
};
- Import AWS Amplify’s OAuth module to handle the sign-in process.
Javascript
import { Auth } from ‘aws-amplify’;
- Add the sign-in functionality using AWS Amplify’s Auth.signIn() method. This function handles user authentication with AWS Cognito.
javascript
const handleSignIn = async (event) => {
event.preventDefault();
try {
const user = await Auth.signIn(email, password);
console.log(‘Sign-in successful:’, user);
// Redirect user to dashboard or other secure area of your app
} catch (error) {
console.error(‘Sign-in error:’, error);
// Display user-friendly error message
}
};
- If MFA is enabled for the user, add functionality to handle MFA when the user signs in.
javascript
const handleSignIn = async (event) => {
event.preventDefault();
try {
const user = await Auth.signIn(email, password);
if (user.challengeName === ‘SMS_MFA’ || user.challengeName === ‘SOFTWARE_TOKEN_MFA’) {
// Prompt user for the MFA code
const code = prompt(‘Enter the MFA code:’);
const loggedUser = await Auth.confirmSignIn(user, code, user.challengeName);
console.log(‘MFA verified, user signed in:’, loggedUser);
} else {
console.log(‘Sign-in successful:’, user);
}
} catch (error) {
console.error(‘Sign-in error:’, error);
}
};
- Store the tokens in local storage or session storage for the duration of the session.
javascript
const handleSignIn = async (event) => {
event.preventDefault();
try {
const user = await Auth.signIn(email, password);
console.log(‘Sign-in successful:’, user);
// Store tokens in local/session storage
localStorage.setItem(‘userToken’, user.signInUserSession.idToken.jwtToken);
// Redirect user to the dashboard or secured page
} catch (error) {
console.error(‘Sign-in error:’, error);
}
};
- Configure the catch and handle errors during sign-in, such as incorrect credentials or user not found.
- Run the application and try signing in with valid credentials.
- Verify the following points:
- The user is authenticated successfully.
- The JWT tokens are returned by AWS Cognito and can be used for session management.
- MFA is prompted if enabled and works as expected.
- Appropriate error messages are displayed for invalid sign-in attempts.
Phase 4 – Implement User Sign-out and Session Management
In this phase, let’s focus on handling user sign-out and managing user sessions to ensure users can securely log out and their sessions are properly managed.
Implementing User Sign-out
- Add a sign-out button in your application’s navigation or dashboard page where the user can easily log out.
Example button in react:
<pclass=”p3″>jsx
<button onClick={handleSignOut}>Sign Out</button>
- Implement the sign-out logic.
Javascript
import { Auth } from ‘aws-amplify’;
const handleSignOut = async () => {
try {
await Auth.signOut();
console.log(‘Sign-out successful’);
// Redirect the user to the home or login page
}catch (error) {
console.error(‘Sign-out error:’, error);
}
};
Managing User Session
- Check if the user is authenticated using Auth.currentAuthenticatedUser().
javascript
const checkUserSession = async () => {
try {
const user = await Auth.currentAuthenticatedUser();
console.log(‘User is signed in:’, user);
// User is signed in, allow access to protected resources
} catch (error) {
console.error(‘No user is signed in’);
// Redirect user to login page if no session is found
}
};
- Retrieve the user’s session tokens (ID token, Access token, Refresh token) using Auth.currentSession().
javascript
const getSessionTokens = async () => {
try {
const session = await Auth.current session();
console.log(‘Session tokens:’, session);
// Access session tokens: ID token, Access token, Refresh token
const idToken = session.getIdToken().getJwtToken();
const accessToken = session.getAccessToken().getJwtToken();
const refreshToken = session.getRefreshToken().getToken();
console.log({ idToken, accessToken, refreshToken });
}catch (error) {
console.error(‘Error retrieving session:’, error);
}
};
Handling Session Expiration
- Implement logic to check for token expiration and prompt the user to refresh their session or automatically refresh it using the refresh token.
Example of handling token expiration:
javascript
const handleSessionExpiration = async () => {
try {
const session = await Auth.currentSession();
const idTokenExpiration = session.getIdToken().getExpiration();
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime >= idTokenExpiration) {
console.log(‘Session has expired’);
// Optionally trigger a refresh or prompt the user to log in again
} else {
console.log(‘Session is still valid’);
}
} catch (error) {
console.error(‘Error checking session expiration:’, error);
}
};
- Configure to refresh tokens using refresh token automatically. AWS Cognito handles automatic token refresh using refresh tokens as long as the refresh token is still valid (default: 30 days). If the user’s Access token expires but their Refresh token is still valid, you can automatically refresh the session.
javascript
const refreshSession = async () => {
try {
const session = await Auth.currentSession();
console.log(‘Session refreshed:’, session);
// Automatically refreshes the session tokens if the Refresh token is still valid
} catch (error) {
console.error(‘Error refreshing session:’, error);
}
};
- In the handleSignOut function, redirect the user after a successful sign-out.
Javascript
const handleSignOut = async () => {
try {
await Auth.signOut();
console.log(‘Sign-out successful’);
// Redirect user to login or home page
window.location.href = ‘/login’; // Adjust the URL according to your app’s routing
}catch (error) {
console.error(‘Sign-out error:’, error);
}
};
Phase 5 – Implement Forgot Password
In this section, let’s focus on implementing the Forgot Password feature to enhance user security and improve user experience. This allows users to reset their password securely if they forget it.
Implementing Forgot Password Flow
- Create a form where users can enter their email to request a password reset.
Example HTML/JSX Form for React:
jsx
<form onSubmit={handleForgotPassword}>
<input
type=”text”
placeholder=”Enter your email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type=”submit”>Reset Password</button>
</form>
Note: The Email field is required for users to request the reset.
- Use AWS Amplify’s Auth.forgotPassword() method to trigger the password reset process.
javascript
import { Auth } from ‘aws-amplify’;
import { useState } from ‘react’;
const ForgotPassword = () => {
const [email, setEmail] = useState(”);
const handleForgotPassword = async (event) => {
event.preventDefault();
try {
const data = await Auth.forgotPassword(email);
console.log(‘Password reset initiated:’, data);
// Redirect user to confirm password page or prompt for verification code
} catch (error) {
console.error(‘Error initiating password reset:’, error);
}
};
return (
<form onSubmit={handleForgotPassword}>
<input
type=”text”
placeholder=”Enter your email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type=”submit”>Reset Password</button>
</form>
);
};
export default ForgotPassword;
- Create a form for the user to enter their email, verification code, and new password.
Example HTML/JSX Form for Password Confirmation:
jsx
<form onSubmit={handleConfirmPassword}>
<input
type=”text”
placeholder=”Enter your email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type=”text”
placeholder=”Enter verification code”
onChange={(e) => setCode(e.target.value)}
required
/>
<input
type=”password”
placeholder=”Enter new password”
onChange={(e) => setNewPassword(e.target.value)}
required
/>
<button type=”submit”>Confirm New Password</button>
</form>
- Use AWS Amplify’s Auth.forgotPasswordSubmit() method to confirm the password reset and allow the user to set a new password.
javascript
import { useState } from ‘react’;
import { Auth } from ‘aws-amplify’;
const ConfirmPassword = () => {
const [email, setEmail] = useState(”);
const [code, setCode] = useState(”);
const [newPassword, setNewPassword] = useState(”);
const handleConfirmPassword = async (event) => {
event.preventDefault();
try {
const data = await Auth.forgotPasswordSubmit(email, code, newPassword);
console.log(‘Password reset confirmed:’, data);
// Optionally redirect the user to the sign-in page
}catch (error) {
console.error(‘Error confirming password reset:’, error);
}
};
return (
<form onSubmit={handleConfirmPassword}>
<input
type=”text”
placeholder=”Enter your email”
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type=”text”
placeholder=”Enter verification code”
onChange={(e) => setCode(e.target.value)}
required
/>
<input
type=”password”
placeholder=”Enter new password”
onChange={(e) => setNewPassword(e.target.value)}
required
/>
<button type=”submit”>Confirm New Password</button>
</form>
);
};
export default ConfirmPassword;
- Implement error handling and display feedback to the user at each step of the password recovery process.
javascript
catch (error) {
if (error.code === ‘UserNotFoundException’) {
console.error(‘No user found with this email.’);
}else if (error.code === ‘CodeMismatchException’) {
console.error(‘Invalid verification code.’);
} else if (error.code === ‘ExpiredCodeException’) {
console.error(‘The verification code has expired.’);
} else {
console.error(‘An error occurred:’, error);
}
}
- Test the password reset request.
- Enter an email that exists in your Cognito User Pool.
- Ensure a verification code is sent to the user’s email or phone.
- Test the password confirmation
- Use the verification code to reset the password.
- Ensure the user can sign in with the new password after the reset is complete.
Troubleshoot and Debug
If you encounter issues with AWS Cognito integration, consider the following steps:
- Check AWS CloudWatch Logs: Review logs for error messages and details about failed authentication attempts.
- Verify Configuration Settings: Ensure that your Cognito user pool and identity pool settings match those configured in your application.
- Consult Documentation: Refer to the AWS Cognito documentation and AWS forums for additional support and troubleshooting tips.
Conclusion
As an application owner or developer, implementing secure user authentication is a critical step in ensuring your cloud applications are safe and user-friendly. AWS Cognito offers a robust, scalable solution for managing user sign-ups, sign-ins, and account recovery with features like MFA and password reset flows. By following the outlined phases—starting from creating a user pool, integrating AWS Cognito into your app, managing user sessions, to handling forgotten passwords—you can provide your users with a seamless and secure authentication experience. With AWS Amplify simplifying the integration process, you can focus on building your application, knowing that authentication is managed securely and efficiently.
- How to Create Secure User Authentication with AWS Cognito for Cloud Applications - September 30, 2024
- 2024 Roadmap to AWS Security Specialty Certification Success - August 16, 2024
- Top 25 AWS Full Stack Developer Interview Questions & Answers - August 14, 2024
- AWS Machine Learning Specialty vs Google ML Engineer – Difference - August 9, 2024
- Deploy a serverless architecture using AWS Lambda and Amazon API Gateway - August 9, 2024
- Mastering AWS SDK Integration in Node.js: A Step-by-Step Guide - August 8, 2024
- Is the AWS Certified Security Specialty Certification Right for You? - August 7, 2024
- How Google Cloud Architects Contribute To Business Transformation? - August 6, 2024