Skip to content

Commit f6127c5

Browse files
committed
Update all dependencies.
1 parent 7477bca commit f6127c5

29 files changed

+1622
-1655
lines changed

.env.example

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
POSTGRES_URL=
33
POSTGRES_PRISMA_URL=
44
POSTGRES_URL_NON_POOLING=
5-
POSTGRES_USER=
6-
POSTGRES_HOST=
7-
POSTGRES_PASSWORD=
8-
POSTGRES_DATABASE=
95

106
# Generate one here: https://generate-secret.vercel.app/32 (only required for localhost)
11-
NEXTAUTH_SECRET=
7+
AUTH_SECRET=

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ yarn-error.log*
3434

3535
# vercel
3636
.vercel
37+
.env*.local

README.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77

88
<p align="center">
99
This is a <a href="https://nextjs.org/">Next.js</a> starter kit that uses <a href="https://next-auth.js.org/">Next-Auth</a> for simple email + password login<br/>
10-
<a href="https://www.prisma.io/">Prisma</a> as the ORM, and a <a href="https://vercel.com/postgres">Vercel Postgres</a> database to persist the data.</p>
10+
<a href="https://www.prisma.io/">Prisma</a> as the ORM, and a <a href="https://vercel.com/postgres">Neon Postgres</a> database to persist the data.</p>
1111

1212
<br/>
1313

1414
## Deploy Your Own
1515

1616
You can clone & deploy it to Vercel with one click:
1717

18-
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js%20Prisma%20PostgreSQL%20Auth%20Starter&demo-description=Simple%20Next.js%2013%20starter%20kit%20that%20uses%20Next-Auth%20for%20auth%20and%20Prisma%20PostgreSQL%20as%20a%20database.&demo-url=https%3A%2F%2Fnextjs-postgres-auth.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F7rsVQ1ZBSiWe9JGO6FUeZZ%2F210cba91036ca912b2770e0bd5d6cc5d%2Fthumbnail.png&project-name=Next.js%%20Prisma%20PostgreSQL%20Auth%20Starter&repository-name=nextjs-postgres-auth-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-postgres-auth-starter&from=templates&skippable-integrations=1&env=NEXTAUTH_SECRET&envDescription=Generate%20a%20random%20secret%3A&envLink=https://generate-secret.vercel.app/&stores=%5B%7B"type"%3A"postgres"%7D%5D)
18+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js%20Prisma%20PostgreSQL%20Auth%20Starter&demo-description=Simple%20Next.js%2013%20starter%20kit%20that%20uses%20Next-Auth%20for%20auth%20and%20Prisma%20PostgreSQL%20as%20a%20database.&demo-url=https%3A%2F%2Fnextjs-postgres-auth.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F7rsVQ1ZBSiWe9JGO6FUeZZ%2F210cba91036ca912b2770e0bd5d6cc5d%2Fthumbnail.png&project-name=Next.js%%20Prisma%20PostgreSQL%20Auth%20Starter&repository-name=nextjs-postgres-auth-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-postgres-auth-starter&from=templates&skippable-integrations=1&env=AUTH_SECRET&envDescription=Generate%20a%20random%20secret%3A&envLink=https://generate-secret.vercel.app/&stores=%5B%7B"type"%3A"postgres"%7D%5D)
1919

2020
## Developing Locally
2121

@@ -30,9 +30,7 @@ npx create-next-app nextjs-typescript-starter --example "https://github.com/verc
3030
First, run the development server:
3131

3232
```bash
33-
npm run dev
34-
# or
35-
yarn dev
33+
pnpm dev
3634
```
3735

3836
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

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

+2-35
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,2 @@
1-
import NextAuth, { type NextAuthOptions } from "next-auth";
2-
import CredentialsProvider from "next-auth/providers/credentials";
3-
import prisma from "@/lib/prisma";
4-
import { compare } from "bcrypt";
5-
6-
export const authOptions: NextAuthOptions = {
7-
providers: [
8-
CredentialsProvider({
9-
credentials: {
10-
email: { label: "Email", type: "email" },
11-
password: { label: "Password", type: "password" }
12-
},
13-
async authorize(credentials) {
14-
const { email, password } = credentials ?? {}
15-
if (!email || !password) {
16-
throw new Error("Missing username or password");
17-
}
18-
const user = await prisma.user.findUnique({
19-
where: {
20-
email,
21-
},
22-
});
23-
// if user doesn't exist or password doesn't match
24-
if (!user || !(await compare(password, user.password))) {
25-
throw new Error("Invalid username or password");
26-
}
27-
return user;
28-
},
29-
}),
30-
],
31-
};
32-
33-
const handler = NextAuth(authOptions);
34-
35-
export { handler as GET, handler as POST };
1+
export { GET, POST } from 'app/auth';
2+
export const runtime = 'edge';

app/api/auth/register/route.ts

-24
This file was deleted.

app/auth.config.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { NextAuthConfig } from 'next-auth';
2+
3+
export const authConfig = {
4+
pages: {
5+
signIn: '/login',
6+
},
7+
providers: [
8+
// added later in auth.ts since it requires bcrypt which is only compatible with Node.js
9+
// while this file is also used in non-Node.js environments
10+
],
11+
callbacks: {
12+
authorized({ auth, request: { nextUrl } }) {
13+
const isLoggedIn = !!auth?.user;
14+
const isOnDashboard = nextUrl.pathname.startsWith('/protected');
15+
if (isOnDashboard) {
16+
if (isLoggedIn) return true;
17+
return false; // Redirect unauthenticated users to login page
18+
} else if (isLoggedIn) {
19+
return Response.redirect(new URL('/protected', nextUrl));
20+
}
21+
return true;
22+
},
23+
},
24+
} satisfies NextAuthConfig;

app/auth.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import NextAuth from 'next-auth';
2+
import Credentials from 'next-auth/providers/credentials';
3+
import bcrypt from 'bcrypt';
4+
import prisma from 'app/prisma';
5+
import { authConfig } from 'app/auth.config';
6+
7+
export const {
8+
handlers: { GET, POST },
9+
auth,
10+
signIn,
11+
signOut,
12+
} = NextAuth({
13+
...authConfig,
14+
providers: [
15+
Credentials({
16+
async authorize({ email, password }: any) {
17+
let user = await prisma.user.findUnique({
18+
where: {
19+
email,
20+
},
21+
});
22+
23+
if (!user) return null;
24+
let passwordsMatch = await bcrypt.compare(password, user.password);
25+
if (passwordsMatch) return user as any;
26+
},
27+
}),
28+
],
29+
});

app/form.tsx

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
export function Form({
2+
action,
3+
children,
4+
}: {
5+
action: any;
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<form
10+
action={action}
11+
className="flex flex-col space-y-4 bg-gray-50 px-4 py-8 sm:px-16"
12+
>
13+
<div>
14+
<label
15+
htmlFor="email"
16+
className="block text-xs text-gray-600 uppercase"
17+
>
18+
Email Address
19+
</label>
20+
<input
21+
id="email"
22+
name="email"
23+
type="email"
24+
placeholder="[email protected]"
25+
autoComplete="email"
26+
required
27+
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
28+
/>
29+
</div>
30+
<div>
31+
<label
32+
htmlFor="password"
33+
className="block text-xs text-gray-600 uppercase"
34+
>
35+
Password
36+
</label>
37+
<input
38+
id="password"
39+
name="password"
40+
type="password"
41+
required
42+
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
43+
/>
44+
</div>
45+
{children}
46+
</form>
47+
);
48+
}

styles/globals.css app/globals.css

File renamed without changes.

app/layout.tsx

+8-25
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
1-
// These styles apply to every route in the application
2-
import "@/styles/globals.css";
3-
import { Metadata } from "next";
4-
import { Inter } from "next/font/google";
5-
import { Toaster } from "react-hot-toast";
6-
import AuthStatus from "@/components/auth-status";
7-
import { Suspense } from "react";
1+
import './globals.css';
82

9-
const inter = Inter({
10-
variable: "--font-inter",
11-
subsets: ["latin"],
12-
});
3+
import { GeistSans } from 'geist/font/sans';
134

14-
const title = "Next.js Prisma Postgres Auth Starter";
5+
const title = 'Next.js Prisma Postgres Auth Starter';
156
const description =
16-
"This is a Next.js starter kit that uses Next-Auth for simple email + password login and a Postgres database to persist the data.";
7+
'This is a Next.js starter kit that uses NextAuth.js for simple email + password login and a Postgres database to persist the data.';
178

18-
export const metadata: Metadata = {
9+
export const metadata = {
1910
title,
2011
description,
2112
twitter: {
22-
card: "summary_large_image",
13+
card: 'summary_large_image',
2314
title,
2415
description,
2516
},
26-
metadataBase: new URL("https://nextjs-postgres-auth.vercel.app"),
27-
themeColor: "#FFF",
17+
metadataBase: new URL('https://nextjs-postgres-auth.vercel.app'),
2818
};
2919

3020
export default async function RootLayout({
@@ -34,14 +24,7 @@ export default async function RootLayout({
3424
}) {
3525
return (
3626
<html lang="en">
37-
<body className={inter.variable}>
38-
<Toaster />
39-
<Suspense fallback="Loading...">
40-
{/* @ts-expect-error Async Server Component */}
41-
<AuthStatus />
42-
</Suspense>
43-
{children}
44-
</body>
27+
<body className={GeistSans.variable}>{children}</body>
4528
</html>
4629
);
4730
}

app/login/page.tsx

+27-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,41 @@
1-
import Image from "next/image";
2-
import Form from "@/components/form";
3-
import Link from "next/link";
1+
import Link from 'next/link';
2+
import { Form } from 'app/form';
3+
import { signIn } from 'app/auth';
44

55
export default function Login() {
66
return (
77
<div className="flex h-screen w-screen items-center justify-center bg-gray-50">
88
<div className="z-10 w-full max-w-md overflow-hidden rounded-2xl border border-gray-100 shadow-xl">
99
<div className="flex flex-col items-center justify-center space-y-3 border-b border-gray-200 bg-white px-4 py-6 pt-8 text-center sm:px-16">
10-
<Link href="/">
11-
<Image
12-
src="/logo.png"
13-
priority
14-
alt="Logo"
15-
className="h-10 w-10 rounded-full"
16-
width={20}
17-
height={20}
18-
/>
19-
</Link>
2010
<h3 className="text-xl font-semibold">Sign In</h3>
2111
<p className="text-sm text-gray-500">
2212
Use your email and password to sign in
2313
</p>
2414
</div>
25-
<Form type="login" />
15+
<Form
16+
action={async (formData: FormData) => {
17+
'use server';
18+
await signIn('credentials', {
19+
redirectTo: '/protected',
20+
email: formData.get('email') as string,
21+
password: formData.get('password') as string,
22+
});
23+
}}
24+
>
25+
<button
26+
type="submit"
27+
className="flex h-10 w-full items-center justify-center rounded-md border text-sm transition-all focus:outline-none"
28+
>
29+
Sign In
30+
</button>
31+
<p className="text-center text-sm text-gray-600">
32+
{"Don't have an account? "}
33+
<Link href="/register" className="font-semibold text-gray-800">
34+
Sign up
35+
</Link>
36+
{' for free.'}
37+
</p>
38+
</Form>
2639
</div>
2740
</div>
2841
);

0 commit comments

Comments
 (0)