Implement auth procedures with code review fixes
Add complete auth backend (Workstream D): - Auth middleware for session/API key authentication - Signup with password or passkey (WebAuthn) - Login flow with device trust and email confirmation - Password reset and email verification - Session management and logout Utilities created: - cookies.ts: Cookie helpers and configuration - crypto.ts: Token generation and hashing - password.ts: zxcvbn validation, argon2id hashing - geo.ts: IP/location extraction from headers - email.ts: Stubbed email sending - session.ts: Session creation and device trust Code review improvements applied: - Use ORPCError instead of Error in procedures - Add ast-grep rule to enforce ORPCError usage - Remove error info leakage (generic messages) - Optimize N+1 query with JOIN in login-password - Extract signupWithPassword/signupWithPasskey for testability - Add 15-minute WebAuthn challenge expiry check - Strengthen CookieOptions type definitions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
42
apps/api-server/src/utils/geo.ts
Normal file
42
apps/api-server/src/utils/geo.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export interface GeoInfo {
|
||||
ip: string | null;
|
||||
city: string | null;
|
||||
region: string | null;
|
||||
country: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract geolocation info from request headers
|
||||
* Supports Cloudflare headers in production, falls back to standard headers
|
||||
* @param headers - Request headers
|
||||
* @returns Geolocation information extracted from headers
|
||||
*/
|
||||
export const getGeoInfo = (headers: Headers): GeoInfo => {
|
||||
// Try Cloudflare headers first (production)
|
||||
const cfIP = headers.get("CF-Connecting-IP");
|
||||
const cfCountry = headers.get("CF-IPCountry");
|
||||
const cfCity = headers.get("CF-IPCity");
|
||||
const cfRegion = headers.get("CF-Region");
|
||||
|
||||
// Fallback to X-Forwarded-For or X-Real-IP
|
||||
const forwardedFor = headers.get("X-Forwarded-For");
|
||||
const realIP = headers.get("X-Real-IP");
|
||||
|
||||
const ip = cfIP ?? realIP ?? forwardedFor?.split(",")[0]?.trim() ?? null;
|
||||
|
||||
return {
|
||||
ip,
|
||||
city: cfCity ?? null,
|
||||
region: cfRegion ?? null,
|
||||
country: cfCountry ?? null,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract User-Agent from request headers
|
||||
* @param headers - Request headers
|
||||
* @returns User-Agent string or "Unknown" if not present
|
||||
*/
|
||||
export const getUserAgent = (headers: Headers): string => {
|
||||
return headers.get("User-Agent") ?? "Unknown";
|
||||
};
|
||||
Reference in New Issue
Block a user