2017-08-23 20:13:45 +00:00
|
|
|
import config from '../../../scripts/load-config'
|
2020-12-13 14:36:07 +00:00
|
|
|
import * as Models from '../models'
|
|
|
|
import { User, Login } from '../index'
|
2017-08-23 20:13:45 +00:00
|
|
|
import crypto from 'crypto'
|
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const scopes = {
|
|
|
|
email: ['See your Email address', 'View the user\'s email address'],
|
|
|
|
image: ['', 'View the user\'s profile picture'],
|
|
|
|
privilege: ['', 'See the user\'s privilege level']
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const accessToken = {
|
|
|
|
ttl: config.oauth2.access_token_life,
|
|
|
|
getToken: (object) => {
|
|
|
|
if (object) return object.token
|
|
|
|
return null
|
|
|
|
},
|
|
|
|
create: async (userId, clientId, scope, ttl) => {
|
|
|
|
const token = crypto.randomBytes(config.oauth2.token_length).toString('hex')
|
|
|
|
const expr = new Date(Date.now() + ttl * 1000)
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (typeof scope === 'object') {
|
|
|
|
scope = scope.join(' ')
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
// Delete already existing tokens with this exact user id, client id and scope, because it will
|
|
|
|
// eventually pile up and flood the database.
|
|
|
|
await Models.OAuth2AccessToken.query().delete().where('user_id', userId)
|
|
|
|
.andWhere('client_id', clientId)
|
|
|
|
|
|
|
|
const obj = {
|
|
|
|
token,
|
|
|
|
scope,
|
|
|
|
user_id: userId,
|
|
|
|
client_id: clientId,
|
|
|
|
expires_at: expr,
|
|
|
|
created_at: new Date()
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
const res = await Models.OAuth2AccessToken.query().insert(obj)
|
|
|
|
if (!res) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return res.token
|
|
|
|
},
|
|
|
|
fetchByToken: async (token) => {
|
|
|
|
if (typeof token === 'object') {
|
|
|
|
return token
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
token = await Models.OAuth2AccessToken.query().where('token', token)
|
|
|
|
if (!token.length) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return token[0]
|
2017-08-23 20:13:45 +00:00
|
|
|
},
|
2020-12-13 14:36:07 +00:00
|
|
|
checkTTL: (object) => {
|
|
|
|
return (object.expires_at > Date.now())
|
|
|
|
},
|
|
|
|
getTTL: (object) => {
|
|
|
|
return (object.expires_at - Date.now())
|
|
|
|
},
|
|
|
|
fetchByUserIdClientId: async (userId, clientId) => {
|
|
|
|
const tkn = await Models.OAuth2AccessToken.query().where('user_id', userId).andWhere('client_id', clientId)
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (!tkn.length) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return tkn[0]
|
|
|
|
}
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const client = {
|
|
|
|
getId: (c) => {
|
|
|
|
return c.id
|
|
|
|
},
|
|
|
|
fetchById: async (id) => {
|
|
|
|
const c = await Models.OAuth2Client.query().where('id', id)
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (!c.length) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return c[0]
|
|
|
|
},
|
|
|
|
checkSecret: (c, secret) => {
|
|
|
|
return c.secret === secret
|
|
|
|
},
|
|
|
|
checkGrantType: (c, grant) => {
|
|
|
|
if (c.grants.indexOf(grant) !== -1) {
|
|
|
|
return true
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return false
|
|
|
|
},
|
|
|
|
getRedirectUri: (c) => {
|
|
|
|
return c.redirect_url
|
|
|
|
},
|
|
|
|
checkRedirectUri: (c, redirectUri) => {
|
|
|
|
return (redirectUri.indexOf(client.getRedirectUri(c)) === 0 &&
|
|
|
|
redirectUri.replace(client.getRedirectUri(c), '').indexOf('#') === -1)
|
|
|
|
},
|
|
|
|
transformScope: (scope) => {
|
|
|
|
if (!scope) return []
|
|
|
|
if (typeof scope === 'object') {
|
2017-08-23 20:13:45 +00:00
|
|
|
return scope
|
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
|
|
|
|
scope = scope.trim()
|
|
|
|
if (scope.indexOf(',') !== -1) {
|
|
|
|
scope = scope.split(',')
|
|
|
|
} else {
|
|
|
|
scope = scope.split(' ')
|
|
|
|
}
|
|
|
|
|
|
|
|
return scope
|
2017-08-23 20:13:45 +00:00
|
|
|
},
|
2020-12-13 14:36:07 +00:00
|
|
|
checkScope: (c, scope) => {
|
|
|
|
if (!scope) return []
|
|
|
|
if (typeof scope === 'string') {
|
|
|
|
scope = c.transformScope(scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
const clientScopes = c.scope.split(' ')
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
for (const i in scope) {
|
|
|
|
if (clientScopes.indexOf(scope[i]) === -1) {
|
|
|
|
return false
|
2018-08-15 17:33:20 +00:00
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return scope
|
|
|
|
}
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const code = {
|
|
|
|
ttl: config.oauth2.code_life,
|
2021-02-09 16:46:04 +00:00
|
|
|
create: async (userId, clientId, scope, ttl) => {
|
2020-12-13 14:36:07 +00:00
|
|
|
const newCode = crypto.randomBytes(config.oauth2.token_length).toString('hex')
|
|
|
|
const expr = new Date(Date.now() + ttl * 1000)
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (typeof scope === 'object') {
|
|
|
|
scope = scope.join(' ')
|
|
|
|
}
|
2017-08-24 16:23:03 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
// Delete already existing codes with this exact user id, client id and scope, because it will
|
|
|
|
// eventually pile up and flood the database, especially when they were never used.
|
|
|
|
await Models.OAuth2Code.query().delete().where('user_id', userId).andWhere('client_id', clientId)
|
|
|
|
|
|
|
|
const obj = {
|
|
|
|
code: newCode,
|
|
|
|
scope,
|
|
|
|
user_id: userId,
|
|
|
|
client_id: clientId,
|
|
|
|
expires_at: expr,
|
|
|
|
created_at: new Date()
|
|
|
|
}
|
|
|
|
|
|
|
|
await Models.OAuth2Code.query().insert(obj)
|
|
|
|
|
|
|
|
return obj.code
|
|
|
|
},
|
|
|
|
fetchByCode: async (c) => {
|
|
|
|
c = await Models.OAuth2Code.query().where('code', c)
|
|
|
|
|
|
|
|
if (!c.length) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return c[0]
|
|
|
|
},
|
|
|
|
removeByCode: async (c) => {
|
|
|
|
if (typeof c === 'object') {
|
|
|
|
c = c.code
|
2017-08-23 20:13:45 +00:00
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
|
|
|
|
return Models.OAuth2Code.query().delete().where('code', c)
|
|
|
|
},
|
|
|
|
getUserId: (c) => {
|
|
|
|
return c.user_id
|
|
|
|
},
|
|
|
|
getClientId: (c) => {
|
|
|
|
return c.client_id
|
2017-08-23 20:13:45 +00:00
|
|
|
},
|
2020-12-13 14:36:07 +00:00
|
|
|
getScope: (c) => {
|
|
|
|
return c.scope
|
|
|
|
},
|
|
|
|
checkTTL: (c) => {
|
|
|
|
return (c.expires_at > Date.now())
|
|
|
|
}
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const refreshToken = {
|
|
|
|
create: async (userId, clientId, scope) => {
|
|
|
|
const token = crypto.randomBytes(config.oauth2.token_length).toString('hex')
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (typeof scope === 'object') {
|
|
|
|
scope = scope.join(' ')
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
const obj = {
|
|
|
|
token,
|
|
|
|
scope,
|
|
|
|
user_id: userId,
|
|
|
|
client_id: clientId,
|
|
|
|
created_at: new Date()
|
2017-08-23 20:13:45 +00:00
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
|
|
|
|
await Models.OAuth2RefreshToken.query().insert(obj)
|
|
|
|
|
|
|
|
return obj.token
|
2017-08-23 20:13:45 +00:00
|
|
|
},
|
2020-12-13 14:36:07 +00:00
|
|
|
fetchByToken: async (token) => {
|
|
|
|
token = await Models.OAuth2RefreshToken.query().where('token', token)
|
2017-08-27 12:41:44 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (!token.length) return null
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return token[0]
|
|
|
|
},
|
|
|
|
removeByUserIdClientId: async (userId, clientId) => {
|
|
|
|
return Models.OAuth2RefreshToken.query().delete().where('user_id', userId)
|
|
|
|
.andWhere('client_id', clientId)
|
|
|
|
},
|
|
|
|
removeByRefreshToken: async (token) => {
|
|
|
|
return Models.OAuth2RefreshToken.query().delete().where('token', token)
|
|
|
|
},
|
|
|
|
getUserId: (t) => {
|
|
|
|
return t.user_id
|
|
|
|
},
|
|
|
|
getClientId: (t) => {
|
|
|
|
return t.client_id
|
|
|
|
},
|
|
|
|
getScope: (t) => {
|
|
|
|
return t.scope
|
|
|
|
}
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export const user = {
|
|
|
|
getId: (u) => {
|
|
|
|
return u.id
|
|
|
|
},
|
|
|
|
fetchById: User.get,
|
|
|
|
fetchByUsername: User.get,
|
|
|
|
checkPassword: Login.password,
|
|
|
|
fetchFromRequest: async (req) => {
|
|
|
|
if (!req.session.user) return null
|
|
|
|
const banStatus = await User.getBanStatus(req.session.user.id)
|
|
|
|
|
|
|
|
if (banStatus.length) {
|
|
|
|
delete req.session.user
|
|
|
|
return null
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return req.session.user
|
|
|
|
},
|
|
|
|
consented: async (userId, clientId, scope) => {
|
|
|
|
if (typeof scope === 'object') {
|
|
|
|
scope = scope.join(' ')
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
const authorized = await Models.OAuth2AuthorizedClient.query().where('user_id', userId)
|
|
|
|
if (!authorized.length) return false
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
let correct = false
|
|
|
|
for (const i in authorized) {
|
|
|
|
if (authorized[i].client_id === clientId) {
|
|
|
|
correct = authorized[i]
|
2017-08-23 20:13:45 +00:00
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
if (correct) {
|
|
|
|
if (correct.scope !== scope) {
|
|
|
|
await Models.OAuth2AuthorizedClient.query().delete().where('user_id', userId)
|
|
|
|
.andWhere('client_id', correct.client_id)
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return false
|
2018-08-15 17:33:20 +00:00
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
correct = true
|
|
|
|
}
|
2017-08-23 20:13:45 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
return correct
|
|
|
|
},
|
|
|
|
consent: async (userId, clientId, scope) => {
|
|
|
|
if (!config.oauth2.save_decision) return true
|
|
|
|
if (typeof scope === 'object') {
|
|
|
|
scope = scope.join(' ')
|
2017-08-23 20:13:45 +00:00
|
|
|
}
|
2020-12-13 14:36:07 +00:00
|
|
|
|
|
|
|
const obj = {
|
|
|
|
scope,
|
|
|
|
user_id: userId,
|
|
|
|
client_id: clientId,
|
|
|
|
created_at: new Date()
|
|
|
|
}
|
|
|
|
|
|
|
|
await Models.OAuth2AuthorizedClient.query().insert(obj)
|
|
|
|
|
|
|
|
return true
|
2017-08-23 20:13:45 +00:00
|
|
|
}
|
|
|
|
}
|