|
|
@ -8,7 +8,7 @@ const mongoose = require('mongoose');
|
|
|
|
|
|
|
|
|
|
|
|
const OAuth2Client = mongoose.model('OAuth2Client');
|
|
|
|
const OAuth2Client = mongoose.model('OAuth2Client');
|
|
|
|
const OAuth2AuthorizationCode = mongoose.model('OAuth2AuthorizationCode');
|
|
|
|
const OAuth2AuthorizationCode = mongoose.model('OAuth2AuthorizationCode');
|
|
|
|
const OAuth2AccessToken = mongoose.model('OAuth2AccessToken');
|
|
|
|
const OAuth2Token = mongoose.model('OAuth2Token');
|
|
|
|
|
|
|
|
|
|
|
|
const uuidv4 = require('uuid').v4;
|
|
|
|
const uuidv4 = require('uuid').v4;
|
|
|
|
const striptags = require('striptags');
|
|
|
|
const striptags = require('striptags');
|
|
|
@ -28,7 +28,7 @@ class OAuth2Service extends SiteService {
|
|
|
|
constructor (dtp) {
|
|
|
|
constructor (dtp) {
|
|
|
|
super(dtp, module.exports);
|
|
|
|
super(dtp, module.exports);
|
|
|
|
|
|
|
|
|
|
|
|
this.populateOAuth2AccessToken = [
|
|
|
|
this.populateOAuth2Token = [
|
|
|
|
{
|
|
|
|
{
|
|
|
|
path: 'user',
|
|
|
|
path: 'user',
|
|
|
|
select: 'username username_lc displayName picture',
|
|
|
|
select: 'username username_lc displayName picture',
|
|
|
@ -47,7 +47,7 @@ class OAuth2Service extends SiteService {
|
|
|
|
|
|
|
|
|
|
|
|
this.log.info('registering OAuth2 action handlers');
|
|
|
|
this.log.info('registering OAuth2 action handlers');
|
|
|
|
this.server.grant(oauth2orize.grant.code(this.processGrant.bind(this)));
|
|
|
|
this.server.grant(oauth2orize.grant.code(this.processGrant.bind(this)));
|
|
|
|
this.server.exchange(oauth2orize.exchange.code(this.processExchange.bind(this)));
|
|
|
|
this.server.exchange(oauth2orize.exchange.code(this.processExchangeCode.bind(this)));
|
|
|
|
|
|
|
|
|
|
|
|
this.log.info('registering OAuth2 serialization routines');
|
|
|
|
this.log.info('registering OAuth2 serialization routines');
|
|
|
|
this.server.serializeClient(this.serializeClient.bind(this));
|
|
|
|
this.server.serializeClient(this.serializeClient.bind(this));
|
|
|
@ -168,7 +168,40 @@ class OAuth2Service extends SiteService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async processExchange (client, code, redirectUri, done) {
|
|
|
|
async issueTokens (authCode) {
|
|
|
|
|
|
|
|
const response = {
|
|
|
|
|
|
|
|
params: {
|
|
|
|
|
|
|
|
coreUserId: authCode.user._id,
|
|
|
|
|
|
|
|
username: authCode.user.username,
|
|
|
|
|
|
|
|
username_lc: authCode.user.username_lc,
|
|
|
|
|
|
|
|
displayName: authCode.user.displayName,
|
|
|
|
|
|
|
|
bio: authCode.user.bio,
|
|
|
|
|
|
|
|
permissions: authCode.user.permissions,
|
|
|
|
|
|
|
|
flags: authCode.user.flags,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
accessToken: generatePassword(256, false),
|
|
|
|
|
|
|
|
refreshToken: generatePassword(256, false),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
await Promise.all([
|
|
|
|
|
|
|
|
OAuth2Token.create({
|
|
|
|
|
|
|
|
type: 'access',
|
|
|
|
|
|
|
|
token: response.accessToken,
|
|
|
|
|
|
|
|
user: authCode.user._id,
|
|
|
|
|
|
|
|
client: authCode.client._id,
|
|
|
|
|
|
|
|
scope: authCode.scope,
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
OAuth2Token.create({
|
|
|
|
|
|
|
|
type: 'refresh',
|
|
|
|
|
|
|
|
token: response.refreshToken,
|
|
|
|
|
|
|
|
user: authCode.user._id,
|
|
|
|
|
|
|
|
client: authCode.client._id,
|
|
|
|
|
|
|
|
scope: authCode.scope,
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async processExchangeCode (client, code, redirectUri, done) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const ac = await OAuth2AuthorizationCode
|
|
|
|
const ac = await OAuth2AuthorizationCode
|
|
|
|
.findOne({ code })
|
|
|
|
.findOne({ code })
|
|
|
@ -178,7 +211,7 @@ class OAuth2Service extends SiteService {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
path: 'user',
|
|
|
|
path: 'user',
|
|
|
|
select: 'username username_lc displayName picture',
|
|
|
|
select: 'username username_lc displayName picture bio permissions flags',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
@ -190,18 +223,10 @@ class OAuth2Service extends SiteService {
|
|
|
|
this.log.alert('OAuth2 redirect mismatch', { provided: redirectUri, onfile: ac.redirectUri });
|
|
|
|
this.log.alert('OAuth2 redirect mismatch', { provided: redirectUri, onfile: ac.redirectUri });
|
|
|
|
return done(null, false);
|
|
|
|
return done(null, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var token = uuidv4();
|
|
|
|
|
|
|
|
var at = new OAuth2AccessToken({
|
|
|
|
|
|
|
|
token,
|
|
|
|
|
|
|
|
user: ac.user._id,
|
|
|
|
|
|
|
|
client: ac.client._id,
|
|
|
|
|
|
|
|
scope: ac.scope,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
await at.save();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const response = await this.issueTokens(ac);
|
|
|
|
this.log.info('OAuth2 grant exchanged for token', { clientID: client._id });
|
|
|
|
this.log.info('OAuth2 grant exchanged for token', { clientID: client._id });
|
|
|
|
return done(null, token);
|
|
|
|
return done(null, response.accessToken, response.refreshToken, response.params);
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
this.log.error('failed to process OAuth2 exchange', { error });
|
|
|
|
this.log.error('failed to process OAuth2 exchange', { error });
|
|
|
|
return done(error);
|
|
|
|
return done(error);
|
|
|
@ -306,9 +331,9 @@ class OAuth2Service extends SiteService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async getAccessToken (accessToken) {
|
|
|
|
async getAccessToken (accessToken) {
|
|
|
|
const token = await OAuth2AccessToken
|
|
|
|
const token = await OAuth2Token
|
|
|
|
.findOne({ token: accessToken })
|
|
|
|
.findOne({ type: 'access', token: accessToken })
|
|
|
|
.populate(this.populateOAuth2AccessToken)
|
|
|
|
.populate(this.populateOAuth2Token)
|
|
|
|
.lean();
|
|
|
|
.lean();
|
|
|
|
return token;
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|