'use strict'; /** * Module dependencies. */ // Public node modules. const _ = require('lodash'); const request = require('request'); // Purest strategies.< const Purest = require('purest'); /** * Connect thanks to a third-party provider. * * * @param {String} provider * @param {String} access_token * * @return {*} */ exports.connect = (provider, query) => { const access_token = query.access_token || query.code || query.oauth_token; return new Promise((resolve, reject) => { if (!access_token) { return reject(null, { message: 'No access_token.', }); } // Get the profile. getProfile(provider, query, async (err, profile) => { if (err) { return reject(err); } // We need at least the mail. if (!profile.email) { return reject([ { message: 'Email was not available.', }, null, ]); } try { const users = await strapi.query('user', 'users-permissions').find({ email: profile.email, }); const advanced = await strapi .store({ environment: '', type: 'plugin', name: 'users-permissions', key: 'advanced', }) .get(); if ( _.isEmpty(_.find(users, { provider })) && !advanced.allow_register ) { return resolve([ null, [{ messages: [{ id: 'Auth.advanced.allow_register' }] }], 'Register action is actualy not available.', ]); } const user = _.find(users, { provider }); if (!_.isEmpty(user)) { return resolve([user, null]); } if ( !_.isEmpty(_.find(users, user => user.provider !== provider)) && advanced.unique_email ) { return resolve([ null, [{ messages: [{ id: 'Auth.form.error.email.taken' }] }], 'Email is already taken.', ]); } // Retrieve default role. const defaultRole = await strapi .query('role', 'users-permissions') .findOne({ type: advanced.default_role }, []); // Create the new user. const params = _.assign(profile, { provider: provider, role: defaultRole._id || defaultRole.id, }); const createdUser = await strapi .query('user', 'users-permissions') .create(params); return resolve([createdUser, null]); } catch (err) { reject([null, err]); } }); }); }; /** * Helper to get profiles * * @param {String} provider * @param {Function} callback */ const getProfile = async (provider, query, callback) => { const access_token = query.access_token || query.code || query.oauth_token; const grant = await strapi .store({ environment: '', type: 'plugin', name: 'users-permissions', key: 'grant', }) .get(); switch (provider) { case 'discord': { const discord = new Purest({ provider: 'discord', config: { discord: { 'https://discordapp.com/api/': { __domain: { auth: { auth: { bearer: '[0]' }, }, }, '{endpoint}': { __path: { alias: '__default', }, }, }, }, }, }); discord .query() .get('users/@me') .auth(access_token) .request((err, res, body) => { if (err) { callback(err); } else { // Combine username and discriminator because discord username is not unique var username = `${body.username}#${body.discriminator}`; callback(null, { username: username, email: body.email, }); } }); break; } case 'facebook': { const facebook = new Purest({ provider: 'facebook', }); facebook .query() .get('me?fields=name,email') .auth(access_token) .request((err, res, body) => { if (err) { callback(err); } else { callback(null, { username: body.name, email: body.email, }); } }); break; } case 'google': { const config = { google: { 'https://www.googleapis.com': { __domain: { auth: { auth: { bearer: '[0]' }, }, }, '{endpoint}': { __path: { alias: '__default', }, }, 'oauth/[version]/{endpoint}': { __path: { alias: 'oauth', version: 'v3', }, }, }, }, }; const google = new Purest({ provider: 'google', config }); google .query('oauth') .get('tokeninfo') .qs({ access_token }) .request((err, res, body) => { if (err) { callback(err); } else { callback(null, { username: body.email.split('@')[0], email: body.email, }); } }); break; } case 'github': { const github = new Purest({ provider: 'github', defaults: { headers: { 'user-agent': 'strapi', }, }, }); request.post( { url: 'https://github.com/login/oauth/access_token', form: { client_id: grant.github.key, client_secret: grant.github.secret, code: access_token, }, }, (err, res, body) => { github .query() .get('user') .auth(body.split('&')[0].split('=')[1]) .request((err, res, body) => { if (err) { callback(err); } else { callback(null, { username: body.login, email: body.email, }); } }); } ); break; } case 'microsoft': { const microsoft = new Purest({ provider: 'microsoft', config: { microsoft: { 'https://graph.microsoft.com': { __domain: { auth: { auth: { bearer: '[0]' }, }, }, '[version]/{endpoint}': { __path: { alias: '__default', version: 'v1.0', }, }, }, }, }, }); microsoft .query() .get('me') .auth(access_token) .request((err, res, body) => { if (err) { callback(err); } else { callback(null, { username: body.userPrincipalName, email: body.userPrincipalName, }); } }); break; } case 'twitter': { const twitter = new Purest({ provider: 'twitter', key: grant.twitter.key, secret: grant.twitter.secret, }); twitter .query() .get('account/verify_credentials') .auth(access_token, query.access_secret) .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' }) .request((err, res, body) => { if (err) { callback(err); } else { callback(null, { username: body.screen_name, email: body.email, }); } }); break; } default: callback({ message: 'Unknown provider.', }); break; } };