95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
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<Maybe<OAuth2Token>> {
|
|
const idNum = parseInt(id, 10)
|
|
if ( !isNaN(idNum) ) {
|
|
return OAuth2TokenModel.query<OAuth2TokenModel>()
|
|
.whereKey(idNum)
|
|
.first()
|
|
}
|
|
}
|
|
|
|
async issue(user: Authenticatable|undefined, client: OAuth2Client, scope?: string): Promise<OAuth2Token> {
|
|
const expiration = this.config.safe('outh2.token.lifetimeSeconds')
|
|
.or(60 * 60 * 6)
|
|
.integer() * 1000
|
|
|
|
const token = make<OAuth2TokenModel>(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<OAuth2TokenString> {
|
|
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<string>((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<Maybe<OAuth2Token>> {
|
|
const secret = this.config.safe('oauth2.secret').string()
|
|
const decoded = await new Promise<any>((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
|
|
}
|
|
}
|
|
}
|