mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
237 lines
7.3 KiB
JavaScript
237 lines
7.3 KiB
JavaScript
import jwt from "jsonwebtoken"
|
|
import { Session, RegenerationToken, User } from "../../db_models"
|
|
|
|
export default class Token {
|
|
static get strategy() {
|
|
return {
|
|
secret: process.env.JWT_SECRET,
|
|
expiresIn: process.env.JWT_EXPIRES_IN ?? "1h",
|
|
algorithm: process.env.JWT_ALGORITHM ?? "HS256",
|
|
}
|
|
}
|
|
|
|
static async createNewAuthToken(payload, options = {}) {
|
|
if (options.updateSession) {
|
|
const sessionData = await Session.findOne({ _id: options.updateSession })
|
|
|
|
payload.session_uuid = sessionData.session_uuid
|
|
} else {
|
|
payload.session_uuid = global.nanoid()
|
|
}
|
|
|
|
const { secret, expiresIn, algorithm } = Token.strategy
|
|
|
|
const token = jwt.sign(
|
|
{
|
|
session_uuid: payload.session_uuid,
|
|
username: payload.username,
|
|
user_id: payload.user_id,
|
|
signLocation: payload.signLocation,
|
|
},
|
|
secret,
|
|
{
|
|
expiresIn: expiresIn,
|
|
algorithm: algorithm
|
|
}
|
|
)
|
|
|
|
return token
|
|
}
|
|
|
|
static async validate(token) {
|
|
let result = {
|
|
expired: false,
|
|
valid: true,
|
|
error: null,
|
|
data: null,
|
|
}
|
|
|
|
if (typeof token === "undefined") {
|
|
result.valid = false
|
|
result.error = "Missing token"
|
|
|
|
return result
|
|
}
|
|
|
|
const { secret } = Token.strategy
|
|
|
|
await jwt.verify(token, secret, async (err, decoded) => {
|
|
if (err) {
|
|
result.valid = false
|
|
result.error = err.message
|
|
|
|
if (err.message === "jwt expired") {
|
|
result.expired = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
result.data = decoded
|
|
|
|
const sessions = await Session.find({ user_id: decoded.user_id })
|
|
const currentSession = sessions.find((session) => session.token === token)
|
|
|
|
if (!currentSession) {
|
|
result.valid = false
|
|
result.error = "Session token not found"
|
|
} else {
|
|
result.session = currentSession
|
|
result.valid = true
|
|
result.user = async () => await User.findOne({ _id: decoded.user_id })
|
|
}
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
static async regenerate(expiredToken, refreshToken, aggregateData = {}) {
|
|
// search for a regeneration token with the expired token (Should exist only one)
|
|
const regenerationToken = await RegenerationToken.findOne({ refreshToken: refreshToken })
|
|
|
|
if (!regenerationToken) {
|
|
throw new Error("Cannot find regeneration token")
|
|
}
|
|
|
|
// check if the regeneration token is valid and not expired
|
|
let decodedRefreshToken = null
|
|
let decodedExpiredToken = null
|
|
|
|
try {
|
|
decodedRefreshToken = jwt.decode(refreshToken)
|
|
decodedExpiredToken = jwt.decode(expiredToken)
|
|
} catch (error) {
|
|
console.error(error)
|
|
// TODO: Storage this incident
|
|
}
|
|
|
|
if (!decodedRefreshToken) {
|
|
throw new Error("Cannot decode refresh token")
|
|
}
|
|
|
|
if (!decodedExpiredToken) {
|
|
throw new Error("Cannot decode expired token")
|
|
}
|
|
|
|
// is not needed to verify the expired token, because it suppossed to be expired
|
|
|
|
// verify refresh token
|
|
await jwt.verify(refreshToken, global.jwtStrategy.secretOrKey, async (err) => {
|
|
// check if is expired
|
|
if (err) {
|
|
if (err.message === "jwt expired") {
|
|
// check if server has enabled the enforcement of regeneration token expiration
|
|
if (global.jwtStrategy.enforceRegenerationTokenExpiration) {
|
|
// delete the regeneration token
|
|
await RegenerationToken.deleteOne({ refreshToken: refreshToken })
|
|
|
|
throw new Error("Regeneration token expired and cannot be regenerated due server has enabled enforcement security policy")
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if the regeneration token is associated with the expired token
|
|
if (decodedRefreshToken.expiredToken !== expiredToken) {
|
|
throw new Error("Regeneration token is not associated with the expired token")
|
|
}
|
|
})
|
|
|
|
// find the session associated with the expired token
|
|
const session = await Session.findOne({ token: expiredToken })
|
|
|
|
if (!session) {
|
|
throw new Error("Cannot find session associated with the expired token")
|
|
}
|
|
|
|
// generate a new token
|
|
const newToken = await this.createNewAuthToken({
|
|
username: decodedExpiredToken.username,
|
|
session_uuid: session.session_uuid,
|
|
user_id: decodedExpiredToken.user_id,
|
|
ip_address: aggregateData.ip_address,
|
|
}, {
|
|
updateSession: session._id,
|
|
})
|
|
|
|
// delete the regeneration token
|
|
await RegenerationToken.deleteOne({ refreshToken: refreshToken })
|
|
|
|
return newToken
|
|
}
|
|
|
|
static async createAuth(payload, options = {}) {
|
|
const token = await this.createNewAuthToken(payload, options)
|
|
|
|
const session = {
|
|
token: token,
|
|
session_uuid: payload.session_uuid,
|
|
username: payload.username,
|
|
user_id: payload.user_id,
|
|
location: payload.signLocation,
|
|
ip_address: payload.ip_address,
|
|
client: payload.client,
|
|
date: new Date().getTime(),
|
|
}
|
|
|
|
if (options.updateSession) {
|
|
await Session.findByIdAndUpdate(options.updateSession, session)
|
|
} else {
|
|
let newSession = new Session(session)
|
|
|
|
await newSession.save()
|
|
}
|
|
|
|
return token
|
|
}
|
|
|
|
static async createRegenerative(expiredToken) {
|
|
// check if token is only expired, if is corrupted, reject
|
|
let decoded = null
|
|
|
|
try {
|
|
decoded = jwt.decode(expiredToken)
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
|
|
if (!decoded) {
|
|
return false
|
|
}
|
|
|
|
// check if token exists on a session
|
|
const sessions = await Session.find({ user_id: decoded.user_id })
|
|
const currentSession = sessions.find((session) => session.token === expiredToken)
|
|
|
|
if (!currentSession) {
|
|
throw new Error("This token is not associated with any session")
|
|
}
|
|
|
|
// create a new refresh token and sign it with maximum expiration time of 1 day
|
|
const refreshToken = jwt.sign(
|
|
{
|
|
expiredToken
|
|
},
|
|
global.jwtStrategy.secretOrKey,
|
|
{
|
|
expiresIn: "1d"
|
|
}
|
|
)
|
|
|
|
// create a new regeneration token and save it
|
|
const regenerationToken = new RegenerationToken({
|
|
expiredToken,
|
|
refreshToken,
|
|
})
|
|
|
|
await regenerationToken.save()
|
|
|
|
// return the regeneration token
|
|
return regenerationToken
|
|
}
|
|
|
|
static async getRegenerationToken(expiredToken) {
|
|
const regenerationToken = await RegenerationToken.findOne({ expiredToken })
|
|
|
|
return regenerationToken
|
|
}
|
|
} |