Getting Started with AWS Cognito Authentication in Next.js Using AWS Amplify

Bantawa Pawan
readytowork, Inc.
Published in
7 min readMar 24, 2023

--

AWS Cognito with Next js

In today’s digital age, security is of utmost importance for any web application. One of the most critical aspects of web application security is user authentication. AWS Cognito is a fully managed user authentication and identity management service offered by Amazon Web Services (AWS). With AWS Cognito, developers can easily add user sign-up, sign-in, and access control to their web applications. In this blog post, we will explore how to use AWS Cognito with Next.js — a popular React-based framework — to add secure user authentication to your web application.

AWS Cognito and its feature

Amazon Web Services (AWS) Cognito is a fully managed authentication and user management service that can be integrated into web and mobile applications. It allows developers to create user pools, which are user directories that manage user registration, authentication, and account recovery. When a user signs up or signs in to an application, AWS Cognito issues JSON Web Tokens (JWTs) that can be used to authenticate the user and authorize access to specific resources.

Features of AWS Cognito:

AWS Cognito provides a variety of features that can be used to create secure, scalable authentication flows. Some of the key features are:

  • User pools: User pools allow developers to manage user registration, authentication, and account recovery. They support a range of authentication methods, including email and password, phone number, and social identity providers.
  • Identity pools: Identity pools provide a way to give users access to AWS resources based on their authenticated identity. They allow users to obtain temporary AWS credentials that can be used to access other AWS services and resources.
  • Multi-factor authentication: AWS Cognito supports multi-factor authentication (MFA), which adds an extra layer of security to user authentication. Users can be required to provide a second factor, such as a code sent to their phone, in addition to their password.
  • Social identity providers: AWS Cognito supports social identity providers, such as Facebook, Google, and Amazon, which allow users to sign in to an application using their existing social media accounts.
  • Password policies: AWS Cognito allows developers to enforce password policies, such as password complexity requirements and password expiration.

Benefits of Using AWS Cognito

AWS Cognito provides several benefits for developers looking to add secure authentication and user management to their applications. Some of the key benefits include:

  • Scalability: AWS Cognito can scale to handle millions of users, making it suitable for applications with large user bases.
  • Security: AWS Cognito provides a range of security features, such as MFA and password policies, that can help protect user accounts and data.
  • Integration with other AWS services: AWS Cognito can be easily integrated with other AWS services, such as AWS Lambda and Amazon API Gateway, allowing developers to create secure and scalable serverless applications.
  • Flexibility: AWS Cognito supports a range of authentication methods, social identity providers, and password policies, allowing developers to customize the authentication flow to suit their application’s needs.

Setting up AWS Cognito

  1. Sign in to the AWS Management Console and navigate to the Cognito console.
  2. Click the “Manage User Pools” button to create a new user pool.
  3. Select an authentication provider for your app.
    (Select Federated Identity provider if you want to add social media/3rd party credentials)

4. Set your password policy, MFA, User account recovery method.
(I selected No MFA for this tutorial)

5. Configure sign-up experience for your app
— Check Enable self-registration if you want to register from your app
— Check Allow Cognito to automatically send messages to verify and confirm if you want cognito to send an email (user signup verification, password reset email )
— You can choose from the select option in Required attribute if you want other fields to be as required while creating a new user
— You can add your own fields to users from the custom attribute section.
(eg. role)
AWS Cognito lets you create mutable custom attributes that can be updated by the user or an administrator after the user account is created. In contrast, AWS Cognito offers read-only custom attributes that can only be set at user creation time and cannot be updated later.

6. Set your email provider for sending email

7. Give a name to your user pool and app client

8. Review your selection and click Confirm to create the user pool

Setting up Next js app

First, make sure that Node.js is installed on your machine. You can download and install Node.js from the official website.

Then,

yarn create next-app

or

yarn create next-app — typescript

Run yarn dev to start the development server on http://localhost:3000
Visit http://localhost:3000 to view your application

Configuring and implementing AWS amplify

Get the following config data from AWS and save it in your .env of next js app:

  • User pool ID
  • AWS project region
  • User pool app client ID

Initializing the configuration: In your app.tsx/app.js

