river-of-ebooks/api/helpers/passport.js

182 lines
5.2 KiB
JavaScript

// api/helpers/passport.js
// from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js
const url = require('url')
const { generateToken } = require('../util')
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) {
return exits.success(new PassportHelper())
}
}
const passport = require('passport')
passport.serializeUser(function (user, next) {
next(null, user.id)
})
passport.deserializeUser(function (id, next) {
return User.findOne({ id: id })
.then(function (user) {
next(null, user)
return user
}).catch(next)
})
function PassportHelper () {
this.protocols = sails.config.protocols
this.loadStrategies = function () {
const strategies = sails.config.passport
for (const key in strategies) {
let options = { passReqToCallback: true }
let Strategy = strategies[key].strategy
if (key === 'local') {
_.extend(options, {
usernameField: 'identifier'
})
passport.use(new Strategy(options, this.protocols.local.login))
} else {
const protocol = strategies[key].protocol
const callbackURL = strategies[key].callback
let baseURL = ''
if (sails.config.custom.baseURL && sails.config.custom.baseURL !== null) {
baseURL = sails.config.custom.baseURL
} else {
sails.log.warn('Please add \'appUrl\' to configuration')
baseURL = sails.getBaseurl()
}
switch (protocol) {
case 'oauth2':
options.callbackURL = url.resolve(baseURL, callbackURL)
break
// other protocols (openid, etc can go here)
}
_.extend(options, strategies[key].options)
passport.use(new Strategy(options, this.protocols[protocol].login))
}
}
}
this.endpoint = function (req, res) {
const strategies = sails.config.passport
const provider = req.param('provider')
if (!_.has(strategies, provider)) return res.redirect('/login')
const scopes = {
google: ['email'],
github: ['user:email']
}
passport.authenticate(provider, { scope: scopes[provider] })(req, res, req.next)
}
// a callback helper to split by req
this.callback = function (req, res, next) {
var provider = req.param('provider', 'local')
var action = req.param('action')
if (provider === 'local' && action !== undefined) {
if (action === 'register' && !req.user) {
this.protocols.local.register(req, res, next)
} else if (action === 'connect' && req.user) {
this.protocols.local.connect(req, res, next)
} else if (action === 'disconnect' && req.user) {
this.protocols.local.disconnect(req, res, next)
} else {
next(new Error('Invalid action'))
}
} else {
if (action === 'disconnect' && req.user) {
this.disconnect(req, res, next)
} else {
passport.authenticate(provider, next)(req, res, req.next)
}
}
}
this.connect = async function (req, q, profile, next) {
let userAttrs = {}
let provider = profile.provider || req.param('provider')
req.session.tokens = q.tokens
q.provider = provider
if (!provider) {
return next(new Error('No provider identified'))
}
// if the profile object from passport has an email, use it
if (profile.emails && profile.emails[0]) userAttrs.email = profile.emails[0].value
// if (!userAttrs.email) return next(new Error('No email available'))
const pass = await Passport.findOne({
provider,
identifier: q.identifier
})
let user
if (!req.user) {
if (!pass) { // new user signing up, create a new user and/or passport
if (userAttrs.email) {
user = await User.findOne({ email: userAttrs.email })
}
if (!user) {
user = await User.create({ userAttrs, signing_secret: await generateToken({ bytes: 24 }) }).fetch()
}
await Passport.create({
...q,
user: user.id
})
next(null, user)
} else { // existing user logging in
if (_.has(q, 'tokens') && q.tokens !== pass.tokens) {
pass.tokens = q.tokens
}
await Passport.update({ id: pass.id }, { tokens: pass.tokens })
user = await User.find({ id: pass.user }).limit(1)
next(null, user[0])
}
} else { // user logged in and trying to add new Passport
if (!pass) {
await Passport.create({
...q,
user: req.user.id
})
next(null, req.user)
} else { // no action, user already logged in and passport exists
next(null, user)
}
}
}
this.disconnect = async function (req, res, next) {
try {
const user = req.user
const provider = req.param('provider')
const pass = Passport.findOne({
provider,
user: user.id
})
await Passport.destroy(pass.id)
next(null, user)
return user
} catch (e) {
next(e)
}
}
this.getPassport = function () {
return passport
}
}