Introduction#

GitHub this shows how to setup nextauth with amplify hosting and your own domain

  • nextjst and nextauth bare
  • amplify hosting
  • my own domain and auth

Setup Project#

let create a new nextjs project

npx create-next-app@latest .

install next-auth

npm install next-auth

Project structure

|--app
|--globals.css
|--layout.css
|--page.tsx
|--pages
|--api
|--auth
|--[...nextauth].js
|--public
|--.env.local
|--next.config.js
|--package.json
|--postcss.config.js
|--tailwind.config.js
|--tsconfig.json
|--.eslintrc.json

Cognito Provider#

Let update [...nextauth].js in pages/api/auth/

import NextAuth from "next-auth";
import CognitoProvider from "next-auth/providers/cognito";
export const authOptions = {
providers: [
CognitoProvider({
clientId: process.env.COGNITO_CLIENT_ID,
clientSecret: process.env.COGNITO_CLIENT_SECRET,
issuer: process.env.COGNITO_ISSUER,
}),
],
secret: process.env.JWT_SECRET,
pages: {},
events: {
async signOut(message) {
console.log(message);
},
},
session: {
// seconds - how long until a idle session expires
maxAge: 5 * 60,
// seconds - how frequently to write to database to extend a session
updateAge: 60 * 60,
//
clientMaxAge: 5 * 60,
//
strategy: "jwt",
},
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
return true;
},
async redirect({ url, baseUrl }) {
return baseUrl;
},
async session({ session, user, token }) {
try {
session.id_token = token.id_token;
} catch (error) {}
return session;
},
async jwt({ token, user, account, profile, isNewUser }) {
try {
token.id_token = account.id_token;
// console.log("callback jwt ", token);
} catch (error) {}
return token;
},
},
};
export default NextAuth(authOptions);

Login Page#

In the NextJS 13, we can setup it in the RootLayout, and use SessionProvider to pass session of user as the following

"use client";
import { SessionProvider } from "next-auth/react";
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<SessionProvider>
<body>{children}</body>
</SessionProvider>
</html>
);
}

The Log In page with login button which will trigger Provider as

"use client";
import { signIn, signOut, useSession } from "next-auth/react";
export default function Home() {
const { data: session } = useSession();
console.log(session);
return (
<main className="flex min-h-screen flex-col items-center justify-center space-y-5">
{session?.user ? (
<>
<h1>Hello {session.user.email}</h1>
<button
className="bg-orange-300 px-20 py-3 rounded-sm"
onClick={() => signOut()}
>
Sign Out
</button>
</>
) : (
<>
<button
type="submit"
// disabled
className="bg-green-400 px-20 py-3 rounded-sm cursor-pointer"
onClick={() => signIn("cognito")}
>
Sign In
</button>
</>
)}
</main>
);
}

Server Side#

Let create a protected page in /app/profile/page.tsx

  • Get cookies or token from header request
  • Verify the cookies or token, get user information
  • Server render the responding page
import { cookies, headers } from "next/headers";
import { decode } from "next-auth/jwt";
const getUser = async () => {
const authHeader = headers().get("authorization");
const sessionToken = cookies().get("next-auth.session-token");
const user = await decode({
token: sessionToken?.value,
secret: process.env.JWT_SECRET as string,
});
console.log("auth header ", authHeader);
console.log("session token from cookies ", sessionToken);
console.log("decode token ", user);
return user;
};
const ProfilePage = async () => {
const user = await getUser();
if (user) {
return (
<div>
<h1>
Hello {user.email} {user.name}{" "}
</h1>
</div>
);
}
return (
<div>
<h1>Please log in first</h1>
</div>
);
};
export default ProfilePage;

Cognito UserPool#

  • create a cognito user pool
  • create client with secret id

The allowed callback URLs look like this

https://en.entest.io/api/auth/callback/cognito

The cognito issuer

COGNITO_ISSUER=https://cognito-idp.us-east-1.amazonaws.com/${USER_POOL_ID}

The cognito user pool id

COGNITO_POOL_ID="cognito-idp.us-east-1.amazonaws.com/${USER_POOL_ID}"

Amplify Hosting#

  • setup environmt variables
  • setup building as below

Build setting

version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- echo "NEXTAUTH_URL=$NEXTAUTH_URL" >> .env
- echo "NEXTAUTH_SECRET=$NEXTAUTH_SECRET" >> .env
- echo "COGNITO_ISSUER=$COGNITO_ISSUER" >> .env
- echo "COGNITO_CLIENT_SECRET=$COGNITO_CLIENT_SECRET" >> .env
- echo "COGNITO_CLIENT_ID=$COGNITO_CLIENT_ID" >> .env
- echo "JWT_SECRET=$JWT_SECRET" >> .env
- npm run build
artifacts:
baseDirectory: .next
files:
- '**/*'
cache:
paths:
- node_modules/**/*

Troubleshooting#

  • delete the cache in build setting and rebuild
  • wait a hour in case of deleting route 53 CNAME record