3. NextAuth
Apa itu NextAuth
Dokumentasi : https://next-auth.js.org/
Kenapa Menggunakan NextAuth?
Implementasi NextAuth
Instalasi NextAuth
package.json
{
"name": "frontend-nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3010",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@heroicons/react": "^2.0.18",
"@react-spring/web": "^9.7.3",
"@tanstack/react-query": "^4.33.0",
"@tanstack/react-query-devtools": "^4.33.0",
"@types/node": "20.4.5",
"@types/react": "18.2.17",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.14",
"axios": "^1.5.0",
"clsx": "^2.0.0",
"eslint": "8.46.0",
"eslint-config-next": "13.4.12",
"formik": "^2.4.3",
"next": "13.4.12",
"next-auth": "^4.23.1",
"postcss": "8.4.27",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-spinners": "^0.13.8",
"sweetalert2": "^11.7.27",
"tailwindcss": "3.3.3",
"typescript": "5.1.6",
"yup": "^1.2.0"
}
}
Membuat API Route
pages/api/auth/[...nextauth].ts
import NextAuth, { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const authOptions: NextAuthOptions = {
providers: [
// ...add more providers here
CredentialsProvider({
type: "credentials",
credentials: {},
authorize(credentials: any, req) {
return {
...credentials,
};
},
}),
],
};
export default NextAuth(authOptions);
Membuat Komponen NextAuthProvider
components/NextAuthProvider.tsx
"use client";
import { SessionProvider } from "next-auth/react";
import React, { ReactNode } from "react";
import { Session } from "next-auth";
interface NextAuthProps {
children: ReactNode;
session: Session | null | undefined;
}
const NextAuthProvider: React.FC<NextAuthProps> = ({ children, session }) => {
return <SessionProvider session={session}>{children}</SessionProvider>;
};
export default NextAuthProvider;
Impelentasi NextAuthProvider pada RootLayout
app/layout.tsx
import "./globals.css";
import React, { ReactNode } from "react";
import type { Metadata } from "next";
import ReactQuery from "../components/ReactQuery";
import NextAuthProvider from "@/components/NextAuthProvider";
import { Session } from "next-auth";
interface NextAuthProps {
children: ReactNode;
session: Session | null | undefined;
}
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({ children, session }: NextAuthProps) {
return (
<html lang="en">
<body className={"px-5 w-screen h-screen overflow-hidden"}>
<NextAuthProvider session={session}>
<ReactQuery>{children}</ReactQuery>
</NextAuthProvider>
</body>
</html>
);
}
Lihat Session pada Komponen Login
app/auth/login/page/.tsx
"use client";
import { useFormik, Form, FormikProvider, getIn } from "formik";
import * as yup from "yup";
import { LoginPayload } from "../interface";
import InputText from "@/components/InputText";
import Label from "@/components/Label";
import Button from "@/components/Button";
import useAuthModule from "../lib";
import Link from "next/link";
import { useSession } from "next-auth/react";
export const registerSchema = yup.object().shape({
email: yup
.string()
.nullable()
.default("")
.email("Gunakan format email")
.required("Wajib isi"),
password: yup
.string()
.nullable()
.default("")
.required("Wajib isi")
.min(8, "Minimal 8 karakater"),
});
const Login = () => {
const { data: session, status } = useSession();
console.log('session', session)
console.log('status', status)
const { useLogin } = useAuthModule();
const { mutate, isLoading } = useLogin();
const formik = useFormik<LoginPayload>({
initialValues: registerSchema.getDefault(),
validationSchema: registerSchema,
enableReinitialize: true,
onSubmit: (payload) => {
mutate(payload);
},
});
const { handleChange, handleSubmit, handleBlur, values, errors } = formik;
return (
<section>
<div className="flex items-center justify-center w-full">
<h1 className="text-3xl text-blue-400">Login</h1>
</div>
<FormikProvider value={formik}>
<Form className="space-y-5" onSubmit={handleSubmit}>
<section>
<Label htmlFor="email" title="Email" />
<InputText
value={values.email}
placeholder="exampel@email.com"
id="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
isError={getIn(errors, "email")}
messageError={getIn(errors, "email")}
/>
</section>
<section>
<Label htmlFor="password" title="Password" />
<InputText
value={values.password}
placeholder="**********"
id="password"
name="password"
type="password"
onChange={handleChange}
onBlur={handleBlur}
isError={getIn(errors, "password")}
messageError={getIn(errors, "password")}
/>
</section>
<section>
<Button
height="lg"
title="Login"
colorSchema="blue"
isLoading={isLoading}
isDisabled={isLoading}
/>
<Link href={"register"}>
<Button title="Halaman Register" colorSchema="green" />
</Link>
</section>
</Form>
</FormikProvider>
</section>
);
};
export default Login;
Perbaharui next.config.ts
next.config.ts
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: {
appDir: true,
},
};
module.exports = nextConfig;
Membuat Konfigurasi global
Menyimpan Access Token dan Refresh Token di NextAuth session
app/auth/lib/index.ts
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useToast } from "@/hook";
import {
LoginPayload,
LoginResponse,
RegisterPayload,
RegisterResponse,
} from "../interface";
import { axiosClient } from "@/lib/axiosClient";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
const useAuthModule = () => {
const { toastError, toastSuccess, toastWarning } = useToast();
const router = useRouter();
...
const login = async (payload: LoginPayload): Promise<LoginResponse> => {
return axiosClient.post("/auth/login", payload).then((res) => res.data);
};
const useLogin = () => {
const { mutate, isLoading } = useMutation(
(payload: LoginPayload) => login(payload),
{
onSuccess: async (response) => {
toastSuccess(response.message);
await signIn("credentials", {
id: response.data.id,
name: response.data.nama,
email: response.data.email,
accessToken: response.data.access_token,
refreshToken: response.data.refresh_token,
redirect: false,
});
router.push("/admin");
},
onError: (error: any) => {
if (error.response.status == 422) {
toastWarning(error.response.data.message);
} else {
toastError();
}
},
}
);
return { mutate, isLoading };
};
return { useRegister, useLogin };
};
export default useAuthModule;
pages/api/auth/[...nextauth].ts
import NextAuth, { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET,
providers: [
CredentialsProvider({
type: "credentials",
credentials: {},
authorize(credentials: any, req) {
return {
...credentials,
};
},
}),
],
callbacks: {
async jwt({ token, user, account, trigger, session }) {
if (trigger === "update") {
return { ...token, ...session.user };
}
return {
...token,
...user,
};
},
async session({ session, user, token }) {
session.user.id = Number(token.id);
session.user.name = token.name;
session.user.email = token.email;
session.user.accessToken = token.accessToken;
session.user.refreshToken = token.refreshToken;
return session;
},
},
pages: {
signIn: "/auth/login",
signOut: "/auth/login",
error: "/auth/error",
},
};
export default NextAuth(authOptions);
Mendefinisikan custom Type session
types/next-auth.d.ts
import { Session } from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: number | undefined | null;
email: string | undefined | null;
name: string | undefined | null;
accessToken: any;
refreshToken: any;
token : any
};
}
}
Merubah komponen halaman admin
app/admin/page.tsx
"use client";
import React from "react";
import { useSession, signOut } from "next-auth/react";
import Button from "@/components/Button";
const Page = () => {
const { data: session, status } = useSession();
return (
<div>
Admin
{JSON.stringify(session)}
{status}
<Button
title="Logout"
colorSchema="red"
onClick={() => {
signOut();
}}
/>
</div>
);
};
export default Page;
Jalankan pada Browser
Implementasi Middleware
Apa itu Midddleware?
Kenapa Menggunakan Midddleware?
Membuat Middleware
middleware.ts
import { withAuth } from "next-auth/middleware";
export default withAuth(
// `withAuth` augments your `Request` with the user's token.
function middleware(req) {
console.log("token", req.nextauth.token);
},
{
callbacks: {
authorized: ({ token }) => {
if (token) return true;
return false;
},
},
pages: {
signIn: "/auth/login",
error: '/api/auth/error',
},
}
);
export const config = { matcher: ["/admin", "/admin/:path*"] };