-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Amazon Cognito is an identity platform for web and mobile apps.
It’s a user directory, an authentication server, and an authorization service for OAuth 2.0 access tokens and AWS credentials.
With Amazon Cognito, you can authenticate and authorize users from the built-in user directory, from your enterprise directory, and from consumer identity providers like Google and Facebook.
- User pool authentication flow
- Choose an Amazon Cognito authentication flow for enterprise applications
- Common Amazon Cognito scenarios
- Should I use the hosted UI or create a custom UI in Amazon Cognito?
AWS Cognito provides multiple authentication flows to cater to different security and user experience needs. These flows ensure flexible, secure, and efficient authentication for your applications.
-
USER_PASSWORD_AUTH:
- Users authenticate using their username and password.
- The application directly sends the credentials to AWS Cognito for verification.
-
ALLOW_CUSTOM_AUTH:
- Supports custom authentication flows.
- Developers can define their own challenge/response mechanisms for user authentication.
-
ALLOW_USER_SRP_AUTH:
- Secure Remote Password (SRP) protocol is used for authentication.
- Provides enhanced security by allowing password verification without sending the actual password over the network.
-
ALLOW_REFRESH_TOKEN_AUTH:
- Uses a refresh token to obtain a new access token without re-authenticating the user.
- Useful for maintaining long-lived sessions without repeatedly asking for user credentials.
USER_SRP_AUTH: The USER_SRP_AUTH flow uses the SRP protocol (Secure Remote Password) where the password never leaves the client and is unknown to the server. This is the recommended flow and is used by default.
USER_SRP_AUTH takes in USERNAME and SRP_A and returns the SRP variables to be used for next challenge execution. USER_PASSWORD_AUTH takes in USERNAME and PASSWORD and returns the next challenge or tokens.
App clients create integration between your app and your user pool. App clients can use their own subset of authentication flows, token characteristics, and security from your user pool.
- Using social identity providers with a user pool
- Adding user pool sign-in through a third party
- User pool federation endpoints and hosted UI reference
This table summarizes the three different approaches to designing a login authentication system in AWS.
AWS provides various tools and services to help developers design and implement robust login authentication systems for their web and mobile applications. This table highlights three different approaches to designing a login authentication system in AWS:
Title | Approach | Description | Tools | Services |
---|---|---|---|---|
Amplify | Create a React app by using AWS Amplify and add authentication with Amazon Cognito | Demonstrates how to use AWS Amplify to create a React-based app and add authentication with Amazon Cognito. | Amplify Libraries, Amplify Studio | AWS Amplify, Amazon Cognito |
AWS-UI | Integrating Amazon Cognito authentication and authorization with web and mobile apps | Provides a comprehensive guide to integrating Amazon Cognito authentication and authorization with web and mobile apps. | AWS Amplify, Amazon Cognito Console | AWS Amplify, Amazon Cognito |
custom | Set up an example React single page application | Creates a React single page application for testing user sign-up, confirmation, and sign-in with Amazon Cognito. | React, Amazon Cognito Console | Amazon Cognito, AWS SDK JS v3 |
Note
An identity user pool is a service provided by platforms like AWS Cognito that helps manage user authentication and authorization.
It allows you to create and maintain a user directory, where users can sign up, sign in, and manage their profiles.
The user pool handles the storage and verification of user credentials, such as passwords and multi-factor authentication (MFA).
It also supports integration with social identity providers like Google and Facebook. This simplifies building secure applications by handling user management and authentication workflows for developers.
You can also check how to create a identity pool as a guest
- Amazon Cognito Identity Provider examples using SDK for JavaScript (v3)
- CognitoIdentityProviderClient
- React Router useNavigate
The AWS SDK for JavaScript v3 enables you to easily work with Amazon Web Services , but has a modular architecture with a separate package for each service.
It also includes many frequently requested features, such as first-class TypeScript support and a new middleware stack
Note
With the Amazon Cognito user pools API, you can configure user pools and authenticate users. To authenticate users from third-party identity providers (IdPs) in this API, you can link IdP users to native user profiles
We will create a React single page application where we can test:
- user
sign-up
, -
confirmation
, -
sign-in
, -
forgot password
, resend code
- and
log-out
.
This example application demonstrates some basic functions of Amazon Cognito user pools.
If you're already experienced in web app development with React, download the example app from GitHub from AWS repo and documetation.
You also would clone these repos:
- code github TS: sign-up, sign-in, confirmation, log-out (basic version) with TypeScript
- code github JS: sign-up, sign-in, confirmation, log-out (basic version) with JS
- code github JS: basic version with JS and Forgot password feature
- code github JS: basic version with JS, forgot password feature and Resend CODE
react-router-dom
npm i react-router-dom
client-cognito-identity-provider
AWS SDK for JavaScript CognitoIdentityProvider Client for Node.js, Browser and React Native.
With the Amazon Cognito user pools API, you can configure user pools and authenticate users. To authenticate users from third-party identity providers (IdPs) in this API, you can link IdP users to native user profiles. Learn more about the authentication and authorization of federated users at Adding user pool sign-in through a third party and in the User pool federation endpoints and hosted UI reference.
npm i @aws-sdk/client-cognito-identity-provider
isAuthenticated Function
This is a JavaScript function named isAuthenticated
that checks if a user is authenticated based on the presence of an access token in the browser's session storage.
const isAuthenticated = () => {
const accessToken = sessionStorage.getItem('accessToken');
return!!accessToken;
};
-
const isAuthenticated = () => {
: This line defines a function namedisAuthenticated
that takes no arguments and returns a value. -
const accessToken = sessionStorage.getItem('accessToken');
: This line retrieves the value of a key named'accessToken'
from the browser's session storage. Session storage is a mechanism that allows storing small amounts of data locally in the browser, and it is cleared when the user closes the browser. -
return!!accessToken;
: This line returns a boolean value indicating whether the user is authenticated or not.
The !!
operator is used to convert the accessToken
value to a boolean. In JavaScript, any value can be converted to a boolean using the !!
operator. Here's how it works:
- If
accessToken
is a truthy value (e.g., a string, a number, or an object), the!!
operator returnstrue
. - If
accessToken
is a falsy value (e.g.,null
,undefined
, or an empty string), the!!
operator returnsfalse
. In this specific case, ifaccessToken
is present in the session storage, the function returnstrue
, indicating that the user is authenticated. IfaccessToken
is not present, the function returnsfalse
, indicating that the user is not authenticated.
The purpose of this function is likely to check whether the user has already authenticated and obtained an access token, which can be used to authorize subsequent requests to a server or API.
App.tsx
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import LoginPage from './loginPage';
import HomePage from './homePage';
import ConfirmUserPage from './confirmUserPage';
import './App.css'
const App = () => {
const isAuthenticated = () => {
const accessToken = sessionStorage.getItem('accessToken');
return !!accessToken;
};
return (
<BrowserRouter>
<Routes>
<Route path="/" element={isAuthenticated() ? <Navigate replace to="/home" /> : <Navigate replace to="/login" />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/confirm" element={<ConfirmUserPage />} />
<Route path="/home" element={isAuthenticated() ? <HomePage /> : <Navigate replace to="/login" />} />
</Routes>
</BrowserRouter>
);
};
export default App;
Note
It is important to imports connection ids: import config from "./config.json";
AuthService.tsx
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { CognitoIdentityProviderClient, InitiateAuthCommand, SignUpCommand, ConfirmSignUpCommand } from "@aws-sdk/client-cognito-identity-provider";
import config from "./config.json";
export const cognitoClient = new CognitoIdentityProviderClient({
region: config.region,
});
export const signIn = async (username: string, password: string) => {
const params = {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: config.clientId,
AuthParameters: {
USERNAME: username,
PASSWORD: password,
},
};
try {
const command = new InitiateAuthCommand(params);
const { AuthenticationResult } = await cognitoClient.send(command);
if (AuthenticationResult) {
sessionStorage.setItem("idToken", AuthenticationResult.IdToken || '');
sessionStorage.setItem("accessToken", AuthenticationResult.AccessToken || '');
sessionStorage.setItem("refreshToken", AuthenticationResult.RefreshToken || '');
return AuthenticationResult;
}
} catch (error) {
console.error("Error signing in: ", error);
throw error;
}
};
export const signUp = async (email: string, password: string) => {
const params = {
ClientId: config.clientId,
Username: email,
Password: password,
UserAttributes: [
{
Name: "email",
Value: email,
},
],
};
try {
const command = new SignUpCommand(params);
const response = await cognitoClient.send(command);
console.log("Sign up success: ", response);
return response;
} catch (error) {
console.error("Error signing up: ", error);
throw error;
}
};
export const confirmSignUp = async (username: string, code: string) => {
const params = {
ClientId: config.clientId,
Username: username,
ConfirmationCode: code,
};
try {
const command = new ConfirmSignUpCommand(params);
await cognitoClient.send(command);
console.log("User confirmed successfully");
return true;
} catch (error) {
console.error("Error confirming sign up: ", error);
throw error;
}
};
LoginPage.tsx
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { signIn, signUp } from './authService';
const LoginPage = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isSignUp, setIsSignUp] = useState(false);
const navigate = useNavigate();
const handleSignIn = async (e: { preventDefault: () => void; }) => {
e.preventDefault();
try {
const session = await signIn(email, password);
console.log('Sign in successful', session);
if (session && typeof session.AccessToken !== 'undefined') {
sessionStorage.setItem('accessToken', session.AccessToken);
if (sessionStorage.getItem('accessToken')) {
window.location.href = '/home';
} else {
console.error('Session token was not set properly.');
}
} else {
console.error('SignIn session or AccessToken is undefined.');
}
} catch (error) {
alert(`Sign in failed: ${error}`);
}
};
const handleSignUp = async (e: { preventDefault: () => void; }) => {
e.preventDefault();
if (password !== confirmPassword) {
alert('Passwords do not match');
return;
}
try {
await signUp(email, password);
navigate('/confirm', { state: { email } });
} catch (error) {
alert(`Sign up failed: ${error}`);
}
};
return (
<div className="loginForm">
<h1>Welcome</h1>
<h4>{isSignUp ? 'Sign up to create an account' : 'Sign in to your account'}</h4>
<form onSubmit={isSignUp ? handleSignUp : handleSignIn}>
<div>
<input
className="inputText"
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
</div>
<div>
<input
className="inputText"
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
</div>
{isSignUp && (
<div>
<input
className="inputText"
id="confirmPassword"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm Password"
required
/>
</div>
)}
<button type="submit">{isSignUp ? 'Sign Up' : 'Sign In'}</button>
</form>
<button onClick={() => setIsSignUp(!isSignUp)}>
{isSignUp ? 'Already have an account? Sign In' : 'Need an account? Sign Up'}
</button>
</div>
);
};
export default LoginPage;
Function: parseJwt
This function takes a JSON Web Token (JWT) as input and returns the decoded JSON payload.
The parseJwt
function is used to extract the JSON payload from a JWT. A JWT is a string that contains three parts:
- Header
- Payload (also known as the claim set)
- Signature
The function takes the JWT as a string and extracts the second part (the payload) by splitting the string at the dots (.
). It then replaces any hyphens (-
) and underscores (_
) with their corresponding URL-safe characters (+
and /
).
Next, the function uses the window.atob
method to decode the base64-encoded payload. The decoded payload is then converted to a string using the split`` and
mapmethods. The
mapmethod is used to convert each character in the string to its corresponding hexadecimal value using the
charCodeAtand
toString` methods.
Finally, the function uses the JSON.parse
method to parse the decoded payload into a JSON object.
Note: The /*eslint-disable*/
comment at the top of the code is used to disable ESLint warnings for this specific function. This is because the function uses window.atob
, which is not recommended in modern JavaScript code. However, in this case, it is used to maintain backwards compatibility with older browsers.
HomePage.tsx
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { useNavigate } from 'react-router-dom';
/*eslint-disable*/
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
const HomePage = () => {
const navigate = useNavigate();
var idToken = parseJwt(sessionStorage.idToken.toString());
var accessToken = parseJwt(sessionStorage.accessToken.toString());
console.log ("Amazon Cognito ID token encoded: " + sessionStorage.idToken.toString());
console.log ("Amazon Cognito ID token decoded: ");
console.log ( idToken );
console.log ("Amazon Cognito access token encoded: " + sessionStorage.accessToken.toString());
console.log ("Amazon Cognito access token decoded: ");
console.log ( accessToken );
console.log ("Amazon Cognito refresh token: ");
console.log ( sessionStorage.refreshToken );
console.log ("Amazon Cognito example application. Not for use in production applications.");
const handleLogout = () => {
sessionStorage.clear();
navigate('/login');
};
/*eslint-enable*/
return (
<div>
<h1>Hello World</h1>
<p>See console log for Amazon Cognito user tokens.</p>
<button onClick={handleLogout}>Logout</button>
</div>
);
};
export default HomePage;
ConfirmPage.tsx
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { confirmSignUp } from './authService';
const ConfirmUserPage = () => {
const navigate = useNavigate();
const location = useLocation();
// eslint-disable-next-line
const [email, setEmail] = useState(location.state?.email || '');
const [confirmationCode, setConfirmationCode] = useState('');
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
await confirmSignUp(email, confirmationCode);
alert("Account confirmed successfully!\nSign in on next page.");
navigate('/login');
} catch (error) {
alert(`Failed to confirm account: ${error}`);
}
};
return (
<div className="loginForm">
<h2>Confirm Account</h2>
<form onSubmit={handleSubmit}>
<div>
<input
className="inputText"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
</div>
<div>
<input
className="inputText"
type="text"
value={confirmationCode}
onChange={(e) => setConfirmationCode(e.target.value)}
placeholder="Confirmation Code"
required />
</div>
<button type="submit">Confirm Account</button>
</form>
</div>
);
};
export default ConfirmUserPage;
Resend code