Add typed context and middleware for oRPC procedures
Use implement(contract).$context<APIContext>() for proper type safety in all procedure handlers. Create authMiddleware and loginRequestMiddleware using os.middleware() and apply with .use() on routes requiring auth. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,10 @@ import type {
|
||||
} from "@simplewebauthn/types";
|
||||
import type { Kysely } from "kysely";
|
||||
import type { DB } from "@reviq/db-schema";
|
||||
import type { APIContext } from "../../context.js";
|
||||
import type { RPInfo } from "../../utils/webauthn.js";
|
||||
import { implement, ORPCError } from "@orpc/server";
|
||||
import { contract } from "@reviq/api-contract";
|
||||
import { ORPCError } from "@orpc/server";
|
||||
import { verifyRegistrationResponse } from "@simplewebauthn/server";
|
||||
import { os } from "../base.js";
|
||||
import {
|
||||
COOKIE_NAMES,
|
||||
COOKIE_OPTIONS,
|
||||
@@ -26,8 +25,6 @@ import { hashPassword, validatePassword } from "../../utils/password.js";
|
||||
import { createSession } from "../../utils/session.js";
|
||||
import { getRPInfo, KNOWN_AAGUIDS } from "../../utils/webauthn.js";
|
||||
|
||||
const os = implement(contract);
|
||||
|
||||
/**
|
||||
* Create user with password authentication
|
||||
* Validates password strength and creates user record
|
||||
@@ -208,14 +205,13 @@ export async function signupWithPasskey(
|
||||
* - Sends verification email (stubbed)
|
||||
*/
|
||||
export const signup = os.auth.signup.handler(async ({ input, context }) => {
|
||||
const ctx = context as APIContext;
|
||||
const { email: rawEmail, password, passkeyInfo } = input;
|
||||
|
||||
// Normalize email to lowercase
|
||||
const email = rawEmail.toLowerCase();
|
||||
|
||||
// Check if email already exists (anti-enumeration: return generic error)
|
||||
const existingUser = await ctx.db
|
||||
const existingUser = await context.db
|
||||
.selectFrom("users")
|
||||
.select(["id"])
|
||||
.where("email", "=", email)
|
||||
@@ -226,17 +222,17 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
|
||||
}
|
||||
|
||||
// Get geo info and user agent for session creation
|
||||
const geo = getGeoInfo(ctx.reqHeaders);
|
||||
const userAgent = getUserAgent(ctx.reqHeaders);
|
||||
const geo = getGeoInfo(context.reqHeaders);
|
||||
const userAgent = getUserAgent(context.reqHeaders);
|
||||
|
||||
let userId: number;
|
||||
|
||||
// Delegate to appropriate signup function
|
||||
if (password) {
|
||||
userId = await signupWithPassword(ctx.db, email, password);
|
||||
userId = await signupWithPassword(context.db, email, password);
|
||||
} else if (passkeyInfo) {
|
||||
const rpInfo = getRPInfo(ctx.origin, ctx.allowedOrigins, ctx.rpName);
|
||||
userId = await signupWithPasskey(ctx.db, email, passkeyInfo, rpInfo);
|
||||
const rpInfo = getRPInfo(context.origin, context.allowedOrigins, context.rpName);
|
||||
userId = await signupWithPasskey(context.db, email, passkeyInfo, rpInfo);
|
||||
} else {
|
||||
// Should never reach here due to schema validation
|
||||
throw new ORPCError("BAD_REQUEST", {
|
||||
@@ -245,7 +241,7 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
|
||||
}
|
||||
|
||||
// Create session (7 days, trusted mode false initially, no device)
|
||||
const session = await createSession(ctx.db, {
|
||||
const session = await createSession(context.db, {
|
||||
userId,
|
||||
deviceId: null,
|
||||
trustedMode: false,
|
||||
@@ -255,7 +251,7 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
|
||||
|
||||
// Set session cookie
|
||||
setCookie(
|
||||
ctx.resHeaders,
|
||||
context.resHeaders,
|
||||
COOKIE_NAMES.SESSION_TOKEN,
|
||||
session.token,
|
||||
COOKIE_OPTIONS.session,
|
||||
@@ -266,7 +262,7 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
|
||||
const expiresAt = generateExpiry(TOKEN_DURATIONS.EMAIL_VERIFICATION);
|
||||
|
||||
// Store verification token (store raw token, not hash - it's already high-entropy)
|
||||
await ctx.db
|
||||
await context.db
|
||||
.insertInto("email_verifications")
|
||||
.values({
|
||||
user_id: userId,
|
||||
|
||||
Reference in New Issue
Block a user