const Model = require('flitter-orm/src/model/Model') const { ObjectId } = require('mongodb') const User = require('../auth/User.model') const ValidScope = require('../scopes/Valid.scope') const uuid = require('uuid/v4') const jwt = require('jsonwebtoken') /* * Token Model * ------------------------------------------------------------- * Put some description here! */ class Token extends Model { static #default_grants = ['database'] static get services() { return [...super.services, 'configs'] } static get schema() { // Return a flitter-orm schema here. return { user_id: ObjectId, token: String, issued: { type: Date, default: () => new Date }, expires: { type: Date, default: () => { const d = new Date d.setDate(d.getDate() + 7) return d }, }, valid: { type: Boolean, default: true }, grants: [String], } } static scopes = [new ValidScope] static async create(user) { const token = new this({ user_id: user._id, grants: this.#default_grants }) await token.save() token.token = token.generate() return await token.save() } static async for_user(user) { const token = await this.findOne({ user_id: user._id }) return token ? token : await this.create(user) } static verify(token) { const secret = this.prototype.configs.get('server.session.secret') const server = this.prototype.configs.get('app.url') return new Promise((res, rej) => { jwt.verify(token, secret, (err, decoded) => { if ( err ) rej(err) else if ( decoded.iss !== server ) rej(new Error('Invalid token issuer: '+decoded.iss)) else { this.findOne({ user_id: ObjectId(decoded.sub) }).then(result => { if ( !result ) rej(new Error('Unable to find associated token. It may have been manually invalidated.')) else res(result) }) } }) }) } generate() { const secret = this.configs.get('server.session.secret') const server = this.configs.get('app.url') const payload = { sub: String(this.user_id), iss: server, permissions: this.grants.join(','), exp: (this.expires.getTime()/1000 | 0), iat: (this.issued.getTime()/1000 | 0), } return jwt.sign(payload, secret) } user() { return this.has_one(User, 'user_id', '_id') } can(grant) { return this.grants.includes(grant) } } module.exports = exports = Token