idk h3 at all
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
setCSRFToken,
|
||||
csrfProtection,
|
||||
getClientIP,
|
||||
getUserAgent,
|
||||
getAuditContext,
|
||||
rateLimitLogin,
|
||||
rateLimitPasswordReset,
|
||||
@@ -37,6 +38,22 @@ import {
|
||||
rateLimitEmailVerification
|
||||
} from "~/server/security";
|
||||
import { logAuditEvent } from "~/server/audit";
|
||||
import type { H3Event } from "vinxi/http";
|
||||
import type { Context } from "../utils";
|
||||
|
||||
/**
|
||||
* Safely extract H3Event from Context
|
||||
* In production: ctx.event is APIEvent, H3Event is at ctx.event.nativeEvent
|
||||
* In development: ctx.event might be H3Event directly
|
||||
*/
|
||||
function getH3Event(ctx: Context): H3Event {
|
||||
// Check if nativeEvent exists (production)
|
||||
if (ctx.event && 'nativeEvent' in ctx.event && ctx.event.nativeEvent) {
|
||||
return ctx.event.nativeEvent as H3Event;
|
||||
}
|
||||
// Otherwise, assume ctx.event is H3Event (development)
|
||||
return ctx.event as unknown as H3Event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JWT with session tracking
|
||||
@@ -115,7 +132,7 @@ function setAuthCookies(
|
||||
const cookieOptions: any = {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
...options
|
||||
};
|
||||
@@ -292,9 +309,9 @@ export const authRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
// Create session with client info
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
const userAgent =
|
||||
ctx.event.nativeEvent.request.headers.get("user-agent") || "unknown";
|
||||
getUserAgent(getH3Event(ctx));
|
||||
const sessionId = await createSession(
|
||||
userId,
|
||||
"14d",
|
||||
@@ -304,16 +321,16 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
const token = await createJWT(userId, sessionId);
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", token, {
|
||||
setCookie(getH3Event(ctx), "userIDToken", token, {
|
||||
maxAge: 60 * 60 * 24 * 14, // 14 days
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax"
|
||||
});
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(ctx.event.nativeEvent);
|
||||
setCSRFToken(getH3Event(ctx));
|
||||
|
||||
// Log successful OAuth login
|
||||
await logAuditEvent({
|
||||
@@ -331,7 +348,7 @@ export const authRouter = createTRPCRouter({
|
||||
};
|
||||
} catch (error) {
|
||||
// Log failed OAuth login
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.login.failed",
|
||||
eventData: {
|
||||
@@ -475,9 +492,9 @@ export const authRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
// Create session with client info
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
const userAgent =
|
||||
ctx.event.nativeEvent.request.headers.get("user-agent") || "unknown";
|
||||
getUserAgent(getH3Event(ctx));
|
||||
const sessionId = await createSession(
|
||||
userId,
|
||||
"14d",
|
||||
@@ -487,16 +504,16 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
const token = await createJWT(userId, sessionId);
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", token, {
|
||||
setCookie(getH3Event(ctx), "userIDToken", token, {
|
||||
maxAge: 60 * 60 * 24 * 14, // 14 days
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax"
|
||||
});
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(ctx.event.nativeEvent);
|
||||
setCSRFToken(getH3Event(ctx));
|
||||
|
||||
// Log successful OAuth login
|
||||
await logAuditEvent({
|
||||
@@ -514,7 +531,7 @@ export const authRouter = createTRPCRouter({
|
||||
};
|
||||
} catch (error) {
|
||||
// Log failed OAuth login
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.login.failed",
|
||||
eventData: {
|
||||
@@ -595,9 +612,9 @@ export const authRouter = createTRPCRouter({
|
||||
const userId = (res.rows[0] as unknown as User).id;
|
||||
|
||||
// Create session with client info
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
const userAgent =
|
||||
ctx.event.nativeEvent.request.headers.get("user-agent") || "unknown";
|
||||
getUserAgent(getH3Event(ctx));
|
||||
const expiresIn = rememberMe ? "14d" : "12h";
|
||||
const sessionId = await createSession(
|
||||
userId,
|
||||
@@ -611,7 +628,7 @@ export const authRouter = createTRPCRouter({
|
||||
const cookieOptions: any = {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax"
|
||||
};
|
||||
|
||||
@@ -620,14 +637,14 @@ export const authRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
setCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"userIDToken",
|
||||
userToken,
|
||||
cookieOptions
|
||||
);
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(ctx.event.nativeEvent);
|
||||
setCSRFToken(getH3Event(ctx));
|
||||
|
||||
// Log successful email link login
|
||||
await logAuditEvent({
|
||||
@@ -645,7 +662,7 @@ export const authRouter = createTRPCRouter({
|
||||
};
|
||||
} catch (error) {
|
||||
// Log failed email link login
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.login.failed",
|
||||
eventData: {
|
||||
@@ -704,10 +721,10 @@ export const authRouter = createTRPCRouter({
|
||||
await conn.execute({ sql: query, args: params });
|
||||
|
||||
// Log successful email verification
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
userId,
|
||||
eventType: "auth.email_verified",
|
||||
eventType: "auth.email.verify.complete",
|
||||
eventData: { email },
|
||||
ipAddress,
|
||||
userAgent,
|
||||
@@ -720,9 +737,9 @@ export const authRouter = createTRPCRouter({
|
||||
};
|
||||
} catch (error) {
|
||||
// Log failed email verification
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.email_verified",
|
||||
eventType: "auth.email.verify.complete",
|
||||
eventData: {
|
||||
email,
|
||||
reason: error instanceof TRPCError ? error.message : "unknown"
|
||||
@@ -749,8 +766,8 @@ export const authRouter = createTRPCRouter({
|
||||
const { email, password, passwordConfirmation } = input;
|
||||
|
||||
// Apply rate limiting
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
rateLimitRegistration(clientIP, ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
rateLimitRegistration(clientIP, getH3Event(ctx));
|
||||
|
||||
// Schema already validates password match, but double check
|
||||
if (password !== passwordConfirmation) {
|
||||
@@ -771,9 +788,9 @@ export const authRouter = createTRPCRouter({
|
||||
});
|
||||
|
||||
// Create session with client info
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
const userAgent =
|
||||
ctx.event.nativeEvent.request.headers.get("user-agent") || "unknown";
|
||||
getUserAgent(getH3Event(ctx));
|
||||
const sessionId = await createSession(
|
||||
userId,
|
||||
"14d",
|
||||
@@ -783,21 +800,21 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
const token = await createJWT(userId, sessionId);
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", token, {
|
||||
setCookie(getH3Event(ctx), "userIDToken", token, {
|
||||
maxAge: 60 * 60 * 24 * 14, // 14 days
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax"
|
||||
});
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(ctx.event.nativeEvent);
|
||||
setCSRFToken(getH3Event(ctx));
|
||||
|
||||
// Log successful registration
|
||||
await logAuditEvent({
|
||||
userId,
|
||||
eventType: "auth.registration.success",
|
||||
eventType: "auth.register.success",
|
||||
eventData: { email, method: "email" },
|
||||
ipAddress: clientIP,
|
||||
userAgent,
|
||||
@@ -807,9 +824,9 @@ export const authRouter = createTRPCRouter({
|
||||
return { success: true, message: "success" };
|
||||
} catch (e) {
|
||||
// Log failed registration
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.registration.failed",
|
||||
eventType: "auth.register.failed",
|
||||
eventData: {
|
||||
email,
|
||||
method: "email",
|
||||
@@ -831,107 +848,131 @@ export const authRouter = createTRPCRouter({
|
||||
emailPasswordLogin: publicProcedure
|
||||
.input(loginUserSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { email, password, rememberMe } = input;
|
||||
try {
|
||||
const { email, password, rememberMe } = input;
|
||||
|
||||
// Apply rate limiting
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
rateLimitLogin(email, clientIP, ctx.event.nativeEvent);
|
||||
// Apply rate limiting
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
rateLimitLogin(email, clientIP, getH3Event(ctx));
|
||||
|
||||
const conn = ConnectionFactory();
|
||||
const res = await conn.execute({
|
||||
sql: "SELECT * FROM User WHERE email = ?",
|
||||
args: [email]
|
||||
});
|
||||
const conn = ConnectionFactory();
|
||||
const res = await conn.execute({
|
||||
sql: "SELECT * FROM User WHERE email = ?",
|
||||
args: [email]
|
||||
});
|
||||
|
||||
// Always run password check to prevent timing attacks
|
||||
const user =
|
||||
res.rows.length > 0 ? (res.rows[0] as unknown as User) : null;
|
||||
const passwordHash = user?.password_hash || null;
|
||||
const passwordMatch = await checkPasswordSafe(password, passwordHash);
|
||||
// Always run password check to prevent timing attacks
|
||||
const user =
|
||||
res.rows.length > 0 ? (res.rows[0] as unknown as User) : null;
|
||||
const passwordHash = user?.password_hash || null;
|
||||
const passwordMatch = await checkPasswordSafe(password, passwordHash);
|
||||
|
||||
// Check all conditions after password verification
|
||||
if (!user || !passwordHash || !passwordMatch) {
|
||||
// Log failed login attempt (wrap in try-catch to ensure it never blocks auth flow)
|
||||
// Check all conditions after password verification
|
||||
if (!user || !passwordHash || !passwordMatch) {
|
||||
// Debug logging (remove after fixing)
|
||||
console.log("Login failed for:", email);
|
||||
console.log("User found:", !!user);
|
||||
console.log("Password hash exists:", !!passwordHash);
|
||||
console.log("Password match:", passwordMatch);
|
||||
|
||||
// Log failed login attempt (wrap in try-catch to ensure it never blocks auth flow)
|
||||
try {
|
||||
const { ipAddress, userAgent } = getAuditContext(
|
||||
getH3Event(ctx)
|
||||
);
|
||||
await logAuditEvent({
|
||||
eventType: "auth.login.failed",
|
||||
eventData: {
|
||||
email,
|
||||
method: "password",
|
||||
reason: "invalid_credentials"
|
||||
},
|
||||
ipAddress,
|
||||
userAgent,
|
||||
success: false
|
||||
});
|
||||
} catch (auditError) {
|
||||
console.error("Audit logging failed:", auditError);
|
||||
}
|
||||
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "no-match"
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!user.provider ||
|
||||
!["email", "google", "github", "apple"].includes(user.provider)
|
||||
) {
|
||||
await conn.execute({
|
||||
sql: "UPDATE User SET provider = ? WHERE id = ?",
|
||||
args: ["email", user.id]
|
||||
});
|
||||
}
|
||||
|
||||
const expiresIn = rememberMe ? "14d" : "12h";
|
||||
|
||||
// Create session with client info (reuse clientIP from rate limiting)
|
||||
const userAgent =
|
||||
getUserAgent(getH3Event(ctx));
|
||||
const sessionId = await createSession(
|
||||
user.id,
|
||||
expiresIn,
|
||||
clientIP,
|
||||
userAgent
|
||||
);
|
||||
|
||||
const token = await createJWT(user.id, sessionId, expiresIn);
|
||||
|
||||
const cookieOptions: any = {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: env.NODE_ENV === "production",
|
||||
sameSite: "lax"
|
||||
};
|
||||
|
||||
if (rememberMe) {
|
||||
cookieOptions.maxAge = 60 * 60 * 24 * 14; // 14 days
|
||||
}
|
||||
|
||||
setCookie(getH3Event(ctx), "userIDToken", token, cookieOptions);
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(getH3Event(ctx));
|
||||
|
||||
// Log successful login (wrap in try-catch to ensure it never blocks auth flow)
|
||||
try {
|
||||
const { ipAddress, userAgent } = getAuditContext(
|
||||
ctx.event.nativeEvent
|
||||
);
|
||||
await logAuditEvent({
|
||||
eventType: "auth.login.failed",
|
||||
eventData: {
|
||||
email,
|
||||
method: "password",
|
||||
reason: "invalid_credentials"
|
||||
},
|
||||
ipAddress,
|
||||
userId: user.id,
|
||||
eventType: "auth.login.success",
|
||||
eventData: { method: "password", rememberMe: rememberMe || false },
|
||||
ipAddress: clientIP,
|
||||
userAgent,
|
||||
success: false
|
||||
success: true
|
||||
});
|
||||
} catch (auditError) {
|
||||
console.error("Audit logging failed:", auditError);
|
||||
}
|
||||
|
||||
return { success: true, message: "success" };
|
||||
} catch (error) {
|
||||
// Log the actual error for debugging
|
||||
console.error("emailPasswordLogin error:", error);
|
||||
console.error("Error stack:", error instanceof Error ? error.stack : "no stack");
|
||||
|
||||
// Re-throw TRPCErrors as-is
|
||||
if (error instanceof TRPCError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Wrap other errors
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "no-match"
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "An error occurred during login",
|
||||
cause: error
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!user.provider ||
|
||||
!["email", "google", "github", "apple"].includes(user.provider)
|
||||
) {
|
||||
await conn.execute({
|
||||
sql: "UPDATE User SET provider = ? WHERE id = ?",
|
||||
args: ["email", user.id]
|
||||
});
|
||||
}
|
||||
|
||||
const expiresIn = rememberMe ? "14d" : "12h";
|
||||
|
||||
// Create session with client info (reuse clientIP from rate limiting)
|
||||
const userAgent =
|
||||
ctx.event.nativeEvent.request.headers.get("user-agent") || "unknown";
|
||||
const sessionId = await createSession(
|
||||
user.id,
|
||||
expiresIn,
|
||||
clientIP,
|
||||
userAgent
|
||||
);
|
||||
|
||||
const token = await createJWT(user.id, sessionId, expiresIn);
|
||||
|
||||
const cookieOptions: any = {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
sameSite: "lax"
|
||||
};
|
||||
|
||||
if (rememberMe) {
|
||||
cookieOptions.maxAge = 60 * 60 * 24 * 14; // 14 days
|
||||
}
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", token, cookieOptions);
|
||||
|
||||
// Set CSRF token for authenticated session
|
||||
setCSRFToken(ctx.event.nativeEvent);
|
||||
|
||||
// Log successful login (wrap in try-catch to ensure it never blocks auth flow)
|
||||
try {
|
||||
await logAuditEvent({
|
||||
userId: user.id,
|
||||
eventType: "auth.login.success",
|
||||
eventData: { method: "password", rememberMe: rememberMe || false },
|
||||
ipAddress: clientIP,
|
||||
userAgent,
|
||||
success: true
|
||||
});
|
||||
} catch (auditError) {
|
||||
console.error("Audit logging failed:", auditError);
|
||||
}
|
||||
|
||||
return { success: true, message: "success" };
|
||||
}),
|
||||
|
||||
requestEmailLinkLogin: publicProcedure
|
||||
@@ -946,7 +987,7 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
try {
|
||||
const requested = getCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"emailLoginLinkRequested"
|
||||
);
|
||||
if (requested) {
|
||||
@@ -1025,7 +1066,7 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
const exp = new Date(Date.now() + 2 * 60 * 1000);
|
||||
setCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"emailLoginLinkRequested",
|
||||
exp.toUTCString(),
|
||||
{
|
||||
@@ -1066,12 +1107,12 @@ export const authRouter = createTRPCRouter({
|
||||
const { email } = input;
|
||||
|
||||
// Apply rate limiting
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
rateLimitPasswordReset(clientIP, ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
rateLimitPasswordReset(clientIP, getH3Event(ctx));
|
||||
|
||||
try {
|
||||
const requested = getCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"passwordResetRequested"
|
||||
);
|
||||
if (requested) {
|
||||
@@ -1145,7 +1186,7 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
const exp = new Date(Date.now() + 5 * 60 * 1000);
|
||||
setCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"passwordResetRequested",
|
||||
exp.toUTCString(),
|
||||
{
|
||||
@@ -1155,10 +1196,10 @@ export const authRouter = createTRPCRouter({
|
||||
);
|
||||
|
||||
// Log password reset request
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
userId: user.id,
|
||||
eventType: "auth.password_reset.requested",
|
||||
eventType: "auth.password.reset.request",
|
||||
eventData: { email },
|
||||
ipAddress,
|
||||
userAgent,
|
||||
@@ -1172,10 +1213,10 @@ export const authRouter = createTRPCRouter({
|
||||
!(error instanceof TRPCError && error.code === "TOO_MANY_REQUESTS")
|
||||
) {
|
||||
const { ipAddress, userAgent } = getAuditContext(
|
||||
ctx.event.nativeEvent
|
||||
getH3Event(ctx)
|
||||
);
|
||||
await logAuditEvent({
|
||||
eventType: "auth.password_reset.requested",
|
||||
eventType: "auth.password.reset.request",
|
||||
eventData: {
|
||||
email: input.email,
|
||||
reason: error instanceof TRPCError ? error.message : "unknown"
|
||||
@@ -1266,20 +1307,20 @@ export const authRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "emailToken", "", {
|
||||
setCookie(getH3Event(ctx), "emailToken", "", {
|
||||
maxAge: 0,
|
||||
path: "/"
|
||||
});
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", "", {
|
||||
setCookie(getH3Event(ctx), "userIDToken", "", {
|
||||
maxAge: 0,
|
||||
path: "/"
|
||||
});
|
||||
|
||||
// Log successful password reset
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
userId: payload.id,
|
||||
eventType: "auth.password_reset.completed",
|
||||
eventType: "auth.password.reset.complete",
|
||||
eventData: {},
|
||||
ipAddress,
|
||||
userAgent,
|
||||
@@ -1289,9 +1330,9 @@ export const authRouter = createTRPCRouter({
|
||||
return { success: true, message: "success" };
|
||||
} catch (error) {
|
||||
// Log failed password reset
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
eventType: "auth.password_reset.completed",
|
||||
eventType: "auth.password.reset.complete",
|
||||
eventData: {
|
||||
reason: error instanceof TRPCError ? error.message : "unknown"
|
||||
},
|
||||
@@ -1317,12 +1358,12 @@ export const authRouter = createTRPCRouter({
|
||||
const { email } = input;
|
||||
|
||||
// Apply rate limiting
|
||||
const clientIP = getClientIP(ctx.event.nativeEvent);
|
||||
rateLimitEmailVerification(clientIP, ctx.event.nativeEvent);
|
||||
const clientIP = getClientIP(getH3Event(ctx));
|
||||
rateLimitEmailVerification(clientIP, getH3Event(ctx));
|
||||
|
||||
try {
|
||||
const requested = getCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"emailVerificationRequested"
|
||||
);
|
||||
if (requested) {
|
||||
@@ -1399,7 +1440,7 @@ export const authRouter = createTRPCRouter({
|
||||
await sendEmail(email, "freno.me email verification", htmlContent);
|
||||
|
||||
setCookie(
|
||||
ctx.event.nativeEvent,
|
||||
getH3Event(ctx),
|
||||
"emailVerificationRequested",
|
||||
Date.now().toString(),
|
||||
{
|
||||
@@ -1409,10 +1450,10 @@ export const authRouter = createTRPCRouter({
|
||||
);
|
||||
|
||||
// Log email verification request
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
userId: user.id,
|
||||
eventType: "auth.email_verification.requested",
|
||||
eventType: "auth.email.verify.request",
|
||||
eventData: { email },
|
||||
ipAddress,
|
||||
userAgent,
|
||||
@@ -1426,10 +1467,10 @@ export const authRouter = createTRPCRouter({
|
||||
!(error instanceof TRPCError && error.code === "TOO_MANY_REQUESTS")
|
||||
) {
|
||||
const { ipAddress, userAgent } = getAuditContext(
|
||||
ctx.event.nativeEvent
|
||||
getH3Event(ctx)
|
||||
);
|
||||
await logAuditEvent({
|
||||
eventType: "auth.email_verification.requested",
|
||||
eventType: "auth.email.verify.request",
|
||||
eventData: {
|
||||
email: input.email,
|
||||
reason: error instanceof TRPCError ? error.message : "unknown"
|
||||
@@ -1468,7 +1509,7 @@ export const authRouter = createTRPCRouter({
|
||||
// Try to get user ID for audit log before clearing cookies
|
||||
let userId: string | null = null;
|
||||
try {
|
||||
const token = getCookie(ctx.event.nativeEvent, "userIDToken");
|
||||
const token = getCookie(getH3Event(ctx), "userIDToken");
|
||||
if (token) {
|
||||
const secret = new TextEncoder().encode(env.JWT_SECRET_KEY);
|
||||
const { payload } = await jwtVerify(token, secret);
|
||||
@@ -1478,17 +1519,17 @@ export const authRouter = createTRPCRouter({
|
||||
// Ignore token verification errors during signout
|
||||
}
|
||||
|
||||
setCookie(ctx.event.nativeEvent, "userIDToken", "", {
|
||||
setCookie(getH3Event(ctx), "userIDToken", "", {
|
||||
maxAge: 0,
|
||||
path: "/"
|
||||
});
|
||||
setCookie(ctx.event.nativeEvent, "emailToken", "", {
|
||||
setCookie(getH3Event(ctx), "emailToken", "", {
|
||||
maxAge: 0,
|
||||
path: "/"
|
||||
});
|
||||
|
||||
// Log signout
|
||||
const { ipAddress, userAgent } = getAuditContext(ctx.event.nativeEvent);
|
||||
const { ipAddress, userAgent } = getAuditContext(getH3Event(ctx));
|
||||
await logAuditEvent({
|
||||
userId,
|
||||
eventType: "auth.logout",
|
||||
|
||||
Reference in New Issue
Block a user