import {isOAuth2Token, OAuth2Client, OAuth2Token, oauth2TokenString, OAuth2TokenString, TokenRepository} from '../types' import {Inject, Injectable} from '../../../di' import {ErrorWithContext, Maybe} from '../../../util' import {OAuth2TokenModel} from '../models/OAuth2TokenModel' import {Config} from '../../../service/Config' import * as jwt from 'jsonwebtoken' import {Authenticatable} from '../../types' import {make} from '../../../make' @Injectable() export class ORMTokenRepository extends TokenRepository { @Inject() protected readonly config!: Config async find(id: string): Promise> { const idNum = parseInt(id, 10) if ( !isNaN(idNum) ) { return OAuth2TokenModel.query() .whereKey(idNum) .first() } } async issue(user: Authenticatable|undefined, client: OAuth2Client, scope?: string): Promise { const expiration = this.config.safe('outh2.token.lifetimeSeconds') .or(60 * 60 * 6) .integer() * 1000 const token = make(OAuth2TokenModel) token.scope = scope token.clientId = client.id token.issued = new Date() token.expires = new Date(Math.floor(Date.now() + expiration)) if ( user ) { token.userId = String(user.getUniqueIdentifier()) } await token.save() return token } async encode(token: OAuth2Token): Promise { const secret = this.config.safe('oauth2.secret').string() const payload = { id: token.id, clientId: token.clientId, iat: Math.floor(token.issued.valueOf() / 1000), exp: Math.floor(token.expires.valueOf() / 1000), ...(token.userId ? { userId: token.userId } : {}), ...(token.scope ? { scope: token.scope } : {}), } const generated = await new Promise((res, rej) => { jwt.sign(payload, secret, {}, (err, gen) => { if (err || !gen) { rej(err || new ErrorWithContext('Unable to encode JWT.', { payload, gen, })) } else { res(gen) } }) }) return oauth2TokenString(generated) } async decode(token: OAuth2TokenString): Promise> { const secret = this.config.safe('oauth2.secret').string() const decoded = await new Promise((res, rej) => { jwt.verify(token, secret, {}, (err, payload) => { if ( err ) { rej(err) } else { res(payload) } }) }) const value = { id: decoded.id, clientId: decoded.clientId, issued: new Date(decoded.iat * 1000), expires: new Date(decoded.exp * 1000), ...(decoded.userId ? { userId: decoded.userId } : {}), ...(decoded.scope ? { scope: decoded.scope } : {}), } if ( isOAuth2Token(value) ) { return value } } }