|
@ -14,16 +14,29 @@ const uuidv4 = require('uuid').v4; |
|
|
const striptags = require('striptags'); |
|
|
const striptags = require('striptags'); |
|
|
|
|
|
|
|
|
const oauth2orize = require('oauth2orize'); |
|
|
const oauth2orize = require('oauth2orize'); |
|
|
const passport = require('passport'); |
|
|
|
|
|
|
|
|
|
|
|
const generatePassword = require('password-generator'); |
|
|
const generatePassword = require('password-generator'); |
|
|
|
|
|
|
|
|
|
|
|
const passport = require('passport'); |
|
|
|
|
|
const BasicStrategy = require('passport-http').BasicStrategy; |
|
|
|
|
|
const ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy; |
|
|
|
|
|
const BearerStrategy = require('passport-http-bearer').Strategy; |
|
|
|
|
|
|
|
|
const { SiteService/*, SiteError*/ } = require('../../lib/site-lib'); |
|
|
const { SiteService/*, SiteError*/ } = require('../../lib/site-lib'); |
|
|
|
|
|
|
|
|
class OAuth2Service extends SiteService { |
|
|
class OAuth2Service extends SiteService { |
|
|
|
|
|
|
|
|
constructor (dtp) { |
|
|
constructor (dtp) { |
|
|
super(dtp, module.exports); |
|
|
super(dtp, module.exports); |
|
|
|
|
|
|
|
|
|
|
|
this.populateOAuth2AccessToken = [ |
|
|
|
|
|
{ |
|
|
|
|
|
path: 'user', |
|
|
|
|
|
select: 'username username_lc displayName picture', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
path: 'client' |
|
|
|
|
|
}, |
|
|
|
|
|
]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async start ( ) { |
|
|
async start ( ) { |
|
@ -41,6 +54,23 @@ class OAuth2Service extends SiteService { |
|
|
this.server.deserializeClient(this.deserializeClient.bind(this)); |
|
|
this.server.deserializeClient(this.deserializeClient.bind(this)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
registerPassport ( ) { |
|
|
|
|
|
const verifyClient = this.verifyClient.bind(this); |
|
|
|
|
|
const verifyHttpBearer = this.verifyHttpBearer.bind(this); |
|
|
|
|
|
|
|
|
|
|
|
const basicStrategy = new BasicStrategy(verifyClient); |
|
|
|
|
|
this.log.info('registering Basic strategy', { basicStrategy }); |
|
|
|
|
|
passport.use(basicStrategy); |
|
|
|
|
|
|
|
|
|
|
|
const clientPasswordStrategy = new ClientPasswordStrategy(verifyClient); |
|
|
|
|
|
this.log.info('registering ClientPassword strategy', { clientPasswordStrategy }); |
|
|
|
|
|
passport.use(clientPasswordStrategy); |
|
|
|
|
|
|
|
|
|
|
|
const httpBearerStrategy = new BearerStrategy(verifyHttpBearer); |
|
|
|
|
|
this.log.info('registering Bearer strategy', { httpBearerStrategy }); |
|
|
|
|
|
passport.use(httpBearerStrategy); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
async serializeClient (client, done) { |
|
|
async serializeClient (client, done) { |
|
|
this.log.debug('serializeClient', { client }); |
|
|
this.log.debug('serializeClient', { client }); |
|
|
return done(null, client.id); |
|
|
return done(null, client.id); |
|
@ -148,7 +178,7 @@ class OAuth2Service extends SiteService { |
|
|
var at = new OAuth2AccessToken({ |
|
|
var at = new OAuth2AccessToken({ |
|
|
token, |
|
|
token, |
|
|
user: ac.userId, |
|
|
user: ac.userId, |
|
|
clientId: ac.clientId, |
|
|
client: ac.clientId, |
|
|
scope: ac.scope, |
|
|
scope: ac.scope, |
|
|
}); |
|
|
}); |
|
|
await at.save(); |
|
|
await at.save(); |
|
@ -243,6 +273,35 @@ class OAuth2Service extends SiteService { |
|
|
.lean(); |
|
|
.lean(); |
|
|
return client; |
|
|
return client; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async verifyClient(clientId, clientSecret, done) { |
|
|
|
|
|
const client = await this.getClientById(clientId); |
|
|
|
|
|
if (!client) { |
|
|
|
|
|
this.log.alert('OAuth2 request from unknown client', { clientId }); |
|
|
|
|
|
return done(null, false); |
|
|
|
|
|
} |
|
|
|
|
|
if (client.clientSecret !== clientSecret) { |
|
|
|
|
|
this.log.alert('OAuth2 client secret mismatch', { clientId }); |
|
|
|
|
|
return done(null, false); |
|
|
|
|
|
} |
|
|
|
|
|
return done(null, client); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async getAccessToken (accessToken) { |
|
|
|
|
|
const token = await OAuth2AccessToken |
|
|
|
|
|
.findOne({ token: accessToken }) |
|
|
|
|
|
.populate(this.populateOAuth2AccessToken) |
|
|
|
|
|
.lean(); |
|
|
|
|
|
return token; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async verifyHttpBearer (accessToken, done) { |
|
|
|
|
|
const token = await this.getAccessToken(accessToken); |
|
|
|
|
|
if (!token) { |
|
|
|
|
|
return done(null, false); |
|
|
|
|
|
} |
|
|
|
|
|
return done(null, token.user, { scope: token.scope }); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
module.exports = { |
|
|
module.exports = { |
|
|