Next Auth with Github

Server Side Authentication with NextAuth.js and GitHub

https://authjs.dev/getting-started/session-management/protecting

https://authjs.dev/getting-started/migrating-to-v5#authenticating-server-side

.env.local

AUTH_SECRET="changemefajsflajsflajksf;laj=" # Added by `npx auth secret`. Read more: https://cli.authjs.dev
GITHUB_ID=3123123123example
GITHUB_SECRET=3123123123example3123123123example

app/api/auth/[...nextauth]/route.ts

import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers

auth.ts

import NextAuth from "next-auth"
import GitHubProvider from "next-auth/providers/github";

if (!process.env.GITHUB_ID || !process.env.GITHUB_SECRET)
    throw new Error("Failed to initialize Github authentication");

if (!process.env.AUTH_SECRET)
    throw new Error("Failed to find AUTH_SECRET, run `npx auth secret` and add to .env.local or prod/preview credentials");

export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: [
        GitHubProvider({
            clientId: process.env.GITHUB_ID,
            clientSecret: process.env.GITHUB_SECRET,
            // the below doesn't seem to work
            //
            // profile(profile) {
            //   return {
            //     id: profile.id.toString(),
            //     name: profile.name || profile.login,
            //     gh_username: profile.login,
            //     email: profile.email,
            //     image: profile.avatar_url
            //   };
            // }
            //
            // instead I get
            //
            // {
            //   user: {
            //     name: 'Ian Cleary',
            //     email: 'redacted@redacted.com',
            //     image: 'https://avatars.githubusercontent.com/u/12312redacted123123?v=4'
            //   },
            //   expires: '2025-05-17T04:15:53.305Z'
            // }
            // as the session
        }),
    ],
})

components/auth/userAvatar.tsx

import { auth } from "@/auth";

import Image from "next/image";

export default async function UserAvatar() {
    const session = await auth()

    if (!session?.user) return null
    console.log(session);
    // {
    //   user: {
    //     name: 'Ian Cleary',
    //     email: 'redacted@redacted.com',
    //     image: 'https://avatars.githubusercontent.com/u/12312redacted123123?v=4'
    //   },
    //   expires: '2025-05-17T04:15:53.305Z'
    // }

    // const userIdWithParams = session.user?.image?.replace("https://avatars.githubusercontent.com/u/","");
    // const userId = userIdWithParams?.replace("?v=4","");

    if (!session?.user) {
      return (
        <div className="rounded-full mt-1 bg-neutral-950 h-8 w-8 border-0">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className="size-8"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
            />
          </svg>
        </div>
      );
    }
    return (
    <div>
      <Image
        className="rounded-full bg-neutral-950 border-0"
        src={(session.user && session.user.image) || ""}
        alt="User Avatar"
        width={32}
        height={32}
      />
      {/* <span>Hello {session.user.name}</span> */}
    </div>
  );
}

signIn.tsx

"use client";
import { useSession, signIn, signOut } from "next-auth/react";

export default function SignIn() {
  const { data: session } = useSession();
  if (session) {
    return (
      <div className="p-8 bg-neutral-600">
        {/* Signed in as {session.user.name} <br />{" "} */}
        <button onClick={() => signOut()}>Sign out</button>
      </div>
    );
  }
  return (
    <div className="p-4 bg-neutral-600 rounded-md">
      <button className="rounded bg-neutral-700 w-full mb-2" onClick={() => signIn()}><div className="m-2">Sign in</div></button>
      <i className="">(Only works for Ian Cleary)</i>
    </div>
  );
}

package.json

{
  "name": "next-auth-example",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "clean": "rimraf .next",
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "next:lint": "next lint"
  },
  "dependencies": {
    // ...
    "next": "^15.4.1",
    "next-auth": "5.0.0-beta.27",
    //...
  },
  "devDependencies": {
    // ...
  }
}