Skip to content

Commit 6285a67

Browse files
committed
2025.02.10 first steps - auth config
1 parent 6a6ee36 commit 6285a67

File tree

15 files changed

+4023
-65
lines changed

15 files changed

+4023
-65
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import { handlers } from '@/auth';
2+
export const { GET, POST } = handlers;

app/globals.css

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,23 @@
33
@tailwind utilities;
44

55
:root {
6-
--background: #ffffff;
6+
--background: #f4f4f4;
77
--foreground: #171717;
8+
--texture-url: url('/texture-light.png');
89
}
910

1011
@media (prefers-color-scheme: dark) {
1112
:root {
12-
--background: #0a0a0a;
13+
--background: #303030;
1314
--foreground: #ededed;
15+
--texture-url: url('/texture-dark.png');
1416
}
1517
}
1618

1719
body {
1820
color: var(--foreground);
19-
background: var(--background);
2021
font-family: Arial, Helvetica, sans-serif;
22+
23+
background: var(--background) var(--texture-url) repeat;
24+
background-size: 100px 100px;
2125
}

app/layout.tsx

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
import type { Metadata } from "next";
2-
import { Geist, Geist_Mono } from "next/font/google";
3-
import "./globals.css";
1+
import type { Metadata } from 'next';
2+
import { Geist, Geist_Mono } from 'next/font/google';
3+
import './globals.css';
4+
import { SessionProvider } from 'next-auth/react';
45

56
const geistSans = Geist({
6-
variable: "--font-geist-sans",
7-
subsets: ["latin"],
7+
variable: '--font-geist-sans',
8+
subsets: ['latin'],
89
});
910

1011
const geistMono = Geist_Mono({
11-
variable: "--font-geist-mono",
12-
subsets: ["latin"],
12+
variable: '--font-geist-mono',
13+
subsets: ['latin'],
1314
});
1415

1516
export const metadata: Metadata = {
16-
title: "Create Next App",
17-
description: "Generated by create next app",
17+
title: 'Create Next App',
18+
description: 'Generated by create next app',
1819
};
1920

