diff --git a/packages/auth/src/config.ts b/packages/auth/src/config.ts new file mode 100644 index 0000000..c438fb3 --- /dev/null +++ b/packages/auth/src/config.ts @@ -0,0 +1 @@ +export const SESSION_COOKIE = "session"; diff --git a/packages/auth/src/entities/cookie.ts b/packages/auth/src/entities/cookie.ts new file mode 100644 index 0000000..f3dfa4a --- /dev/null +++ b/packages/auth/src/entities/cookie.ts @@ -0,0 +1,15 @@ +type CookieAttributes = { + secure?: boolean; + path?: string; + domain?: string; + sameSite?: "lax" | "strict" | "none"; + httpOnly?: boolean; + maxAge?: number; + expires?: Date; +}; + +export type Cookie = { + name: string; + value: string; + attributes: CookieAttributes; +}; diff --git a/packages/auth/src/entities/errors.ts b/packages/auth/src/entities/errors.ts new file mode 100644 index 0000000..285bc0f --- /dev/null +++ b/packages/auth/src/entities/errors.ts @@ -0,0 +1,23 @@ +export class AuthenticationError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + } +} + +export class UnauthenticatedError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + } +} + +export class UnauthorizedError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + } +} + +export class InputParseError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + } +} diff --git a/packages/auth/src/entities/session.test.ts b/packages/auth/src/entities/session.test.ts new file mode 100644 index 0000000..39c86be --- /dev/null +++ b/packages/auth/src/entities/session.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import { sessionSchema } from "./session"; + +describe("sessionSchema", () => { + it("accepts a valid session", () => { + const result = sessionSchema.parse({ + id: "session_1", + userId: "1", + expiresAt: new Date(), + }); + expect(result.userId).toBe("1"); + }); + + it("rejects non-Date expiresAt", () => { + expect(() => + sessionSchema.parse({ + id: "session_1", + userId: "1", + expiresAt: "2026-05-04", + }), + ).toThrow(); + }); +}); diff --git a/packages/auth/src/entities/session.ts b/packages/auth/src/entities/session.ts new file mode 100644 index 0000000..cedf8a9 --- /dev/null +++ b/packages/auth/src/entities/session.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +export const sessionSchema = z.object({ + id: z.string(), + userId: z.string(), + expiresAt: z.date(), +}); + +export type Session = z.infer; diff --git a/packages/auth/src/entities/user.test.ts b/packages/auth/src/entities/user.test.ts new file mode 100644 index 0000000..b2911c2 --- /dev/null +++ b/packages/auth/src/entities/user.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { userSchema } from "./user"; + +describe("userSchema", () => { + it("accepts a valid user", () => { + const result = userSchema.parse({ + id: "1", + username: "alice", + passwordHash: "hashed_password_1", + }); + expect(result.username).toBe("alice"); + }); + + it("rejects username shorter than 3 chars", () => { + expect(() => + userSchema.parse({ + id: "1", + username: "ab", + passwordHash: "hashed_password_1", + }), + ).toThrow(); + }); + + it("rejects passwordHash shorter than 6 chars", () => { + expect(() => + userSchema.parse({ + id: "1", + username: "alice", + passwordHash: "abc", + }), + ).toThrow(); + }); +}); diff --git a/packages/auth/src/entities/user.ts b/packages/auth/src/entities/user.ts new file mode 100644 index 0000000..3de2240 --- /dev/null +++ b/packages/auth/src/entities/user.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +export const userSchema = z.object({ + id: z.string(), + username: z.string().min(3).max(31), + passwordHash: z.string().min(6).max(255), +}); + +export type User = z.infer;