2022-06-23 21:22:51 +02:00
|
|
|
const redis = require("../redis/init")
|
2022-01-25 23:54:50 +01:00
|
|
|
const { v4: uuidv4 } = require("uuid")
|
2022-08-04 17:06:59 +02:00
|
|
|
const { logWarn } = require("../logging")
|
2022-08-04 20:03:50 +02:00
|
|
|
const env = require("../environment")
|
2021-07-06 19:10:04 +02:00
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
interface CreateSession {
|
2022-08-05 18:13:03 +02:00
|
|
|
sessionId: string
|
2022-08-25 20:41:47 +02:00
|
|
|
tenantId: string
|
|
|
|
csrfToken?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Session extends CreateSession {
|
|
|
|
userId: string
|
2022-08-05 18:13:03 +02:00
|
|
|
lastAccessedAt: string
|
|
|
|
createdAt: string
|
2022-08-25 20:41:47 +02:00
|
|
|
// make optional attributes required
|
|
|
|
csrfToken: string
|
2022-08-05 18:13:03 +02:00
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
interface SessionKey {
|
|
|
|
key: string
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ScannedSession {
|
|
|
|
value: Session
|
|
|
|
}
|
2022-08-05 18:13:03 +02:00
|
|
|
|
2021-12-03 12:17:48 +01:00
|
|
|
// a week in seconds
|
|
|
|
const EXPIRY_SECONDS = 86400 * 7
|
2021-07-06 19:10:04 +02:00
|
|
|
|
2022-08-05 18:13:03 +02:00
|
|
|
function makeSessionID(userId: string, sessionId: string) {
|
|
|
|
return `${userId}/${sessionId}`
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
export async function getSessionsForUser(userId: string): Promise<Session[]> {
|
2022-08-08 10:34:45 +02:00
|
|
|
if (!userId) {
|
|
|
|
console.trace("Cannot get sessions for undefined userId")
|
|
|
|
return []
|
|
|
|
}
|
2022-08-05 18:13:03 +02:00
|
|
|
const client = await redis.getSessionClient()
|
2022-08-25 20:41:47 +02:00
|
|
|
const sessions: ScannedSession[] = await client.scan(userId)
|
|
|
|
return sessions.map(session => session.value)
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|
|
|
|
|
2022-08-05 18:13:03 +02:00
|
|
|
export async function invalidateSessions(
|
|
|
|
userId: string,
|
|
|
|
opts: { sessionIds?: string[]; reason?: string } = {}
|
|
|
|
) {
|
2022-05-24 23:57:32 +02:00
|
|
|
try {
|
2022-08-05 18:13:03 +02:00
|
|
|
const reason = opts?.reason || "unknown"
|
|
|
|
let sessionIds: string[] = opts.sessionIds || []
|
2022-08-25 20:41:47 +02:00
|
|
|
let sessionKeys: SessionKey[]
|
2022-05-24 23:57:32 +02:00
|
|
|
|
|
|
|
// If no sessionIds, get all the sessions for the user
|
2022-08-05 22:35:26 +02:00
|
|
|
if (sessionIds.length === 0) {
|
2022-08-25 20:41:47 +02:00
|
|
|
const sessions = await getSessionsForUser(userId)
|
|
|
|
sessionKeys = sessions.map(session => ({
|
|
|
|
key: makeSessionID(session.userId, session.sessionId),
|
|
|
|
}))
|
2022-05-24 23:57:32 +02:00
|
|
|
} else {
|
|
|
|
// use the passed array of sessionIds
|
2022-08-05 18:13:03 +02:00
|
|
|
sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
|
2022-08-25 20:41:47 +02:00
|
|
|
sessionKeys = sessionIds.map(sessionId => ({
|
2022-05-24 23:57:32 +02:00
|
|
|
key: makeSessionID(userId, sessionId),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
if (sessionKeys && sessionKeys.length > 0) {
|
2022-08-04 20:03:50 +02:00
|
|
|
const client = await redis.getSessionClient()
|
|
|
|
const promises = []
|
2022-08-25 20:41:47 +02:00
|
|
|
for (let sessionKey of sessionKeys) {
|
|
|
|
promises.push(client.delete(sessionKey.key))
|
2022-08-04 20:03:50 +02:00
|
|
|
}
|
|
|
|
if (!env.isTest()) {
|
|
|
|
logWarn(
|
2022-08-25 20:41:47 +02:00
|
|
|
`Invalidating sessions for ${userId} (reason: ${reason}) - ${sessionKeys
|
|
|
|
.map(sessionKey => sessionKey.key)
|
2022-08-04 20:03:50 +02:00
|
|
|
.join(", ")}`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
await Promise.all(promises)
|
2022-05-24 23:57:32 +02:00
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.error(`Error invalidating sessions: ${err}`)
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
export async function createASession(
|
|
|
|
userId: string,
|
|
|
|
createSession: CreateSession
|
|
|
|
) {
|
2022-04-07 13:32:00 +02:00
|
|
|
// invalidate all other sessions
|
2022-08-05 18:13:03 +02:00
|
|
|
await invalidateSessions(userId, { reason: "creation" })
|
2022-04-07 13:32:00 +02:00
|
|
|
|
|
|
|
const client = await redis.getSessionClient()
|
2022-08-25 20:41:47 +02:00
|
|
|
const sessionId = createSession.sessionId
|
|
|
|
const csrfToken = createSession.csrfToken ? createSession.csrfToken : uuidv4()
|
|
|
|
const key = makeSessionID(userId, sessionId)
|
|
|
|
|
|
|
|
const session: Session = {
|
|
|
|
...createSession,
|
|
|
|
csrfToken,
|
2022-04-07 13:32:00 +02:00
|
|
|
createdAt: new Date().toISOString(),
|
|
|
|
lastAccessedAt: new Date().toISOString(),
|
|
|
|
userId,
|
|
|
|
}
|
2022-08-25 20:41:47 +02:00
|
|
|
await client.store(key, session, EXPIRY_SECONDS)
|
2022-04-07 13:32:00 +02:00
|
|
|
}
|
|
|
|
|
2022-08-05 18:13:03 +02:00
|
|
|
export async function updateSessionTTL(session: Session) {
|
2021-07-06 19:10:04 +02:00
|
|
|
const client = await redis.getSessionClient()
|
2021-07-08 00:29:19 +02:00
|
|
|
const key = makeSessionID(session.userId, session.sessionId)
|
2021-07-08 00:30:14 +02:00
|
|
|
session.lastAccessedAt = new Date().toISOString()
|
2021-07-08 00:29:19 +02:00
|
|
|
await client.store(key, session, EXPIRY_SECONDS)
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|
|
|
|
|
2022-08-05 18:13:03 +02:00
|
|
|
export async function endSession(userId: string, sessionId: string) {
|
2021-07-06 19:10:04 +02:00
|
|
|
const client = await redis.getSessionClient()
|
|
|
|
await client.delete(makeSessionID(userId, sessionId))
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
export async function getSession(
|
|
|
|
userId: string,
|
|
|
|
sessionId: string
|
|
|
|
): Promise<Session> {
|
2022-08-05 22:35:26 +02:00
|
|
|
if (!userId || !sessionId) {
|
|
|
|
throw new Error(`Invalid session details - ${userId} - ${sessionId}`)
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|
|
|
|
const client = await redis.getSessionClient()
|
2022-08-05 22:35:26 +02:00
|
|
|
const session = await client.get(makeSessionID(userId, sessionId))
|
|
|
|
if (!session) {
|
|
|
|
throw new Error(`Session not found - ${userId} - ${sessionId}`)
|
|
|
|
}
|
|
|
|
return session
|
2021-07-06 19:10:04 +02:00
|
|
|
}
|