2021
export default function RootLayout({
@@ -24,10 +25,8 @@ export default function RootLayout({
2425
}>) {
2526
return (
2627
<html lang="en">
27-
<body
28-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
29-
>
30-
{children}
28+
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
29+
<SessionProvider>{children}</SessionProvider>
3130
</body>
3231
</html>
3332
);

app/page.tsx

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,28 @@
1-
import Image from "next/image";
1+
import SigninButton from '@/components/signin-button/signin-button';
2+
import Image from 'next/image';
23

34
export default function Home() {
45
return (
56
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
67
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
7-
<Image
8-
className="dark:invert"
9-
src="/next.svg"
10-
alt="Next.js logo"
11-
width={180}
12-
height={38}
13-
priority
14-
/>
8+
<Image className="dark:invert" src="/next.svg" alt="Next.js logo" width={180} height={38} priority />
159
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
1610
<li className="mb-2">
17-
Get started by editing{" "}
18-
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
19-
app/page.tsx
20-
</code>
21-
.
11+
Get started by editing{' '}
12+
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">app/page.tsx</code>.
2213
</li>
2314
<li>Save and see your changes instantly.</li>
2415
</ol>
2516

2617
<div className="flex gap-4 items-center flex-col sm:flex-row">
18+
<SigninButton />
2719
<a
2820
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
2921
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
3022
target="_blank"
3123
rel="noopener noreferrer"
3224
>
33-
<Image
34-
className="dark:invert"
35-
src="/vercel.svg"
36-
alt="Vercel logomark"
37-
width={20}
38-
height={20}
39-
/>
25+
<Image className="dark:invert" src="/vercel.svg" alt="Vercel logomark" width={20} height={20} />
4026
Deploy now
4127
</a>
4228
<a
@@ -56,13 +42,7 @@ export default function Home() {
5642
target="_blank"
5743
rel="noopener noreferrer"
5844
>
59-
<Image
60-
aria-hidden
61-
src="/file.svg"
62-
alt="File icon"
63-
width={16}
64-
height={16}
65-
/>
45+
<Image aria-hidden src="/file.svg" alt="File icon" width={16} height={16} />
6646
Learn
6747
</a>
6848
<a
@@ -71,13 +51,7 @@ export default function Home() {
7151
target="_blank"
7252
rel="noopener noreferrer"
7353
>
74-
<Image
75-
aria-hidden
76-
src="/window.svg"
77-
alt="Window icon"
78-
width={16}
79-
height={16}
80-
/>
54+
<Image aria-hidden src="/window.svg" alt="Window icon" width={16} height={16} />
8155
Examples
8256
</a>
8357
<a
@@ -86,13 +60,7 @@ export default function Home() {
8660
target="_blank"
8761
rel="noopener noreferrer"
8862
>
89-
<Image
90-
aria-hidden
91-
src="/globe.svg"
92-
alt="Globe icon"
93-
width={16}
94-
height={16}
95-
/>
63+
<Image aria-hidden src="/globe.svg" alt="Globe icon" width={16} height={16} />
9664
Go to nextjs.org →
9765
</a>
9866
</footer>

auth.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import GitHub from 'next-auth/providers/github';
2+
import type { NextAuthConfig } from 'next-auth';
3+
4+
// https://authjs.dev/guides/edge-compatibility#middleware
5+
export default {
6+
providers: [GitHub],
7+
} satisfies NextAuthConfig;

auth.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { MongoDBAdapter } from '@auth/mongodb-adapter';
2+
import NextAuth from 'next-auth';
3+
import authConfig from './auth.config';
4+
import mongoClientPromise from './lib/mongo-client';
5+
import { ObjectId } from 'mongodb';
6+
7+
export const { handlers, signIn, signOut, auth } = NextAuth({
8+
adapter: MongoDBAdapter(mongoClientPromise),
9+
session: { strategy: 'jwt' },
10+
...authConfig,
11+
callbacks: {
12+
/**
13+
* signIn is called whenever a user signs in (OAuth or credentials).
14+
* Here we have access to the `profile`, which includes GitHub user info.
15+
*/
16+
async signIn({ account, profile, user }) {
17+
// Only do this if the provider is GitHub
18+
if (account?.provider === 'github') {
19+
const client = await mongoClientPromise;
20+
const db = client.db('auth');
21+
22+
await db
23+
.collection('accounts')
24+
.updateOne(
25+
{ userId: new ObjectId(user.id) },
26+
{ $set: { githubId: profile?.node_id, githubLogin: profile?.login } },
27+
);
28+
}
29+
return true;
30+
},
31+
32+
async jwt({ token, profile, account }) {
33+
if (profile && account?.provider === 'github') {
34+
token.githubId = profile.node_id;
35+
}
36+
return token;
37+
},
38+
39+
async session({ session, token }) {
40+
if (token?.githubId) {
41+
session.user.githubId = token.githubId as string;
42+
}
43+
return session;
44+
},
45+
},
46+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client';
2+
import { signIn, signOut, useSession } from 'next-auth/react';
3+
4+
export default function SigninButton() {
5+
const { data: session } = useSession();
6+
console.log('session', session);
7+
8+
return (
9+
<div>
10+
{session ? (
11+
<div>
12+
<p>Signed in as {session.user?.email}</p>
13+
<button onClick={() => signOut()}>Sign Out</button>
14+
</div>
15+
) : (
16+
<button onClick={() => signIn('github')}>Sign in with GitHub</button>
17+
)}
18+
</div>
19+
);
20+
}

lib/mongo-client.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb
2+
import { MongoClient, ServerApiVersion } from 'mongodb';
3+
4+
if (!process.env.MONGODB_URI_AUTH) {
5+
throw new Error('Invalid/Missing environment variable: "MONGODB_URI_AUTH"');
6+
}
7+
8+
const uri = process.env.MONGODB_URI_AUTH;
9+
const options = {
10+
serverApi: {
11+
version: ServerApiVersion.v1,
12+
strict: true,
13+
deprecationErrors: true,
14+
},
15+
};
16+
17+
let mongoClient: MongoClient;
18+
let mongoClientPromise: Promise<MongoClient>;
19+
20+
if (process.env.NODE_ENV === 'development') {
21+
// In development mode, use a global variable so that the value
22+
// is preserved across module reloads caused by HMR (Hot Module Replacement).
23+
const globalWithMongo = global as typeof globalThis & {
24+
_mongoClientPromise: Promise<MongoClient>;
25+
};
26+
27+
if (!globalWithMongo._mongoClientPromise) {
28+
mongoClient = new MongoClient(uri, options);
29+
globalWithMongo._mongoClientPromise = mongoClient.connect();
30+
}
31+
mongoClientPromise = globalWithMongo._mongoClientPromise;
32+
} else {
33+
// In production mode, it's best to not use a global variable.
34+
mongoClient = new MongoClient(uri, options);
35+
mongoClientPromise = mongoClient.connect();
36+
}
37+
38+
// Export a module-scoped MongoClient. By doing this in a
39+
// separate module, the mongoClient can be shared across functions.
40+
export default mongoClientPromise;

middleware.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import NextAuth from 'next-auth';
2+
import authConfig from './auth.config';
3+
4+
export const { auth: middleware } = NextAuth(authConfig);

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,22 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@auth/mongodb-adapter": "^3.7.4",
13+
"mongodb": "^6.13.0",
14+
"next": "15.1.6",
15+
"next-auth": "5.0.0-beta.25",
1216
"react": "^19.0.0",
13-
"react-dom": "^19.0.0",
14-
"next": "15.1.6"
17+
"react-dom": "^19.0.0"
1518
},
1619
"devDependencies": {
17-
"typescript": "^5",
20+
"@eslint/eslintrc": "^3",
1821
"@types/node": "^20",
1922
"@types/react": "^19",
2023
"@types/react-dom": "^19",
21-
"postcss": "^8",
22-
"tailwindcss": "^3.4.1",
2324
"eslint": "^9",
2425
"eslint-config-next": "15.1.6",
25-
"@eslint/eslintrc": "^3"
26+
"postcss": "^8",
27+
"tailwindcss": "^3.4.1",
28+
"typescript": "^5"
2629
}
2730
}

0 commit comments

Comments
 (0)