import jwt from "jsonwebtoken" import { Session, RefreshToken, User, TosViolations } from "@db_models" export default class Token { static get authStrategy() { return { secret: process.env.JWT_SECRET, expiresIn: process.env.JWT_EXPIRES_IN ?? "24h", algorithm: process.env.JWT_ALGORITHM ?? "HS256", } } static get refreshStrategy() { return { secret: process.env.JWT_SECRET, expiresIn: process.env.JWT_REFRESH_EXPIRES_IN ?? "30d", algorithm: process.env.JWT_ALGORITHM ?? "HS256", } } static async signToken(payload, strategy = "authStrategy") { const { secret, expiresIn, algorithm } = Token[strategy] ?? Token.authStrategy const token = jwt.sign(payload, secret, { expiresIn: expiresIn, algorithm: algorithm, }) return token } static async createAuthToken(payload) { const jwt_token = await this.signToken(payload, "authStrategy") const session = new Session({ token: jwt_token, username: payload.username, user_id: payload.user_id, sign_location: payload.sign_location, ip_address: payload.ip_address, client: payload.client, date: new Date().getTime(), created_at: new Date().getTime(), }) await session.save() return jwt_token } static async createRefreshToken(user_id, authToken) { const jwt_token = await this.signToken( { user_id, }, "refreshStrategy", ) const refreshRegistry = new RefreshToken({ authToken: authToken, refreshToken: jwt_token, }) await refreshRegistry.save() return jwt_token } static async basicDecode(token) { const { secret } = Token.authStrategy return new Promise((resolve, reject) => { jwt.verify(token, secret, async (err, decoded) => { if (err) { reject(err) } resolve(decoded) }) }) } 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.authStrategy 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 // check account tos violation const violation = await TosViolations.findOne({ user_id: decoded.user_id, }) if (violation) { console.log("violation", violation) result.valid = false result.banned = { reason: violation.reason, expire_at: violation.expire_at, } return result } 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 handleRefreshToken(payload) { const { authToken, refreshToken } = payload if (!authToken || !refreshToken) { throw new OperationError(400, "Missing refreshToken or authToken") } let result = { error: undefined, token: undefined, refreshToken: undefined, } await jwt.verify( refreshToken, Token.refreshStrategy.secret, async (err, decoded) => { if (err) { result.error = err.message return false } if (!decoded.user_id) { result.error = "Missing user_id" return false } let currentSession = await Session.findOne({ user_id: decoded.user_id, token: authToken, }).catch((err) => { return null }) if (!currentSession) { result.error = "Session not matching with provided token" return false } currentSession = currentSession.toObject() await Session.findOneAndDelete({ _id: currentSession._id.toString(), }) result.token = await this.createAuthToken({ ...currentSession, date: new Date().getTime(), }) result.refreshToken = await this.createRefreshToken( decoded.user_id, result.token, ) return true }, ) if (result.error) { throw new OperationError(401, result.error) } return result } }