# Introdução

Por
Pedro Amorim de Gregori
Em 
Publicado 2024-04-25

Autenticação por terceiros é um método que utiliza APIs disponibilizadas por empresas. Para utilizá-la, é necessário a criação de um client de OAuth no site da outra empresa seja ela github, google, etc. Essa API é ligada com a aplicação por um CallBack e ela pode retornar dados variados dependendo do client.

# Instalação e configuração para o OAuth

Para utilizar autenticação por terceiros, será necessário instalar a biblioteca artic e configurar o Client ID e o Client Secret do provider no .env. A lista de todos os providers pode ser vista na esquerda do Artic.

npm install artic

É preciso configurar uma aplicação no site do provedor, o que varia de provedor para provedor. Com o CLIENT_ID e o CLIENT_SECRET, deve-se colocá-los em um arquivo .env.

GITHUB_CLIENT_ID="Aqui entra o client id"
GITHUB_CLIENT_SECRET="Aqui entra o client secret"

O schema precisa ser mudado para suportar operações com o OAuth.

schema.prisma
model User {
    //...
    username String
    github_id Int @unique // Aqui varia dependendo do provedor
    //...
}

O lucia também precisa de algumas modificações.

auth/lucia.ts
import { GitHub } from "arctic";

const adapter = new PrismaAdapter(client.session, client.user);

export const github = new GitHub(
	process.env.GITHUB_CLIENT_ID!,
	process.env.GITHUB_CLIENT_SECRET!
);

//...
export const lucia = new Lucia(adapter, {
	sessionCookie: {
		attributes: {
			secure: process.env.NODE_ENV === "production",
		},
	},
	getUserAttributes: (attributes) => { // Adiciona o atributo githubId
		return {
			username: attributes.username,
			githubId: attributes.github_id,
		};
	},
});

declare module "lucia" {
	interface Register {
		Lucia: typeof lucia;
		DatabaseUserAttributes: DatabaseUserAttributes;
	}
}

interface DatabaseUserAttributes {
	// Atributos do usuário
	username: string;
	github_id: number;
}

# Entrar com o provider

O OAuth é praticamente configurar rotas para interagir com a Api do terceiro. Para isso, será necessário criar uma rota para iniciar o processo de Login com o provider.

Quando utilizado irá aparecer outra janela para realizar login com o github que enviará os dados obtidos pelo callback definido na criação do OAuth client.

api/signin/github/route.ts
import { generateState } from "arctic";
import { github } from "@/../auth/lucia";
import { cookies } from "next/headers";

export async function GET(): Promise<Response> {
	const state = generateState();
	const url = await github.createAuthorizationURL(state);

	cookies().set("github_oauth_state", state, {
		path: "/",
		secure: process.env.NODE_ENV === "production",
		httpOnly: true,
		maxAge: 60 * 10,
		sameSite: "lax",
	});
	return Response.redirect(url);
}

Depois crie uma rota para receber a resposta do Github.

api/signin/github/callback/route.ts
import { github, lucia } from "@/../auth/lucia";
import { cookies } from "next/headers";
import { OAuth2RequestError } from "arctic";
import prisma from "@/../prisma/index";

export async function GET(request: Request): Promise<Response> {
	const url = new URL(request.url);
	const code = url.searchParams.get("code");
	const state = url.searchParams.get("state");
	const storedState = cookies().get("github_oauth_state")?.value ?? null;
	if (!code || !state || !storedState || state !== storedState) {
		return new Response(null, {
			status: 400,
		});
	}

	try {
		const tokens = await github.validateAuthorizationCode(code);
		const githubUserResponse = await fetch("https://api.github.com/user", {
			headers: {
				Authorization: `Bearer ${tokens.accessToken}`,
			},
		});
		const githubUser: GitHubUser = await githubUserResponse.json();

		const existingUser = await prisma.user.findUnique({
			where: { github_id: githubUser.id },
		});
		// Se o usuário já existir, somente a sessão será criada.
		if (existingUser) {
			const session = await lucia.createSession(existingUser.id, {});
			const sessionCookie = lucia.createSessionCookie(session.id);
			cookies().set(
				sessionCookie.name,
				sessionCookie.value,
				sessionCookie.attributes
			);
			return new Response(null, {
				status: 302,
				headers: {
					Location: "/",
				},
			});
		}
		// Caso ainda não exista um usuário é criado um
		const user = await prisma.user.create({
			data: { github_id: githubUser.id, username: githubUser.login },
		});

		const session = await lucia.createSession(user.id, {});
		const sessionCookie = lucia.createSessionCookie(session.id);
		cookies().set(
			sessionCookie.name,
			sessionCookie.value,
			sessionCookie.attributes
		);
		return new Response(null, {
			status: 302,
			headers: {
				Location: "/",
			},
		});
	} catch (e) {
		if (e instanceof OAuth2RequestError) {
			return new Response(null, {
				status: 400,
			});
		}
		return new Response(null, {
			status: 500,
		});
	}
}

interface GitHubUser {
	id: number;
	login: string;
}

Agora o usuário já estará logado.

# Validar sessão/ Verificar usuário e Desconectar/LogOut

A validação do usuário e logout são iguais aos utilizados com credentials. Ver no tópico de credentials.