Saltar al contenido principal

Auth

Instalaciones

  • Funciona ya que solo utiliza JS
npm i bcryptjs
npm i --save-dev @types/bcryptjs

bun add bcryptjs
bun add --save-dev @types/bcryptjs
  • No funciona porque utiliza C/C++
npm i bcrypt
npm i --save-dev @types/bcrypt

bun add bcrypt
bun add --save-dev @types/bcrypt

Variables de entorno

Wrangler

name = ""
compatibility_date = "2023-12-01"

[vars]
SUPABASE_URL=""
SUPABASE_KEY=""
JWT_SECRET=""

Bindings

src\bindings\index.ts
export type Env = {
  SUPABASE_URL: string;
  SUPABASE_KEY: string;
  JWT_SECRET: string;
};

Utils. Encriptar y verificar contraseña

src\utils\index.ts
import { compare, hash } from "bcryptjs";

export const hashPassword = async (password: string): Promise<string> => {
  return hash(password, 10);
};

export const comparePassword = async (
  plainPassowrd: string,
  hashedPassword: string
): Promise<boolean> => {
  return compare(plainPassowrd, hashedPassword);
};

Middleware

Validar datos

src\auth\validator.ts
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";

// REGISTER =========================================================
export const registerSchema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
  password: z.string().trim().min(5).max(100),
});

// Middleware. Validaciones
export const zRegisterValidator = zValidator("form", registerSchema);

// LOGIN ============================================================
export const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().trim().min(5).max(100),
});

// Middleware. Validaciones
export const zLoginValidator = zValidator("form", loginSchema);

Validar JWT para proteger rutas

src\middleware\auth.ts
import { MiddlewareHandler } from "hono";
import { jwt } from "hono/jwt";

/**
 * Valida el token de autorización
 * @param c
 * @param next
 * @returns
 */
export const authMiddleware: MiddlewareHandler = async (c, next) => {
  const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET });
  return jwtMiddleware(c, next);
};

Service

  • Interactúa con la base de datos
src\auth\service.ts
import { SupabaseClient } from "@supabase/supabase-js";
import { User } from "./validator";

export const addUser = async (
  supabase: SupabaseClient,
  user: User
): Promise<User> => {
  const { data, error } = await supabase.from("users").insert(user).select();
  if (error) throw error;
  return data as unknown as User;
};

export const findUserByEmail = async (
  supabase: SupabaseClient,
  email: string
): Promise<User> => {
  const { data, error } = await supabase
    .from("users")
    .select()
    .eq("email", email)
    .maybeSingle();

  if (error) throw error;
  return data as unknown as User;
};

Rutas

import { Hono } from "hono";
import { sign } from "hono/jwt";
import { Env } from "../bindings";
import { getSupabase, supabaseMiddleware } from "../middleware/supabase";
import { comparePassword, hashPassword } from "../utils";
import { addUser, findUserByEmail } from "./service";
import {
  LoginUser,
  User,
  zLoginValidator,
  zRegisterValidator,
} from "./validator";

const authApp = new Hono<{ Bindings: Env }>().basePath("/auth");

authApp.use("*", supabaseMiddleware);

authApp.post("/register", zRegisterValidator, async (c) => {
  // Extraer el cuerpo de la petición (ya validado)
  // form-data, x-www-form-urlencoded
  const body = await c.req.parseBody<User>();
  body.password = await hashPassword(body.password);
  const supabase = getSupabase(c);
 
  const user = await addUser(supabase, body);
  return c.json(user);
});



authApp.post("/login", zLoginValidator, async (c) => {
  // Extraer el cuerpo de la petición (ya validado)
  // form-data, x-www-form-urlencoded
  // const login = <Pick<LoginUser, "email" | "password">>();
  const body = await c.req.parseBody<LoginUser>();
  const supabase = getSupabase(c);
  const user = await findUserByEmail(supabase, body.email);

  if (!user) return c.json({ error: "Unauthorized or Not found" }, 401);
  const checkPassword = await comparePassword(body.password, user.password);
  if (!checkPassword)
    return c.json({ error: "Unauthorized or Not found" }, 401);

  // JWT. Asignar datos al payload y firmar el token
  const token = await sign({ id: user.email }, c.env.JWT_SECRET);
  return c.json({ user, token });
});

export default authApp;

Proteger rutas

src\users\index.ts
// ...

const userApp = new Hono<{ Bindings: Env }>();

// Middleware para validar el token de autorización
userApp.use("*", supabaseMiddleware, authMiddleware);

// ...