enh
This commit is contained in:
parent
a0a05615b1
commit
b49015b446
4 changed files with 53 additions and 24 deletions
|
|
@ -16,7 +16,7 @@ import signedUrlCache from '@/src/lib/signed-url-cache';
|
||||||
import { seed } from '@/src/db/seed';
|
import { seed } from '@/src/db/seed';
|
||||||
import '@/src/jobs/jobs-index';
|
import '@/src/jobs/jobs-index';
|
||||||
import { startAutomatedJobs } from '@/src/lib/automatedJobs';
|
import { startAutomatedJobs } from '@/src/lib/automatedJobs';
|
||||||
import { verifyToken } from '@/src/lib/jwt-utils';
|
import { verifyToken, UserJWTPayload, StaffJWTPayload } from '@/src/lib/jwt-utils';
|
||||||
|
|
||||||
seed()
|
seed()
|
||||||
initFunc()
|
initFunc()
|
||||||
|
|
@ -77,30 +77,35 @@ app.use('/api/trpc', createExpressMiddleware({
|
||||||
if (authHeader?.startsWith('Bearer ')) {
|
if (authHeader?.startsWith('Bearer ')) {
|
||||||
const token = authHeader.substring(7);
|
const token = authHeader.substring(7);
|
||||||
try {
|
try {
|
||||||
const decoded = (await verifyToken(token)) as any;
|
const decoded = await verifyToken(token);
|
||||||
|
|
||||||
// Check if this is a staff token (has staffId)
|
// Check if this is a staff token (has staffId)
|
||||||
if (decoded.staffId) {
|
if ('staffId' in decoded) {
|
||||||
|
const staffPayload = decoded as StaffJWTPayload;
|
||||||
// This is a staff token, verify staff exists
|
// This is a staff token, verify staff exists
|
||||||
const staff = await db.query.staffUsers.findFirst({
|
const staff = await db.query.staffUsers.findFirst({
|
||||||
where: eq(staffUsers.id, decoded.staffId),
|
where: eq(staffUsers.id, staffPayload.staffId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (staff) {
|
if (staff) {
|
||||||
user=staffUser
|
|
||||||
staffUser = {
|
staffUser = {
|
||||||
id: staff.id,
|
id: staff.id,
|
||||||
name: staff.name,
|
name: staff.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const userPayload = decoded as UserJWTPayload;
|
||||||
// This is a regular user token
|
// This is a regular user token
|
||||||
user = decoded;
|
user = {
|
||||||
|
userId: userPayload.userId,
|
||||||
|
name: userPayload.name,
|
||||||
|
email: userPayload.email,
|
||||||
|
mobile: userPayload.mobile,
|
||||||
|
};
|
||||||
|
|
||||||
// Check if user is suspended
|
// Check if user is suspended
|
||||||
const details = await db.query.userDetails.findFirst({
|
const details = await db.query.userDetails.findFirst({
|
||||||
where: eq(userDetails.userId, user.userId),
|
where: eq(userDetails.userId, userPayload.userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (details?.isSuspended) {
|
if (details?.isSuspended) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
import { SignJWT, jwtVerify, errors } from 'jose';
|
import { SignJWT, jwtVerify, errors, JWTPayload } from 'jose';
|
||||||
import { jwtSecret } from './env-exporter';
|
import { jwtSecret } from './env-exporter';
|
||||||
|
|
||||||
|
// JWT Payload Types
|
||||||
|
export interface UserJWTPayload extends JWTPayload {
|
||||||
|
userId: number;
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
mobile?: string;
|
||||||
|
roles?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StaffJWTPayload extends JWTPayload {
|
||||||
|
staffId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert string secret to Uint8Array
|
// Convert string secret to Uint8Array
|
||||||
const getSecret = () => {
|
const getSecret = () => {
|
||||||
if (!jwtSecret) {
|
if (!jwtSecret) {
|
||||||
|
|
@ -14,7 +28,7 @@ const getSecret = () => {
|
||||||
* Compatible with tokens signed by jsonwebtoken library
|
* Compatible with tokens signed by jsonwebtoken library
|
||||||
*/
|
*/
|
||||||
export const signToken = async (
|
export const signToken = async (
|
||||||
payload: Record<string, any>,
|
payload: Record<string, unknown>,
|
||||||
expiresIn: string = '7d'
|
expiresIn: string = '7d'
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const secret = getSecret();
|
const secret = getSecret();
|
||||||
|
|
@ -30,7 +44,7 @@ export const signToken = async (
|
||||||
* Verify a JWT token
|
* Verify a JWT token
|
||||||
* Compatible with tokens signed by jsonwebtoken library
|
* Compatible with tokens signed by jsonwebtoken library
|
||||||
*/
|
*/
|
||||||
export const verifyToken = async (token: string): Promise<Record<string, any>> => {
|
export const verifyToken = async (token: string): Promise<JWTPayload> => {
|
||||||
try {
|
try {
|
||||||
const secret = getSecret();
|
const secret = getSecret();
|
||||||
const { payload } = await jwtVerify(token, secret);
|
const { payload } = await jwtVerify(token, secret);
|
||||||
|
|
@ -49,8 +63,10 @@ export const verifyToken = async (token: string): Promise<Record<string, any>> =
|
||||||
/**
|
/**
|
||||||
* Check if an error is a JWT-related error
|
* Check if an error is a JWT-related error
|
||||||
*/
|
*/
|
||||||
export const isJWTError = (error: any): boolean => {
|
export const isJWTError = (error: unknown): boolean => {
|
||||||
return error instanceof errors.JOSEError ||
|
return error instanceof errors.JOSEError ||
|
||||||
error?.message?.includes('token') ||
|
(error instanceof Error && (
|
||||||
error?.message?.includes('JWT');
|
error.message.includes('token') ||
|
||||||
|
error.message.includes('JWT')
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { db } from '@/src/db/db_index'
|
||||||
import { staffUsers, userDetails } from '@/src/db/schema'
|
import { staffUsers, userDetails } from '@/src/db/schema'
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { ApiError } from '@/src/lib/api-error'
|
import { ApiError } from '@/src/lib/api-error'
|
||||||
import { verifyToken } from '@/src/lib/jwt-utils'
|
import { verifyToken, UserJWTPayload, StaffJWTPayload } from '@/src/lib/jwt-utils'
|
||||||
|
|
||||||
interface AuthenticatedRequest extends Request {
|
interface AuthenticatedRequest extends Request {
|
||||||
user?: {
|
user?: {
|
||||||
|
|
@ -29,13 +29,14 @@ export const authenticateUser = async (req: AuthenticatedRequest, res: Response,
|
||||||
const token = authHeader.substring(7);
|
const token = authHeader.substring(7);
|
||||||
console.log(req.headers)
|
console.log(req.headers)
|
||||||
|
|
||||||
const decoded = (await verifyToken(token)) as any;
|
const decoded = await verifyToken(token);
|
||||||
|
|
||||||
// Check if this is a staff token (has staffId)
|
// Check if this is a staff token (has staffId)
|
||||||
if (decoded.staffId) {
|
if ('staffId' in decoded) {
|
||||||
|
const staffPayload = decoded as StaffJWTPayload;
|
||||||
// This is a staff token, verify staff exists
|
// This is a staff token, verify staff exists
|
||||||
const staff = await db.query.staffUsers.findFirst({
|
const staff = await db.query.staffUsers.findFirst({
|
||||||
where: eq(staffUsers.id, decoded.staffId),
|
where: eq(staffUsers.id, staffPayload.staffId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!staff) {
|
if (!staff) {
|
||||||
|
|
@ -47,12 +48,18 @@ export const authenticateUser = async (req: AuthenticatedRequest, res: Response,
|
||||||
name: staff.name,
|
name: staff.name,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
const userPayload = decoded as UserJWTPayload;
|
||||||
// This is a regular user token
|
// This is a regular user token
|
||||||
req.user = decoded;
|
req.user = {
|
||||||
|
userId: userPayload.userId,
|
||||||
|
name: userPayload.name,
|
||||||
|
email: userPayload.email,
|
||||||
|
mobile: userPayload.mobile,
|
||||||
|
};
|
||||||
|
|
||||||
// Check if user is suspended
|
// Check if user is suspended
|
||||||
const details = await db.query.userDetails.findFirst({
|
const details = await db.query.userDetails.findFirst({
|
||||||
where: eq(userDetails.userId, decoded.userId),
|
where: eq(userDetails.userId, userPayload.userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (details?.isSuspended) {
|
if (details?.isSuspended) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { db } from '@/src/db/db_index'
|
||||||
import { staffUsers } from '@/src/db/schema'
|
import { staffUsers } from '@/src/db/schema'
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { ApiError } from '@/src/lib/api-error'
|
import { ApiError } from '@/src/lib/api-error'
|
||||||
import { verifyToken } from '@/src/lib/jwt-utils'
|
import { verifyToken, StaffJWTPayload } from '@/src/lib/jwt-utils'
|
||||||
|
|
||||||
// Extend Request interface to include staffUser
|
// Extend Request interface to include staffUser
|
||||||
declare global {
|
declare global {
|
||||||
|
|
@ -20,9 +20,10 @@ declare global {
|
||||||
/**
|
/**
|
||||||
* Verify JWT token and extract payload
|
* Verify JWT token and extract payload
|
||||||
*/
|
*/
|
||||||
const verifyStaffToken = async (token: string) => {
|
const verifyStaffToken = async (token: string): Promise<StaffJWTPayload> => {
|
||||||
try {
|
try {
|
||||||
return await verifyToken(token);
|
const payload = await verifyToken(token);
|
||||||
|
return payload as StaffJWTPayload;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ApiError('Access denied. Invalid auth credentials', 401);
|
throw new ApiError('Access denied. Invalid auth credentials', 401);
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +48,7 @@ export const authenticateStaff = async (req: Request, res: Response, next: NextF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify token and extract payload
|
// Verify token and extract payload
|
||||||
const decoded = (await verifyStaffToken(token)) as any;
|
const decoded = await verifyStaffToken(token);
|
||||||
|
|
||||||
// Verify staffId exists in token
|
// Verify staffId exists in token
|
||||||
if (!decoded.staffId) {
|
if (!decoded.staffId) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue