update project
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { verifyAccessToken, AuthContext } from '../services/jwt.service';
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
auth?: AuthContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractToken(req: Request): string | null {
|
||||
const cookie = req.cookies?.access_token;
|
||||
if (cookie) return cookie;
|
||||
|
||||
const auth = req.headers.authorization;
|
||||
if (auth) {
|
||||
const [scheme, token] = auth.split(' ');
|
||||
if (scheme?.toLowerCase() === 'bearer' && token) return token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function authMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const token = extractToken(req);
|
||||
|
||||
if (!token) {
|
||||
res.status(401).json({ success: false, error: 'Not authenticated' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
req.auth = await verifyAccessToken(token);
|
||||
next();
|
||||
} catch (err: any) {
|
||||
res.status(err.status || 401).json({ success: false, error: err.message || 'Invalid token' });
|
||||
}
|
||||
}
|
||||
60
apps/api/src/middleware/bitok-auth.ts
Normal file
60
apps/api/src/middleware/bitok-auth.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { jwtVerify, decodeProtectedHeader } from 'jose';
|
||||
import { getSigningKey } from '../services/jwks.service';
|
||||
import { env } from '../config/env';
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: { bitokUserId: string; email?: string };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function bitokAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const header = req.headers.authorization;
|
||||
if (!header?.startsWith('Bearer ')) {
|
||||
res.status(401).json({ success: false, error: 'No token provided' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = header.slice(7);
|
||||
|
||||
// Decode header to get kid
|
||||
const protectedHeader = decodeProtectedHeader(token);
|
||||
if (protectedHeader.alg !== 'RS256') {
|
||||
res.status(401).json({ success: false, error: 'Invalid token algorithm' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protectedHeader.kid) {
|
||||
res.status(401).json({ success: false, error: 'Token missing kid' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the signing key for this kid
|
||||
const key = await getSigningKey(protectedHeader.kid);
|
||||
|
||||
// Verify the token
|
||||
const { payload } = await jwtVerify(token, key, {
|
||||
issuer: env.bitokIssuer,
|
||||
audience: env.bitokAudience,
|
||||
algorithms: ['RS256'],
|
||||
});
|
||||
|
||||
if (!payload.sub) {
|
||||
res.status(401).json({ success: false, error: 'Token missing subject' });
|
||||
return;
|
||||
}
|
||||
|
||||
req.user = {
|
||||
bitokUserId: payload.sub,
|
||||
email: payload.email as string | undefined,
|
||||
};
|
||||
|
||||
next();
|
||||
} catch {
|
||||
res.status(401).json({ success: false, error: 'Invalid or expired token' });
|
||||
}
|
||||
}
|
||||
25
apps/api/src/middleware/rate-limit.ts
Normal file
25
apps/api/src/middleware/rate-limit.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
export const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 20,
|
||||
message: { success: false, error: 'Too many login attempts, try again later' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
export const registerLimiter = rateLimit({
|
||||
windowMs: 60 * 60 * 1000,
|
||||
max: 3,
|
||||
message: { success: false, error: 'Too many registration attempts, try again later' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
export const seedPhraseLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 3,
|
||||
message: { success: false, error: 'Too many attempts. Try again in 15 minutes.' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
Reference in New Issue
Block a user