Skip to content

Commit 9948a5d

Browse files
author
Harshit Yadav
committed
feat: login/logout functionality implemnted
1 parent 533d39d commit 9948a5d

File tree

13 files changed

+874
-433
lines changed

13 files changed

+874
-433
lines changed

README.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,90 @@ export const config = {
205205
- this file will contain all types of routes
206206

207207
44. Edit middleware.ts file
208-
// cuurently here : 2:32:14
208+
209+
- add routes condition in `middleware.ts` file
210+
211+
45. Do valdiations in `auth.config.ts` file
212+
213+
46. Edit `auth.ts` file
214+
215+
```
216+
export const {
217+
handlers: { GET, POST },
218+
auth,
219+
signIn,
220+
signOut,
221+
} = NextAuth({
222+
adapter: PrismaAdapter(db), // prisma adapter is supported on non edge
223+
session: { strategy: "jwt" },
224+
...authConfig,
225+
});
226+
227+
```
228+
229+
47. Implement functionality in `login.ts` file
230+
231+
```
232+
"use server"; // necessary in every auth action
233+
234+
import * as z from "zod";
235+
import { LoginSchema } from "@/schema";
236+
import { signIn } from "@/auth";
237+
import { DEFAULT_LOGIN_REDIRECT } from "@/route";
238+
import { AuthError } from "next-auth";
239+
240+
export const Login = async (values: z.infer<typeof LoginSchema>) => {
241+
const validatedFields = LoginSchema.safeParse(values); // valdiating the input values
242+
if (!validatedFields.success) {
243+
return { error: "Invalid fields! " };
244+
}
245+
const { email, password } = validatedFields.data;
246+
try {
247+
await signIn("credentials", {
248+
email,
249+
password,
250+
redirectTo: DEFAULT_LOGIN_REDIRECT,
251+
});
252+
} catch (error) {
253+
if (error instanceof AuthError) {
254+
switch (error.type) {
255+
case "CredentialsSignin":
256+
return { error: "Invalid credentials!" };
257+
default:
258+
return { error: "Something went wrong!" };
259+
}
260+
}
261+
throw error;
262+
}
263+
};
264+
265+
```
266+
267+
48. Go to settings page and add logout button, and its functionality in `settings.tsx` file
268+
269+
```
270+
import { auth, signOut } from '@/auth'
271+
import React from 'react'
272+
273+
const Settings = async () => {
274+
const user = await auth()
275+
return (
276+
<div>
277+
{JSON.stringify(user)}
278+
<form action={async () => {
279+
'use server'
280+
await signOut()
281+
}}>
282+
<button type='submit'>
283+
Singout
284+
</button>
285+
</form>
286+
</div>
287+
)
288+
}
289+
290+
export default Settings
291+
292+
```
293+
294+
#### Login/Logout completed succesfully

actions/auth/login.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
"use server"; // necessary in every auth action
22

3-
export const Login = (values: any) => {
4-
console.log(values);
3+
import * as z from "zod";
4+
import { LoginSchema } from "@/schema";
5+
import { signIn } from "@/auth";
6+
import { DEFAULT_LOGIN_REDIRECT } from "@/route";
7+
import { AuthError } from "next-auth";
8+
9+
export const Login = async (values: z.infer<typeof LoginSchema>) => {
10+
const validatedFields = LoginSchema.safeParse(values); // valdiating the input values
11+
if (!validatedFields.success) {
12+
return { error: "Invalid fields! " };
13+
}
14+
const { email, password } = validatedFields.data;
15+
try {
16+
await signIn("credentials", {
17+
email,
18+
password,
19+
redirectTo: DEFAULT_LOGIN_REDIRECT,
20+
});
21+
} catch (error) {
22+
if (error instanceof AuthError) {
23+
switch (error.type) {
24+
case "CredentialsSignin":
25+
return { error: "Invalid credentials!" };
26+
default:
27+
return { error: "Something went wrong!" };
28+
}
29+
}
30+
throw error;
31+
}
532
};

actions/auth/register.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { RegisterSchema } from "@/schema";
44
import * as z from "zod";
5-
import bcrypt from "bcrypt";
5+
import bcrypt from "bcryptjs";
66
import { db } from "@/lib/database.connection";
77
import { getUserByEmail } from "@/lib/actions/user.action";
88

app/(protected)/settings/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
import { auth } from '@/auth'
1+
import { auth, signOut } from '@/auth'
22
import React from 'react'
33

44
const Settings = async () => {
55
const user = await auth()
66
return (
77
<div>
88
{JSON.stringify(user)}
9+
<form action={async () => {
10+
'use server'
11+
await signOut()
12+
}}>
13+
<button type='submit'>
14+
Singout
15+
</button>
16+
</form>
917
</div>
1018
)
1119
}

app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ export default function RootLayout({
1818
<html lang="en">
1919
<body className={inter.className}>{children}</body>
2020
</html>
21+
2122
)
2223
}

auth.config.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
11
// * This file is used to trigger the middleware on the edge
22
// * That is why we use auth.ts and auth.config.ts as seprate files
33

4-
5-
import GitHub from "next-auth/providers/github";
4+
import bcrypt from "bcryptjs";
5+
import Credentials from "next-auth/providers/credentials";
66

77
import type { NextAuthConfig } from "next-auth";
8+
import { LoginSchema } from "./schema";
9+
import { getUserByEmail } from "./lib/actions/user.action";
810

911
export default {
10-
providers: [GitHub],
12+
providers: [
13+
Credentials({
14+
async authorize(credentials) {
15+
const validatedFields = LoginSchema.safeParse(credentials); // again doing validation
16+
if (validatedFields.success) {
17+
// if validation is successfull
18+
const { email, password } = validatedFields.data;
19+
20+
const user = await getUserByEmail(email); // checking if user is present in database
21+
if (!user || !user.password) return null; // password will be null when user has registered using google or github
22+
23+
const passwordsMatch = await bcrypt.compare(password, user.password); // comparing the hashed password
24+
25+
if (passwordsMatch) {
26+
return user;
27+
}
28+
}
29+
return null;
30+
},
31+
}),
32+
],
1133
} satisfies NextAuthConfig;

auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { PrismaAdapter } from "@auth/prisma-adapter";
66
export const {
77
handlers: { GET, POST },
88
auth,
9+
signIn,
10+
signOut,
911
} = NextAuth({
1012
adapter: PrismaAdapter(db), // prisma adapter is supported on non edge
1113
session: { strategy: "jwt" },

components/auth/login-form.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client'
2-
import React from 'react'
2+
import React, { startTransition, useState } from 'react'
33
import { CardWrapper } from './card-wrapper'
44
import { Input } from "@/components/ui/input";
55
import {
@@ -21,6 +21,9 @@ import { FormError } from '../form-error';
2121
import { Login } from '@/actions/auth/login';
2222
const LoginForm = () => {
2323

24+
const [error, setError] = useState<string | undefined>("");
25+
const [success, setSuccess] = useState<string | undefined>("");
26+
2427
const form = useForm<z.infer<typeof LoginSchema>>({
2528
resolver: zodResolver(LoginSchema),
2629
defaultValues: {
@@ -30,8 +33,23 @@ const LoginForm = () => {
3033
})
3134

3235
const onSubmit = (values: z.infer<typeof LoginSchema>) => {
33-
Login(values)
34-
console.log(values)
36+
setError("");
37+
setSuccess("");
38+
startTransition(() => {
39+
Login(values)
40+
.then((data) => {
41+
if (data?.error) {
42+
form.reset();
43+
setError(data?.error);
44+
}
45+
46+
if (data?.success) {
47+
form.reset();
48+
setSuccess(data.success);
49+
}
50+
})
51+
.catch(() => setError("Something went wrong"));
52+
});
3553
}
3654

3755
return (
@@ -88,8 +106,8 @@ const LoginForm = () => {
88106
</FormItem>
89107
)}
90108
/>
91-
<FormSuccess message="Login completed" />
92-
<FormError message="Invalid email" />
109+
<FormSuccess message={success} />
110+
<FormError message={error} />
93111

94112

95113
<Button

delete.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Document</title>
7+
</head>
8+
<body>
9+
10+
</body>
11+
</html>

middleware.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,31 @@ import {
1111
const { auth } = NextAuth(authConfig);
1212

1313
export default auth((req) => {
14-
// req.auth
1514
const { nextUrl } = req;
16-
const isLoggeedIn = !!req.auth;
15+
const isLoggedIn = !!req.auth;
1716

1817
const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
1918
const isAuthRoute = authRoutes.includes(nextUrl.pathname);
2019
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
2120

21+
// order of if condition matters here
2222
if (isApiAuthRoute) {
2323
return null;
2424
}
2525

26-
// TODO ; start from here
26+
if (isAuthRoute) {
27+
if (isLoggedIn) {
28+
return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
29+
} else {
30+
return null;
31+
}
32+
}
33+
34+
if (!isLoggedIn && !isPublicRoute) {
35+
return Response.redirect(new URL("/auth/login", nextUrl));
36+
}
37+
38+
return null;
2739
});
2840

2941
// Optionally, don't invoke Middleware on some paths

0 commit comments

Comments
 (0)