Next Auth with Github
.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
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/userAvatar.tsx
import { auth } from "@/auth";
import * as stylex from "@stylexjs/stylex";
import { colors, size } from "../app/tokens.stylex";
const styles = stylex.create({
avatar: {
borderRadius: "100%",
height: "3em",
width: "3em",
},
});
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","");
return (
<div>
<img
{...stylex.props(styles.avatar)}
src={session.user && session.user.image || ""} alt="User Avatar" />
<span>Hello {session.user.name}</span>
</div>
)
}
package.json
{
"name": "next-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": {
"@babel/runtime": "^7.26.7",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.2.4",
"@shikijs/rehype": "^2.2.0",
"@stylexjs/open-props": "^0.7.5",
"@stylexjs/stylex": "^0.7.5",
"autoprefixer": "^10.4.20",
"esbuild": "^0.24.2",
"next": "^15.2.4",
"next-auth": "5.0.0-beta.25",
"next-view-transitions": "^0.3.4",
"react": "^18",
"react-dom": "^18",
"rehype-katex": "^7.0.1",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0"
},
"devDependencies": {
"@stylexjs/eslint-plugin": "^0.7.5",
"@stylexjs/postcss-plugin": "0.10.1",
"@types/json-schema": "^7.0.15",
"@types/mdx": "^2.0.13",
"@types/node": "^22.7.6",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.57.1",
"eslint-config-next": "15.2.4",
"rimraf": "^5.0.10",
"typescript": "^5.3.3"
}
}
app/page.tsx
import React from "react";
import stylex from "@stylexjs/stylex";
import Header from "@/components/header";
import SignIn from "@/components/signIn";
import UserAvatar from "@/components/UserAvatar";
import { colors } from "./tokens.stylex";
import { SessionProvider } from "next-auth/react";
export default async function Home() {
return (
<>
<SessionProvider>
<Header />
<main {...stylex.props(styles.main)}>
<div {...stylex.props(styles.content)}>
{children} // content
</div>
</main>
<div {...stylex.props(styles.signin)}>
<div>
<SignIn />
<UserAvatar />
</div>
</div>
</SessionProvider>
</>
);
}
const styles = stylex.create({
main: {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
},
content: {
fontFamily: "system-ui, sans-serif",
overflow: "hidden",
width: {
"@media (max-width: 1400px)": "90%",
default: "60%",
},
paddingBlockStart: {
default: 16,
"@media (min-width: 800px)": 32,
},
paddingBlockEnd: {
default: 16,
"@media (min-width: 800px)": 32,
},
},
text: {
color: colors.neutral100,
},
signin: {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
}
});
const MEDIA_MOBILE = "@media (max-width: 700px)" as const;
const MEDIA_TABLET =
"@media (min-width: 701px) and (max-width: 1120px)" as const;