Enterprise-Level Authentication in a Containerized Environment for Next.js 13 - AuthJS Patch

Enterprise-Level Authentication in a Containerized Environment for Next.js 13 - AuthJS Patch

Recently, I've published a step by step guide on Enterprise-Level Authentication in a Containerized Environment for NextJS and while searching on the internet I saw a post on Keycloak groups. benmarte was having a problem while integrating Keycloak using auth.js instead of next-auth. In this post I'll explain how you can handle authentication using auth.js.

To get the most out of this post please setup your environment as I shared in my previous post.

TL;DR

https://github.com/ozdemirrulass/nextjs-keycloak-authjs

NextJS 13 Keycloak Integration Using AuthJS

  1. Install AuthJS as it is suggested in official documentation:
npm install next-auth@beta
  1. Create .env.local file in the root directory of your next-app project and add the following:
AUTH_SECRET=secret

NEXT_PUBLIC_KEYCLOAK_REALM=<realm-name>
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=<client-name>
KEYCLOAK_CLIENT_SECRET=<client-secret-from-keycloak>
NEXT_LOCAL_KEYCLOAK_URL="http://localhost:8080"
NEXT_CONTAINER_KEYCLOAK_ENDPOINT="http://keycloak:8080"
💡
You can use following commands to produce AUTH_SECRET value. This secret is used to sign and encrypt cookies.
💡
You can see where to find other values in my PREVIOUS POST.
openssl rand -base64 33

or

npx auth secret
  1. Create /types folder in your projects root and create node-env.d.ts file in it.
// /types/node-env.d.ts
declare namespace NodeJS {
    export interface ProcessEnv {
      NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: string
      KEYCLOAK_CLIENT_SECRET: string
      NEXT_LOCAL_KEYCLOAK_URL: string
      NEXT_PUBLIC_KEYCLOAK_REALM: string
      NEXT_CONTAINER_KEYCLOAK_ENDPOINT: string
    }
  }
  1. Create auth.ts file under your /src directory and add the following code:
// /src/auth.ts
import NextAuth from "next-auth"
import Keycloak from "next-auth/providers/keycloak";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Keycloak({
      jwks_endpoint: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/certs`,
      wellKnown: undefined,
      clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
      issuer: `${process.env.NEXT_LOCAL_KEYCLOAK_URL}/realms/${process.env.NEXT_PUBLIC_KEYCLOAK_REALM}`,
      authorization: {
        params: {
          scope: "openid email profile",
        },
        url: `${process.env.NEXT_LOCAL_KEYCLOAK_URL}/realms/myrealm/protocol/openid-connect/auth`,
      },
      token: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/token`,
      userinfo: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/userinfo`,
    }),
  ],
});
💡
This part is important! We must use any kind of parameter indicating urls as localhost but urls used for sending a request must use container name for connectivity purposes through docker network.
  1. Now it's time to create our route.ts and it must be in /src/app/api/auth/[...nextauth]/route.ts
// /src/app/api/auth/[...nextauth]/route.ts

import { handlers } from "@/auth";
export const { GET, POST } = handlers;
  1. We are going to need a Sign-In component! Let's create It. Create a components folder under your /src directory and create a file as sign-in.tsx.
// /src/components/sign-in.tsx

import { signIn } from "@/auth"

export function SignIn() {
  return (
    <form
      action={async () => {
        "use server"
        await signIn("keycloak")
      }}
    >
      <button type="submit">Signin with Keycloak</button>
    </form>
  )
}
  1. It's time to replace our src/app/page.tsx :
// src/app/page.tsx
import { auth } from "@/auth";
import { SignIn } from "../components/sign-in";

export default async function Home() {
  const session = await auth();
  if (session) {
    return (
      <div>
        <div>Your name is {session.user?.name}</div>
      </div>
    );
  }
  return (
    <div>
      <SignIn />
    </div>
  );
}

Congratulations 👏 You've done it. You just implemented an Enterprise-Level Authentication in a Containerized Environment for Next.js 13 using AuthJS instead of NextAuth !

docker compose -f docker-compose.dev.yml build
docker compose -f docker-compose.dev.yml up -d

Thank you and see you soon!