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);
// ...