import { Amplify } from "aws-amplify"
Amplify.configure({
Auth: {
verificationCodeValidity: 300, //verification code validity default 1 hrs, now set to 5 min
userPoolId: process.env.NEXT_PUBLIC_AWS_USER_POOL_ID,
region: process.env.NEXT_PUBLIC_AWS_PROJECT_REGION,
userPoolWebClientId: process.env.NEXT_PUBLIC_AWS_USER_POOL_APP_CLIENT_ID,
forgotPassword: {
// Limit password resets to 3 attempts per hrs
deliveryLimit: 3,
timeToLive: 60, // 60 minutes (1 hours)
},
},
})
function MyApp({ Component, pageProps }: AppProps) {
//other codes below

More on configuration:
https://docs.amplify.aws/lib/client-configuration/configuring-amplify-categories/q/platform/js/#scoped-configuration

Registering new user:

Create a user register page in pages/register path:

import React, { useState } from "react";
import { Auth } from "aws-amplify"
type User = {
email: string;
password: string;
};
const RegisterPage:React.FC = () => {
const [user, setUser] = useState<User>({
email: "",
password: "",
});
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setUser({ ...user, [event.target.name]: event.target.value });
};
const handleSubmit = async(event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
const result = await Auth.signUp({
username: user.email,
password: user.password,
// if custom attribute is added
attributes: {
"custom:role": "user",
},
})
return result
} catch (error) {
console.error("Error registering user:", error)
}
console.log("User registered:", user);
};
return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input
type="email"
name="email"
value={user.email}
onChange={handleInputChange}
required
/>
</label>
<br />
<label>
Password:
<input
type="password"
name="password"
value={user.password}
onChange={handleInputChange}
required
/>
</label>
<br />
<button type="submit">Register</button>
</form>
);
};
export default RegisterPage;

Sign in user with aws-amplify:

Create a login page in pages/login path:

import React, { useState } from "react";
import { Auth } from "aws-amplify"
type User = {
email: string;
password: string;
};
const LoginPage:React.FC = () => {
const [user, setUser] = useState<User>({
email: "",
password: "",
});
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setUser({ ...user, [event.target.name]: event.target.value });
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try{
// Send the user data to the server to login the user
await Auth.signIn(formik.values.email, formik.values.password)
console.log("User logged in:", user);
} catch(e){
concole.log(e)
}
};
return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input
type="email"
name="email"
value={user.email}
onChange={handleInputChange}
required
/>
</label>
<br />
<label>
Password:
<input
type="password"
name="password"
value={user.password}
onChange={handleInputChange}
required
/>
</label>
<br />
<button type="submit">Login</button>
</form>
);
};
export default LoginPage;

To listen to the auth status change we need to Hub in our app.tsx

import { Amplify, Hub, Auth } from "aws-amplify";
interface IUser {
userId: string
email: string
email_verified: boolean
role: string
}
function MyApp({ Component, pageProps }: AppProps) {
const [user, setUser] = useState(null)
const getCurrentSession = async () => {
try {
const session = await Auth.currentSession();
const sessionData = session.getIdToken();
if (sessionData) {
const { payload } = sessionData;
//"custom:role": role if custom attribute is added
const { email, sub, email_verified, "custom:role": role } = payload;
const user: IUser = {
userId: sub,
email: email,
email_verified: email_verified,
role: role,
};
setUser(user);
}
} catch (e) {
console.log(e)
}
};
const initialLoad = useCallback(async () => {
Hub.listen("auth", async ({ payload: { event, data } }) => {
switch (event) {
case "signIn": {
const role = data?.attributes["custom:role"];
//if role doesn't match as defined logout
if (role === "admin") {
antdMsg.error({
content: "Not authenticated",
});
await Auth.signOut();
break;
}
const user: IUser = {
userId: data?.username,
email: data?.attributes?.email,
email_verified: data?.attributes?.email_verified,
role: role,
};
setUser(user);
//set user data to redux/context
break;
}
case "signUp":
break;
case "signOut":
setUser(null);
//clear user data in redux/context
break;
case "signIn_failure":
break;
default:
}
});
}, []);
useEffect(() => {
initialLoad();
getCurrentSession();
}, []);
return (
//other codes below

Conclusion

In conclusion, we can say that the aws-amplify the package provides a simple and convenient way to integrate authentication with AWS Cognito in Next.js applications. However, it may not cover all use cases or provide the level of customization required for more complex applications. Therefore, it may be necessary to use the aws-sdk package directly to access the full range of Cognito functionality. In future blog posts, we will explore the use of this package for authentication with Cognito in Next.js and other topics related to it.

--

--