2018-11-02 00:21:36 +00:00
|
|
|
// api/helpers/passport.js
|
|
|
|
// from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js
|
2018-11-07 20:06:36 +00:00
|
|
|
const url = require('url')
|
2019-03-05 23:55:31 +00:00
|
|
|
const { generateToken } = require('../util')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
friendlyName: 'Load PassportHelper',
|
|
|
|
description: 'Load a PassportHelper instance',
|
|
|
|
inputs: {},
|
|
|
|
exits: {
|
|
|
|
success: {
|
|
|
|
outputFriendlyName: 'Passport helper',
|
|
|
|
outputDescription: 'A PassportHelper instance'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
fn: async function (inputs, exits) {
|
2018-11-07 20:06:36 +00:00
|
|
|
return exits.success(new PassportHelper())
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-07 20:06:36 +00:00
|
|
|
const passport = require('passport')
|
2018-11-12 22:12:16 +00:00
|
|
|
passport.serializeUser(function (user, next) {
|
2018-11-07 20:06:36 +00:00
|
|
|
next(null, user.id)
|
|
|
|
})
|
2018-11-12 22:12:16 +00:00
|
|
|
passport.deserializeUser(function (id, next) {
|
2018-11-20 00:22:41 +00:00
|
|
|
return User.findOne({ id: id })
|
2018-11-12 22:12:16 +00:00
|
|
|
.then(function (user) {
|
2018-11-07 20:06:36 +00:00
|
|
|
next(null, user)
|
|
|
|
return user
|
|
|
|
}).catch(next)
|
|
|
|
})
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
function PassportHelper () {
|
2018-11-07 20:06:36 +00:00
|
|
|
this.protocols = sails.config.protocols
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
this.loadStrategies = function () {
|
2018-11-07 20:06:36 +00:00
|
|
|
const strategies = sails.config.passport
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
for (const key in strategies) {
|
2018-11-20 00:22:41 +00:00
|
|
|
let options = { passReqToCallback: true }
|
2018-11-07 20:06:36 +00:00
|
|
|
let Strategy = strategies[key].strategy
|
2018-11-02 00:21:36 +00:00
|
|
|
if (key === 'local') {
|
|
|
|
_.extend(options, {
|
|
|
|
usernameField: 'identifier'
|
2018-11-07 20:06:36 +00:00
|
|
|
})
|
|
|
|
passport.use(new Strategy(options, this.protocols.local.login))
|
2018-11-02 00:21:36 +00:00
|
|
|
} else {
|
2018-11-07 20:06:36 +00:00
|
|
|
const protocol = strategies[key].protocol
|
|
|
|
const callbackURL = strategies[key].callback
|
|
|
|
let baseURL = ''
|
2019-01-31 21:52:18 +00:00
|
|
|
if (sails.config.custom.baseURL && sails.config.custom.baseURL !== null) {
|
|
|
|
baseURL = sails.config.custom.baseURL
|
2018-11-02 00:21:36 +00:00
|
|
|
} else {
|
2018-11-07 20:06:36 +00:00
|
|
|
sails.log.warn('Please add \'appUrl\' to configuration')
|
|
|
|
baseURL = sails.getBaseurl()
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (protocol) {
|
|
|
|
case 'oauth2':
|
2018-11-07 20:06:36 +00:00
|
|
|
options.callbackURL = url.resolve(baseURL, callbackURL)
|
|
|
|
break
|
2018-11-02 00:21:36 +00:00
|
|
|
// other protocols (openid, etc can go here)
|
|
|
|
}
|
|
|
|
|
2018-11-07 20:06:36 +00:00
|
|
|
_.extend(options, strategies[key].options)
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-07 20:06:36 +00:00
|
|
|
passport.use(new Strategy(options, this.protocols[protocol].login))
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
this.endpoint = function (req, res) {
|
2018-11-07 20:06:36 +00:00
|
|
|
const strategies = sails.config.passport
|
|
|
|
const provider = req.param('provider')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-12 22:12:16 +00:00
|
|
|
if (!_.has(strategies, provider)) return res.redirect('/login')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2019-01-31 21:52:18 +00:00
|
|
|
const scopes = {
|
|
|
|
google: ['email'],
|
|
|
|
github: ['user:email']
|
|
|
|
}
|
|
|
|
|
|
|
|
passport.authenticate(provider, { scope: scopes[provider] })(req, res, req.next)
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
// a callback helper to split by req
|
|
|
|
this.callback = function (req, res, next) {
|
2018-11-07 20:06:36 +00:00
|
|
|
var provider = req.param('provider', 'local')
|
|
|
|
var action = req.param('action')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
if (provider === 'local' && action !== undefined) {
|
|
|
|
if (action === 'register' && !req.user) {
|
2018-11-07 20:06:36 +00:00
|
|
|
this.protocols.local.register(req, res, next)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else if (action === 'connect' && req.user) {
|
2018-11-07 20:06:36 +00:00
|
|
|
this.protocols.local.connect(req, res, next)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else if (action === 'disconnect' && req.user) {
|
2018-11-07 20:06:36 +00:00
|
|
|
this.protocols.local.disconnect(req, res, next)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else {
|
2018-11-12 22:12:16 +00:00
|
|
|
next(new Error('Invalid action'))
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (action === 'disconnect' && req.user) {
|
2018-11-07 20:06:36 +00:00
|
|
|
this.disconnect(req, res, next)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else {
|
2018-11-07 20:06:36 +00:00
|
|
|
passport.authenticate(provider, next)(req, res, req.next)
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
this.connect = async function (req, q, profile, next) {
|
2018-11-07 20:06:36 +00:00
|
|
|
let userAttrs = {}
|
|
|
|
let provider = profile.provider || req.param('provider')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-07 20:06:36 +00:00
|
|
|
req.session.tokens = q.tokens
|
|
|
|
q.provider = provider
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
if (!provider) {
|
2018-11-07 20:06:36 +00:00
|
|
|
return next(new Error('No provider identified'))
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if the profile object from passport has an email, use it
|
2018-11-12 22:12:16 +00:00
|
|
|
if (profile.emails && profile.emails[0]) userAttrs.email = profile.emails[0].value
|
2019-02-04 21:59:13 +00:00
|
|
|
// if (!userAttrs.email) return next(new Error('No email available'))
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-12 22:12:16 +00:00
|
|
|
const pass = await Passport.findOne({
|
2018-11-02 00:21:36 +00:00
|
|
|
provider,
|
2019-01-31 21:52:18 +00:00
|
|
|
identifier: q.identifier
|
2018-11-07 20:06:36 +00:00
|
|
|
})
|
2018-11-02 00:21:36 +00:00
|
|
|
|
2018-11-07 20:06:36 +00:00
|
|
|
let user
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
if (!req.user) {
|
2019-01-31 21:52:18 +00:00
|
|
|
if (!pass) { // new user signing up, create a new user and/or passport
|
2019-02-04 21:59:13 +00:00
|
|
|
if (userAttrs.email) {
|
|
|
|
user = await User.findOne({ email: userAttrs.email })
|
|
|
|
}
|
2019-01-31 21:52:18 +00:00
|
|
|
if (!user) {
|
2019-03-06 00:56:55 +00:00
|
|
|
user = await User.create({ userAttrs, signing_secret: await generateToken({ bytes: 24 }) }).fetch()
|
2019-01-31 21:52:18 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
await Passport.create({
|
|
|
|
...q,
|
|
|
|
user: user.id
|
2018-11-07 20:06:36 +00:00
|
|
|
})
|
2018-11-12 22:12:16 +00:00
|
|
|
next(null, user)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else { // existing user logging in
|
2019-01-31 21:52:18 +00:00
|
|
|
if (_.has(q, 'tokens') && q.tokens !== pass.tokens) {
|
|
|
|
pass.tokens = q.tokens
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
2019-01-31 21:52:18 +00:00
|
|
|
await Passport.update({ id: pass.id }, { tokens: pass.tokens })
|
2019-03-25 18:08:30 +00:00
|
|
|
user = await User.find({ id: pass.user }).limit(1)
|
2019-01-31 21:52:18 +00:00
|
|
|
next(null, user[0])
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
} else { // user logged in and trying to add new Passport
|
2019-01-31 21:52:18 +00:00
|
|
|
if (!pass) {
|
2018-11-02 00:21:36 +00:00
|
|
|
await Passport.create({
|
|
|
|
...q,
|
|
|
|
user: req.user.id
|
2018-11-07 20:06:36 +00:00
|
|
|
})
|
2018-11-12 22:12:16 +00:00
|
|
|
next(null, req.user)
|
2018-11-02 00:21:36 +00:00
|
|
|
} else { // no action, user already logged in and passport exists
|
2018-11-12 22:12:16 +00:00
|
|
|
next(null, user)
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
this.disconnect = async function (req, res, next) {
|
|
|
|
try {
|
2018-11-07 20:06:36 +00:00
|
|
|
const user = req.user
|
|
|
|
const provider = req.param('provider')
|
2018-11-02 00:21:36 +00:00
|
|
|
|
|
|
|
const pass = Passport.findOne({
|
|
|
|
provider,
|
|
|
|
user: user.id
|
2018-11-07 20:06:36 +00:00
|
|
|
})
|
|
|
|
await Passport.destroy(pass.id)
|
|
|
|
next(null, user)
|
|
|
|
return user
|
2018-11-02 00:21:36 +00:00
|
|
|
} catch (e) {
|
2018-11-12 22:12:16 +00:00
|
|
|
next(e)
|
2018-11-02 00:21:36 +00:00
|
|
|
}
|
2018-11-07 20:06:36 +00:00
|
|
|
}
|
2018-11-02 00:21:36 +00:00
|
|
|
this.getPassport = function () {
|
2018-11-07 20:06:36 +00:00
|
|
|
return passport
|
|
|
|
}
|
|
|
|
}
|