-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
122 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import cls from 'cls-hooked'; | ||
import { Request } from 'express'; | ||
import { SESSION_ID_KEY_NAME } from '@common/types'; | ||
|
||
const NAMESPACE_NAME = 'session-id-namespace'; | ||
|
||
export const sessionIdNamespace = cls.createNamespace(NAMESPACE_NAME); | ||
|
||
export const getNamespace = () => cls.getNamespace(NAMESPACE_NAME); | ||
|
||
export const setNamespace = (key: string, value: Request) => { | ||
const namespace = getNamespace(); | ||
if (namespace) { | ||
namespace.set(key, value); | ||
} | ||
}; | ||
|
||
const getFromNamespace = <T>(key: 'req' | typeof SESSION_ID_KEY_NAME): T | null => { | ||
const namespace = getNamespace(); | ||
return namespace ? namespace.get(key) : null; | ||
}; | ||
|
||
// Helper function for getting the current request from CLS | ||
export const getCurrentRequest = (): Request | null => { | ||
return getFromNamespace<Request>('req'); | ||
}; | ||
|
||
// Helper function for getting the current requestId from CLS | ||
export const getCurrentSessionId = (): string | null => { | ||
return getFromNamespace<string>(SESSION_ID_KEY_NAME); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { createHmac } from 'crypto'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import { NextFunction, Request, Response } from 'express'; | ||
import { logger } from '@js/utils'; | ||
import { SESSION_ID_KEY_NAME } from '@common/types'; | ||
import { sessionIdNamespace } from '@common/lib/cls/session-id'; | ||
|
||
const SECRET_KEY = process.env.APP_SESSION_ID_SECRET as string; | ||
|
||
export const sessionMiddleware = async (req: Request, res: Response, next: NextFunction) => { | ||
if (!SECRET_KEY) { | ||
logger.error('"sessionMiddleware": secrey key is missing!'); | ||
return next(); | ||
} | ||
|
||
try { | ||
let sessionId: string | null = null; | ||
|
||
const token = req.headers['x-session-id']; | ||
|
||
// Take session id from the request's headers | ||
if (token && typeof token === 'string') { | ||
const decoded = verifySignedSessionId(token); | ||
|
||
// if session id is valid, use it | ||
// WARNING: might want to throw 403 error in the future, if session_id is used | ||
// not only for logs | ||
if (decoded) { | ||
sessionId = token; | ||
} | ||
} | ||
|
||
// if session id previously wasn't validated, generate a new one | ||
if (!sessionId) { | ||
sessionId = generateSignedSessionId(); | ||
} | ||
|
||
if (sessionId) { | ||
res.setHeader('X-Session-ID', sessionId); | ||
|
||
sessionIdNamespace.run(() => { | ||
sessionIdNamespace.set(SESSION_ID_KEY_NAME, sessionId); | ||
sessionIdNamespace.set('req', req); | ||
|
||
req[SESSION_ID_KEY_NAME] = sessionId; | ||
next(); | ||
}); | ||
} else { | ||
logger.warn('"sessionMiddleware": wasnt able to set sessionId for unexpected reasons.'); | ||
next(); | ||
} | ||
} catch (err) { | ||
logger.error('"sessionMiddleware" failed due to unexpected error'); | ||
next(); | ||
} | ||
}; | ||
|
||
// Generate a signed sessionId | ||
const generateSignedSessionId = () => { | ||
const sessionId = uuidv4(); | ||
const signature = createHmac('sha256', SECRET_KEY).update(sessionId).digest('hex'); | ||
return `${sessionId}.${signature}`; | ||
}; | ||
|
||
// Verify a signed sessionId | ||
const verifySignedSessionId = (signedSessionId: string) => { | ||
const [sessionId, signature] = signedSessionId.split('.'); | ||
if (!sessionId || !signature) { | ||
return false; | ||
} | ||
|
||
const expectedSignature = createHmac('sha256', SECRET_KEY).update(sessionId).digest('hex'); | ||
|
||
if (!expectedSignature) logger.warn('Session ID validation failed.'); | ||
|
||
return signature === expectedSignature; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import { endpointsTypes } from 'shared-types'; | ||
import type { SESSION_ID_KEY_NAME } from '@common/types'; | ||
|
||
declare module 'express' { | ||
interface Request { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
body: endpointsTypes.BodyPayload<any>; | ||
requestId?: string; // Optional requestId property | ||
[SESSION_ID_KEY_NAME]?: string | null; // Optional sessionId property | ||
} | ||
} |