Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ SENDGRID_SENDER_ID=""

NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="change-me"
NEXTAUTH_ROOT_DOMAIN="http://localhost"
NEXTAUTH_ROOT_DOMAIN="http://localhost"

NEXT_PUBLIC_URL="http://localhost:3000"
JWT_SECRET=""
99 changes: 99 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@sendgrid/mail": "^8.1.0",
"bcrypt": "^5.1.1",
"eclipse-components": "^0.0.141",
"jsonwebtoken": "^9.0.2",
"next": "14.1.0",
"next-auth": "^4.24.5",
"prisma": "^5.9.1",
Expand All @@ -21,6 +22,7 @@
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
Expand All @@ -31,4 +33,4 @@
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
}
11 changes: 11 additions & 0 deletions src/app/Providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

import { SessionProvider } from "next-auth/react";

type ProvidersProps = {
children: React.ReactNode;
};

export default function Providers({ children }: ProvidersProps) {
return <SessionProvider>{children}</SessionProvider>;
}
23 changes: 21 additions & 2 deletions src/app/api/auth/signup/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { prisma } from "@/lib/prisma";
import bcrypt from "bcrypt";
import { NextResponse } from "next/server";
import { sendEmail } from "@/lib/email";
import { jwtGenerateEmailVerificationToken } from "@/lib/jwt";

/**
*
Expand Down Expand Up @@ -64,7 +66,6 @@ export async function POST(request: Request) {
}

// If the user doesn't exist, then we need to create the user and the Auth

const newUser = await prisma.user.create({
data: {
firstName: firstName,
Expand All @@ -80,5 +81,23 @@ export async function POST(request: Request) {

console.log(email, " Account Created");

return NextResponse.json({ user: newUser }, { status: 201 });
// send the verification email
const token = await jwtGenerateEmailVerificationToken(email);
try {
await sendEmail(
email,
"Eclipse Expos -- Email Verification",
`Click the following link to verify your email: ${process.env.NEXT_PUBLIC_URL}/verify?token=${token}\n\nThis link will expire in 10 minutes.`
);
} catch {
return NextResponse.json(
{ user: newUser, isEmailSent: false },
{ status: 201 }
);
}

return NextResponse.json(
{ user: newUser, isEmailSent: true },
{ status: 201 }
);
}
100 changes: 50 additions & 50 deletions src/app/api/auth/validate-credentials/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,63 @@ import { NextRequest, NextResponse } from "next/server";
* @returns NextResponse.json
*/
export async function POST(request: NextRequest) {
const { username, password } = await request.json();
const { username, password } = await request.json();

console.log(username, " Attempted login");
console.log(username, " Attempted login");

const requestHeaders = headers();
const ip = requestHeaders.get("x-forwarded-for") ?? "";
const requestHeaders = headers();
const ip = requestHeaders.get("x-forwarded-for") ?? "";

const user = await prisma.user.findUnique({
where: {
email: username,
},
include: {
auth: true,
},
});

// Return error if user is not found or doesn't have auth
if (!user || !user.auth) {
await prisma.loginAttempt.create({
data: {
email: username,
ip,
result: "USER_NOT_FOUND",
},
});
return NextResponse.json({ error: "User not registered" });
}

const isValid = await bcrypt.compare(password, user.auth.passwordHash);

if (!isValid) {
await prisma.loginAttempt.create({
data: {
email: username,
ip,
result: "INVALID_PASSWORD",
associatedUser: {
connect: user,
},
},
});
return NextResponse.json({ error: "Invalid login" });
}
const user = await prisma.user.findUnique({
where: {
email: username,
},
include: {
auth: true,
},
});

console.log(username, " Logged in");
// Return error if user is not found or doesn't have auth
if (!user || !user.auth) {
await prisma.loginAttempt.create({
data: {
email: username,
ip,
result: "USER_NOT_FOUND",
},
});
return NextResponse.json({ error: "User not registered" });
}

const { auth, ...rest } = user;
const isValid = await bcrypt.compare(password, user.auth.passwordHash);

if (!isValid) {
await prisma.loginAttempt.create({
data: {
email: username,
ip,
result: "SUCCESS",
associatedUser: {
connect: user,
},
data: {
email: username,
ip,
result: "INVALID_PASSWORD",
associatedUser: {
connect: user,
},
},
});
return NextResponse.json({ user: rest });
return NextResponse.json({ error: "Invalid login" });
}

console.log(username, " Logged in");

const { auth, ...rest } = user;

await prisma.loginAttempt.create({
data: {
email: username,
ip,
result: "SUCCESS",
associatedUser: {
connect: user,
},
},
});
return NextResponse.json({ user: rest });
}
Loading