flwo / apps /server /src /lib /jwt.js
moustafa7's picture
refactor: major security, performance, and API quality improvements
4a31b63
Raw History Blame Contribute Delete
1.63 kB
import jwt from 'jsonwebtoken'
import { randomUUID } from 'crypto'
import { config } from '../config/index.js'
import { safeRedis } from '../config/redis.js'
const ISS = 'templyn-api'
const AUD_CLIENT = 'templyn-client'
const AUD_ADMIN = 'templyn-admin'
export function signAccessToken(userId, role) {
return jwt.sign(
{ sub: userId, role, type: 'access', jti: randomUUID(), iss: ISS, aud: role === 'CLIENT' ? AUD_CLIENT : AUD_ADMIN },
config.JWT_ACCESS_SECRET,
{ expiresIn: config.JWT_ACCESS_EXPIRES }
)
}
export function signRefreshToken(userId) {
return jwt.sign(
{ sub: userId, type: 'refresh', jti: randomUUID(), iss: ISS, aud: AUD_CLIENT },
config.JWT_REFRESH_SECRET,
{ expiresIn: config.JWT_REFRESH_EXPIRES }
)
}
export function verifyAccessToken(token) {
const decoded = jwt.verify(token, config.JWT_ACCESS_SECRET)
if (decoded.iss !== ISS) throw new Error('Invalid issuer')
return decoded
}
export function verifyRefreshToken(token) {
const decoded = jwt.verify(token, config.JWT_REFRESH_SECRET)
if (decoded.iss !== ISS) throw new Error('Invalid issuer')
return decoded
}
export async function blacklistToken(token) {
const decoded = jwt.decode(token)
if (!decoded?.exp) return
const ttl = decoded.exp - Math.floor(Date.now() / 1000)
if (ttl > 0) await safeRedis.setex(`blacklist:${token}`, ttl, '1')
}
export async function verifySocketToken(token) {
try {
const decoded = verifyAccessToken(token)
const isBlacklisted = await safeRedis.get(`blacklist:${token}`)
if (isBlacklisted) return null
return decoded
} catch {
return null
}
}