login endpoint authentication now working
This commit is contained in:
parent
c0fe9dcf0f
commit
274ee8e4de
|
@ -1,13 +1,16 @@
|
||||||
"use server"
|
"use server"
|
||||||
import prisma from 'app/lib/db';
|
import prisma from 'app/lib/db';
|
||||||
import { jwtVerify, JWTPayload, decodeJwt } from 'jose';
|
import { jwtVerify, JWTPayload, decodeJwt, SignJWT } from 'jose';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
|
import { loginSchema, LoginSchema } from 'app/login/schema';
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { TextEncoder } from 'util';
|
||||||
|
|
||||||
export async function getJWTSecretKey() {
|
export async function getJWTSecretKey() {
|
||||||
const secret = process.env.JWT_SECRET
|
const secret = process.env.JWT_SECRET
|
||||||
if (!secret) throw new Error("There is no JWT secret key")
|
if (!secret) throw new Error("There is no JWT secret key")
|
||||||
return new TextEncoder().encode(secret)
|
const enc: Uint8Array = new TextEncoder().encode(secret)
|
||||||
|
return enc
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyJwt(token: string): Promise<JWTPayload | null> {
|
export async function verifyJwt(token: string): Promise<JWTPayload | null> {
|
||||||
|
@ -76,25 +79,20 @@ export async function setUserDataCookie(userData) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserLogin = {
|
|
||||||
email: string,
|
|
||||||
password: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function login(userLogin: UserLogin) {
|
export async function login(userLogin: LoginSchema) {
|
||||||
|
const isSafe = loginSchema.safeParse(userLogin)
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
if (!isSafe.success) throw new Error("parse failed")
|
||||||
const user = await prisma.user.findFirst({ where: { email: userLogin.email } })
|
const user = await prisma.user.findFirst({ where: { email: userLogin.email } })
|
||||||
if (!user) { throw new Error('user does not exist') }
|
if (!user) throw new Error("user does not exist")
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
console.log(`client user: ${JSON.stringify(userLogin)}
|
|
||||||
db user: ${JSON.stringify(user)}`)
|
|
||||||
const passwordIsValid = await bcrypt.compare(userLogin.password, user.password)
|
const passwordIsValid = await bcrypt.compare(userLogin.password, user.password)
|
||||||
if (!passwordIsValid) throw new Error('invalid password')
|
if (!passwordIsValid) throw new Error("password is not valid")
|
||||||
//return the user object without the hashed password
|
return { email: userLogin.email }
|
||||||
return { email: user.email, id: user.id }
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
throw new Error('login failed')
|
||||||
throw new Error('invalid login or password')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NextResponse, NextRequest } from "next/server";
|
import { NextResponse, NextRequest } from "next/server";
|
||||||
|
import prisma from "app/lib/db";
|
||||||
import { SignJWT } from "jose";
|
import { SignJWT } from "jose";
|
||||||
|
|
||||||
import { getJWTSecretKey, login, setUserDataCookie } from "../actions";
|
import { getJWTSecretKey, login, setUserDataCookie } from "../actions";
|
||||||
|
@ -13,9 +14,9 @@ const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
//POST endpoint
|
//POST endpoint
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const { body } = await request.json()
|
const body = await request.json()
|
||||||
const { email, password } = body
|
|
||||||
console.log(`body: ${JSON.stringify(body)}`)
|
console.log(`body: ${JSON.stringify(body)}`)
|
||||||
|
const { email, password } = body
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
const res = {
|
const res = {
|
||||||
|
@ -37,7 +38,7 @@ export async function POST(request: NextRequest) {
|
||||||
.setProtectedHeader({ alg: 'HS256' })
|
.setProtectedHeader({ alg: 'HS256' })
|
||||||
.setIssuedAt()
|
.setIssuedAt()
|
||||||
.setExpirationTime('1h')
|
.setExpirationTime('1h')
|
||||||
.sign(getJWTSecretKey())
|
.sign(await getJWTSecretKey())
|
||||||
|
|
||||||
//make response
|
//make response
|
||||||
const res = { success: true }
|
const res = { success: true }
|
||||||
|
|
|
@ -6,28 +6,44 @@ import { toast } from "@/components/ui/use-toast";
|
||||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { login } from "app/api/auth/actions";
|
import { redirect } from "next/navigation";
|
||||||
|
import { loginSchema } from "./schema";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
const formSchema = z.object({
|
|
||||||
email: z.string().email(),
|
|
||||||
password: z.string().min(6)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function LoginForm() {
|
export default function LoginForm() {
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const router = useRouter()
|
||||||
resolver: zodResolver(formSchema),
|
const form = useForm<z.infer<typeof loginSchema>>({
|
||||||
|
resolver: zodResolver(loginSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
function onErrors(errors) {
|
const onSubmit = form.handleSubmit(async (data) => {
|
||||||
toast({
|
// const res = await login(data)
|
||||||
title: "WHOOPS",
|
// if (res?.error) {
|
||||||
description: JSON.stringify(errors)
|
// toast({ title: "Whoops!", description: res.error })
|
||||||
|
// form.reset()
|
||||||
|
// } else {
|
||||||
|
// toast({ title: "login successful" })
|
||||||
|
// }
|
||||||
|
const res = await fetch('/api/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
})
|
})
|
||||||
}
|
if (res.status === 200) {
|
||||||
|
toast({ title: "login successful!" })
|
||||||
|
router.push('/submission')
|
||||||
|
} else {
|
||||||
|
toast({ title: "login failed!" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form action={login}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="email"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { z } from "zod"
|
||||||
|
export const loginSchema = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
export type LoginSchema = z.infer<typeof loginSchema>
|
|
@ -17,10 +17,9 @@ export default async function(request: NextRequest) {
|
||||||
// const url = `${process.env.NEXT_PUBLIC_BASE_URL}/login?redirect=${request.nextUrl.pathname + request.nextUrl.search}`
|
// const url = `${process.env.NEXT_PUBLIC_BASE_URL}/login?redirect=${request.nextUrl.pathname + request.nextUrl.search}`
|
||||||
const url = request.nextUrl.clone()
|
const url = request.nextUrl.clone()
|
||||||
url.pathname = "/login"
|
url.pathname = "/login"
|
||||||
|
|
||||||
if (protectedRoutes.some(pattern => matchesWildcard(request.nextUrl.pathname, pattern))) {
|
if (protectedRoutes.some(pattern => matchesWildcard(request.nextUrl.pathname, pattern))) {
|
||||||
const token = request.cookies.get('token')
|
const token = request.cookies.get('token')
|
||||||
|
console.log(`token: ${JSON.stringify(token)}`)
|
||||||
//NOTE - may need to add logic to return 401 for api routes
|
//NOTE - may need to add logic to return 401 for api routes
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
@ -30,9 +29,9 @@ export default async function(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
//decode and verify jwt cookie
|
//decode and verify jwt cookie
|
||||||
const jwtIsVerified = await verifyJwt(token.value)
|
const jwtIsVerified = await verifyJwt(token.value)
|
||||||
|
|
||||||
if (!jwtIsVerified) {
|
if (!jwtIsVerified) {
|
||||||
//delete token
|
//delete token
|
||||||
|
console.log('could not verify jwt')
|
||||||
request.cookies.delete('token')
|
request.cookies.delete('token')
|
||||||
return NextResponse.redirect(url)
|
return NextResponse.redirect(url)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue