"use server"; import { AuthError } from "next-auth"; import { signIn, signOut } from "@/auth.config"; import { apiBaseUrl } from "@/lib"; import { UserMeError } from "@/lib/auth-errors"; import { addAuthEvent } from "@/lib/sentry-breadcrumbs"; import type { UtmParams } from "@/lib/utm"; import type { SignInFormData, SignUpFormData } from "@/types"; export async function authenticate( prevState: unknown, formData: SignInFormData, ) { try { addAuthEvent("login", { email: formData.email }); await signIn("credentials", { ...formData, redirect: false, }); return { message: "Success", }; } catch (error) { if (error instanceof AuthError) { addAuthEvent("error", { type: error.type }); switch (error.type) { case "CredentialsSignin": return { message: "Credentials error", errors: { credentials: "Invalid email or password", }, }; case "CallbackRouteError": return { message: error.cause?.err?.message, }; default: return { message: "Unknown error", errors: { unknown: "Unknown error", }, }; } } } } export const createNewUser = async ( formData: SignUpFormData, attribution: UtmParams = {}, ) => { const url = new URL(`${apiBaseUrl}/users`); if (formData.invitationToken) { url.searchParams.append("invitation_token", formData.invitationToken); } for (const [key, value] of Object.entries(attribution)) { url.searchParams.append(key, value); } const bodyData = { data: { type: "users", attributes: { name: formData.name, email: formData.email, password: formData.password, ...(formData.company && { company_name: formData.company }), }, }, }; try { const response = await fetch(url.toString(), { method: "POST", headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, body: JSON.stringify(bodyData), }); const parsedResponse = await response.json(); if (!response.ok) { return { ...parsedResponse, status: response.status }; } return parsedResponse; } catch (_error) { return { errors: [ { source: { pointer: "" }, detail: "Network error or server is unreachable", }, ], }; } }; export const getToken = async (formData: SignInFormData) => { const url = new URL(`${apiBaseUrl}/tokens`); const bodyData = { data: { type: "tokens", attributes: { email: formData.email, password: formData.password, }, }, }; try { const response = await fetch(url.toString(), { method: "POST", headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, body: JSON.stringify(bodyData), }); if (!response.ok) return null; const parsedResponse = await response.json(); const accessToken = parsedResponse.data.attributes.access; const refreshToken = parsedResponse.data.attributes.refresh; return { accessToken, refreshToken, }; } catch (_error) { throw new Error("Error in trying to get token"); } }; export const getUserByMe = async ( accessToken: string, signal?: AbortSignal, ) => { const url = new URL(`${apiBaseUrl}/users/me?include=roles`); try { const response = await fetch(url.toString(), { method: "GET", headers: { Accept: "application/vnd.api+json", Authorization: `Bearer ${accessToken}`, }, signal, }); if (!response.ok) { const errorMessage = response.status === 401 ? "Invalid or expired token" : response.status === 403 ? "Access denied" : response.status === 404 ? "User not found" : "Unable to load user"; throw new UserMeError(errorMessage, response.status); } const parsedResponse = await response.json(); const userRole = parsedResponse.included?.find( (item: any) => item.type === "roles", ); const permissions = { manage_users: userRole.attributes.manage_users || false, manage_account: userRole.attributes.manage_account || false, manage_providers: userRole.attributes.manage_providers || false, manage_scans: userRole.attributes.manage_scans || false, manage_ingestions: userRole.attributes.manage_ingestions || false, manage_integrations: userRole.attributes.manage_integrations || false, manage_billing: userRole.attributes.manage_billing || false, manage_alerts: userRole.attributes.manage_alerts || false, manage_lighthouse_ai_configuration: userRole.attributes.manage_lighthouse_ai_configuration || false, unlimited_visibility: userRole.attributes.unlimited_visibility || false, }; return { name: parsedResponse.data.attributes.name, email: parsedResponse.data.attributes.email, company: parsedResponse.data.attributes.company_name, dateJoined: parsedResponse.data.attributes.date_joined, permissions, }; } catch (error: unknown) { if (error instanceof UserMeError) throw error; throw new UserMeError( error instanceof Error ? error.message : "Network error or server unreachable", ); } }; export async function logOut() { await signOut({ redirectTo: "/sign-in" }); }