Neste tutorial, vou apresentar como criar autenticação na versão mais recente do Next.js (v14), utilizando a biblioteca next-auth.
Pesquisei bastante sobre o assunto, porém, encontrei muito conteúdo estrangeiro e, em alguns casos, os tutoriais eram mais complexos do que realmente precisavam ser.
Utilizar o next-auth para autenticar sua aplicação é uma excelente opção, pois além de oferecer vários provedores (Google, GitHub, Facebook e muitos outros), facilita muito a vida de quem deseja adicionar essas opções de autenticação em seu projeto.
Sem mais delongas, vamos direto ao ponto.
Para começar, vamos utilizar o seguinte comando em nosso terminal para iniciar uma aplicação Next.js:
yarn create next-app
Assim que executamos o comando acima, é apresentado um questionário com algumas opções. Como não iremos utilizar o TypeScript neste exemplo, altere apenas a primeira opção que pergunta se vamos utilizar o TypeScript para “não”.
Após o projeto ser criado, acesse a pasta da aplicação e vamos adicionar algumas bibliotecas executando os seguintes comandos:
yarn add next-auth # Biblioteca do next-auth yarn add react-hook-form # Biblioteca para facilitar o trabalho com formulários dentro da nossa aplicação (opcional)
E para facilitar na estilização do nosso exemplo, vamos utilizar a biblioteca daisyui.com que deixa tudo muito mais bonito e é muito fácil de se implementar.
Na raiz do nosso projeto rode o comando:
yarn add daisyui@latest -D
Para instalar a biblioteca e agora no arquivo tailwind.config.js adicione as duas propriedades:
....
plugins: [require("daisyui")],
daisyui: {
themes: ["light"],
},
....
Pronto agora com o nosso exemplo com uma certa “estilização”, vamos continuar com a autenticação. 😉
Agora, vamos acessar nosso projeto utilizando o VSCode ou outro editor de código de sua preferência e fazer algumas modificações.
A primeira delas é limpar todo o código desnecessário que o Next.js gera assim que iniciamos um novo projeto.
No arquivo page.js
que se encontra dentro da pasta app/
, vamos deixar da seguinte forma:
'use client';
import { signIn } from "next-auth/react"; import { useState } from "react";
import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; export default function Home() {
const { register, handleSubmit, formState: { errors } } = useForm();
const route = useRouter()
const [error, setError] = useState(null)
const login = async (data) => {
const result = await signIn('auth-tidi', { email: data.email, password: data.password, redirect: false })
if (result.error) {
setError("Login Inválido")
return
}
route.push('/dashboard')
}
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<div className="w-3/12">
<div className="flex justify-center mb-9">
<svg width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fillRule="evenodd" clipRule="evenodd" className="fill-current"><path d="M22.672 15.226l-2.432.811.841 2.515c.33 1.019-.209 2.127-1.23 2.456-1.15.325-2.148-.321-2.463-1.226l-.84-2.518-5.013 1.677.84 2.517c.391 1.203-.434 2.542-1.831 2.542-.88 0-1.601-.564-1.86-1.314l-.842-2.516-2.431.809c-1.135.328-2.145-.317-2.463-1.229-.329-1.018.211-2.127 1.231-2.456l2.432-.809-1.621-4.823-2.432.808c-1.355.384-2.558-.59-2.558-1.839 0-.817.509-1.582 1.327-1.846l2.433-.809-.842-2.515c-.33-1.02.211-2.129 1.232-2.458 1.02-.329 2.13.209 2.461 1.229l.842 2.515 5.011-1.677-.839-2.517c-.403-1.238.484-2.553 1.843-2.553.819 0 1.585.509 1.85 1.326l.841 2.517 2.431-.81c1.02-.33 2.131.211 2.461 1.229.332 1.018-.21 2.126-1.23 2.456l-2.433.809 1.622 4.823 2.433-.809c1.242-.401 2.557.484 2.557 1.838 0 .819-.51 1.583-1.328 1.847m-8.992-6.428l-5.01 1.675 1.619 4.828 5.011-1.674-1.62-4.829z"></path></svg>
</div>
<form onSubmit={handleSubmit(login)} className="flex flex-col gap-4">
<input {...register('email', { required: true })} className="input input-bordered" placeholder="Digite o seu e-mail" required={true} />
<input {...register('password', { required: true })} className="input input-bordered" type="password" label="Senha" placeholder="Digite a sua senha" required={true} />
<button className="btn btn-primary">Acessar</button>
</form>
{
error && <div role="alert" className="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{error}</span>
</div>
}
<div className="divider">OU</div>
<button className="btn btn-outline w-full" onClick={() => signIn('github', { callbackUrl: '/dashboard' })}>Acesse com o GitHub</button>
</div>
</main>
)
}
Agora, para utilizar o next-auth
, precisamos criar um arquivo dentro da pasta app
e deixá-lo assim: api/auth/[...nextauth]/route.ts
. Dentro desse arquivo, adicionaremos o código:
import NextAuth from "next-auth"
import GitHubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials"
const nextAuthOptions = {
providers: [
GitHubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET
}),
CredentialsProvider({
id: 'auth-tidi',
name: 'Credentials',
credentials: {
email: { label: "Email", type: "text", placeholder: "Digite o seu e-mail" },
password: { label: "Password", type: "password", placeholder: "Digite a sua senha" }
},
async authorize(credentials, req) {
const res = await fetch("http://localhost:3001/pt/auth", {
method: 'POST',
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" }
})
const result = await res.json()
if (res.ok) {
return result.data
}
return null
}
})
],
callbacks: {
async jwt({ token, user }) {
user && (token.user = user)
return token
},
async session({ session, token }) {
session = token.user as any
return session
}
}
}
const handler = NextAuth(nextAuthOptions)
export { handler as GET, handler as POST, nextAuthOptions }
Nesse código, estamos utilizando dois provedores: um do GitHub e outro de Credentials. O GitHub permitirá que o usuário realize o login em sua aplicação utilizando a conta do Github, enquanto o Credentials nos permite customizar o login para uma aplicação própria.
No Credentials, nas propriedades ID e Name, você pode adicionar as que forem mais convenientes para o seu projeto. Altere também a URL de autenticação no método fetch. Lembre-se de que utilizei o fetch
como exemplo, mas você pode utilizar outros formatos, como a biblioteca axios
, por exemplo.
Após ter configurado o page.ts
, vamos criar outro arquivo dentro de uma nova pasta dashboard
para simular que o usuário está logado, contendo o seguinte código: /app/dashboard/page.js
.
import { nextAuthOptions } from "../api/auth/[...nextauth]/route"
import { getServerSession } from "next-auth";
import BtnLogout from "./btnLogout";
import { redirect } from "next/navigation"
export default async function Dashboard() {
const session = await getServerSession(nextAuthOptions)
if (!session) {
redirect('/')
}
return (
<main className="flex flex-col gap-3 w-screen h-screen items-center justify-center">
<div className="avatar">
<div className="w-24 rounded-full">
<img src={session?.image || session?.avatar} />
</div>
</div>
<strong>{session?.name}</strong>
<BtnLogout />
</main>
)
}
Vamos criar também um componente de botão para o logout contendo o seguinte código: btnLogout.jsx
.
'use client';
import { signOut } from "next-auth/react";
export default function BtnLogout() {
return (
<button onClick={() => signOut()} className="btn mt-5">Logout</button>
)
}
Prontinho! Provavelmente, a autenticação do seu projeto já estará funcionando.
Agora, basta customizar e fazer as devidas melhorias conforme a necessidade do seu projeto.
Deixo aqui o link do GitHub caso tenha interesse em clonar o modelo desse tutorial.
Espero que esse tutorial te ajude e te de um norte para essa questão de autenticação nos seus projetos.
Valeu! 🙏
[…] Veja também como criar uma tela de login utilizando o NextJS + Next-Auth […]