Skip to content

Commit

Permalink
Merge pull request #9 from git-init-priyanshu/nextjs
Browse files Browse the repository at this point in the history
Authentication
  • Loading branch information
git-init-priyanshu authored Aug 7, 2024
2 parents 082e0b8 + db5060c commit 31ce8a0
Show file tree
Hide file tree
Showing 29 changed files with 482 additions and 485 deletions.
46 changes: 26 additions & 20 deletions app/(auth)/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { JWTPayload, SignJWT, importJWK } from 'jose';
import bcrypt from 'bcryptjs';

import prisma from "@/prisma/prismaClient";
import { loginSchema, signinSchema } from './zodSchema';
import { signinSchema, signupSchema } from './zodSchema';

const generateJWT = async (payload: JWTPayload) => {
const secret = process.env.JWT_SECRET || 'secret';
Expand All @@ -23,15 +23,15 @@ const generateJWT = async (payload: JWTPayload) => {
return jwt;
};

export const SigninAction = async (data: z.infer<typeof signinSchema>) => {
export const SignupAction = async (data: z.infer<typeof signupSchema>) => {
try {
// User validation
const isUserExist = await prisma.user.findFirst({
const user = await prisma.user.findFirst({
where: {
email: data.email
}
})
if (isUserExist) return {
if (user && user.password) return {
success: false,
error: "Looks like you already have an account",
};
Expand All @@ -40,16 +40,24 @@ export const SigninAction = async (data: z.infer<typeof signinSchema>) => {
const salt = await bcrypt.genSalt(Number(process.env.SALT) || 10);
const hashedPassword = await bcrypt.hash(data.password, salt);

const authToken = await generateJWT({ email: data.email });

await prisma.user.create({
data: {
const jwtPayload = {
id: user?.id,
email: data.email,
name: user?.name,
picture: user?.picture
}
const authToken = await generateJWT(jwtPayload);

await prisma.user.upsert({
where: { email: data.email },
update: {
password: hashedPassword
},
create: {
name: data.name,
username: data.username,
email: data.email,
password: hashedPassword,
isVerified: true,
verifyToken: authToken,
}
})

Expand All @@ -63,7 +71,7 @@ export const SigninAction = async (data: z.infer<typeof signinSchema>) => {
}
}

export const LoginAction = async (data: z.infer<typeof loginSchema>) => {
export const SigninAction = async (data: z.infer<typeof signinSchema>) => {
try {
// User validation
const user = await prisma.user.findFirst({
Expand All @@ -83,15 +91,13 @@ export const LoginAction = async (data: z.infer<typeof loginSchema>) => {
error: "Invalid credentials",
}

const authToken = await generateJWT({ email: data.email });

await prisma.user.update({
where: { id: user.id },
data: {
isVerified: true,
verifyToken: authToken
}
})
const jwtPayload = {
id: user?.id,
email: data.email,
name: user?.name,
picture: user?.picture
}
const authToken = await generateJWT(jwtPayload);

// Setting the cookie
cookies().set('token', authToken, { httpOnly: true });
Expand Down
82 changes: 0 additions & 82 deletions app/(auth)/login/page.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions app/(auth)/signin/components/GoogleAuthButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export default function GoogleAuthButton() {
<Button
variant="outline"
className="w-full flex gap-2"
onClick={() => signIn("google", { redirect: true })}
onClick={() => signIn("google" )}
>
<Image src={Google} alt="google" width={15} />
Sign in with Google
Continue with Google
</Button>
)
}
64 changes: 64 additions & 0 deletions app/(auth)/signin/components/LogInForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Link from "next/link"
import { useRouter } from "next/navigation"
import { useForm } from 'react-hook-form'
import { z } from 'zod'

import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

import { SigninAction } from "../../actions"
import { signinSchema } from '../../zodSchema'
import { toast } from "sonner"


export default function LogInForm() {
const router = useRouter()

const { register, handleSubmit } = useForm<z.infer<typeof signinSchema>>();

const submitForm = async (data: z.infer<typeof signinSchema>) => {
const parsedData = signinSchema.parse({
email: data.email,
password: data.password
})
const response = await SigninAction(parsedData);
if (response.success) {
toast.success("login completed")
router.push('/')
} else {
toast.error(response.error)
}
};

return (
<form className="grid gap-4 text-left" onSubmit={handleSubmit(submitForm)}>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
type="email"
placeholder="johndoe@email.com"
{...register('email', { required: true })}
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<Link
href="/forgot-password"
className="ml-auto inline-block text-sm underline"
>
Forgot your password?
</Link>
</div>
<Input
type="password"
{...register('password', { required: true })}
/>
</div>
<Button type="submit" className="w-full bg-blue-500">
Sign in
</Button>
</form>
)
}
21 changes: 12 additions & 9 deletions app/(auth)/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
"use client"

import Link from "next/link"

import SignInForm from "./components/SignInForm"
import LogInForm from "./components/LogInForm"
import GoogleAuthButton from "./components/GoogleAuthButton"

export default function Signup() {
export default function Login() {

return (
<>
<div className="grid gap-2 text-center">
<h1 className="text-3xl font-bold text-blue-500">Signin</h1>
<h1 className="text-3xl font-bold text-blue-500">Sign in</h1>
<p className="text-balance text-muted-foreground">
Enter your credentials below to login to your account
Enter your email below to login to your account
</p>
</div>
<div className="flex flex-col gap-2 mt-4 text-center text-sm">
<SignInForm />
<p className="">OR</p>
<LogInForm />
<p className="divider">OR</p>
<GoogleAuthButton />
<p>
Already have an account?{" "}
<Link href="/login" className="underline">
Login
Don&apos;t have an account?{" "}
<Link href="/signup" className="underline">
Sign up
</Link>
</p>
</div>
Expand Down
20 changes: 20 additions & 0 deletions app/(auth)/signup/components/GoogleAuthButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client"

import Image from "next/image";
import { signIn } from "next-auth/react";

import Google from '@/public/google_icon.svg'
import { Button } from "@/components/ui/button";

export default function GoogleAuthButton() {
return (
<Button
variant="outline"
className="w-full flex gap-2"
onClick={() => signIn("google", { redirect: true })}
>
<Image src={Google} alt="google" width={15} />
Continue with Google
</Button>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { z } from 'zod'

import { signinSchema } from '../../zodSchema'
import { SigninAction } from '../../actions'
import { signupSchema } from '../../zodSchema'
import { SignupAction } from '../../actions'

export default function CredentialsForm() {
const router = useRouter();

const { register, handleSubmit } = useForm<z.infer<typeof signinSchema>>();
const { register, handleSubmit } = useForm<z.infer<typeof signupSchema>>();

const submitForm = async (data: z.infer<typeof signinSchema>) => {
const parsedData = signinSchema.parse({
const submitForm = async (data: z.infer<typeof signupSchema>) => {
const parsedData = signupSchema.parse({
name: data.name,
username: data.username,
email: data.email,
password: data.password
})

const response = await SigninAction(parsedData);
const response = await SignupAction(parsedData);
if (response.success) {
toast.success("Signin completed")
router.push('/')
Expand Down Expand Up @@ -68,7 +68,7 @@ export default function CredentialsForm() {
/>
</div>
<Button type="submit" className="w-full bg-blue-500">
Signin
Sign up
</Button>
</form>
)
Expand Down
28 changes: 28 additions & 0 deletions app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Link from "next/link"

import SignInForm from "./components/SignInForm"
import GoogleAuthButton from "./components/GoogleAuthButton"

export default function Signup() {
return (
<>
<div className="grid gap-2 text-center">
<h1 className="text-3xl font-bold text-blue-500">Sign up</h1>
<p className="text-balance text-muted-foreground">
Enter your credentials below to login to your account
</p>
</div>
<div className="flex flex-col gap-2 mt-4 text-center text-sm">
<SignInForm />
<p className="divider">OR</p>
<GoogleAuthButton />
<p>
Already have an account?{" "}
<Link href="/api/auth/signin" className="underline">
Sign in
</Link>
</p>
</div>
</>
)
}
4 changes: 2 additions & 2 deletions app/(auth)/zodSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const passwordValidation = new RegExp(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
);

export const signinSchema = z.object({
export const signupSchema = z.object({
name: z.string().min(1).max(50),
username: z.string().min(1).max(20),
email: z.string().email(),
password: z.string().min(1).regex(passwordValidation)
})

export const loginSchema = z.object({
export const signinSchema = z.object({
email: z.string().email(),
password: z.string()
})
Loading

0 comments on commit 31ce8a0

Please sign in to comment.