Skip to content

Instantly share code, notes, and snippets.

@ishan123456789
Created June 17, 2020 14:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ishan123456789/2fba00d48c0d7d900d03d97d35c90c4e to your computer and use it in GitHub Desktop.
Save ishan123456789/2fba00d48c0d7d900d03d97d35c90c4e to your computer and use it in GitHub Desktop.
Fix for strapi to allow multiple providers
// /extensions/users-permissions
"use strict";
/**
* Module dependencies.
*/
// Public node modules.
const _ = require("lodash");
const request = require("request");
// Purest strategies.
const purest = require("purest")({ request });
const purestConfig = require("@purest/providers");
/**
* 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;
console.log("heelllllllooooooooooooooorererewrwrwer");
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([null, err]);
}
// We need at least the mail.
if (!profile.email) {
return reject([null, { message: "Email was not available." }]);
}
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 = users[0];
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,
confirmed: true,
});
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 = 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 = purest({
provider: "facebook",
config: purestConfig,
});
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 google = purest({ provider: "google", config: purestConfig });
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 = purest({
provider: "github",
config: purestConfig,
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, userbody) => {
if (err) {
return callback(err);
}
// This is the public email on the github profile
if (userbody.email) {
return callback(null, {
username: userbody.login,
email: userbody.email,
});
}
// Get the email with Github's user/emails API
github
.query()
.get("user/emails")
.auth(body.split("&")[0].split("=")[1])
.request((err, res, emailsbody) => {
if (err) {
return callback(err);
}
return callback(null, {
username: userbody.login,
email: Array.isArray(emailsbody)
? emailsbody.find((email) => email.primary === true).email
: null,
});
});
});
}
);
break;
}
case "microsoft": {
const microsoft = purest({
provider: "microsoft",
config: purestConfig,
});
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 = purest({
provider: "twitter",
config: purestConfig,
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;
}
case "instagram": {
const instagram = purest({
config: purestConfig,
provider: "instagram",
key: grant.instagram.key,
secret: grant.instagram.secret,
});
instagram
.query()
.get("users/self")
.qs({ access_token })
.request((err, res, body) => {
if (err) {
callback(err);
} else {
callback(null, {
username: body.data.username,
email: `${body.data.username}@strapi.io`, // dummy email as Instagram does not provide user email
});
}
});
break;
}
case "vk": {
const vk = purest({
provider: "vk",
config: purestConfig,
});
vk.query()
.get("users.get")
.auth(access_token)
.qs({ id: query.raw.user_id, v: "5.013" })
.request((err, res, body) => {
if (err) {
callback(err);
} else {
callback(null, {
username: `${body.response[0].last_name} ${body.response[0].first_name}`,
email: query.raw.email,
});
}
});
break;
}
default:
callback({
message: "Unknown provider.",
});
break;
}
};
@var-bin
Copy link

var-bin commented Apr 5, 2021

Hi @ishan123456789,

Thank you for your Gist. Currently, I am dealing with the same issue that you were faced in Allow users to use multiple auth providers #2468. Could you tell me please, is there any additional step in order to have multiple multiple auth providers?

Thanks,
Vitalii

@ishan123456789
Copy link
Author

Hi @var-bin
you'll have to copy from /node_modules/strapi-plugin-users-permissions/services/Providers.js to /extensions/users-permissions/services/Providers.js and update the lines with commented code https://gist.github.com/ishan123456789/2fba00d48c0d7d900d03d97d35c90c4e#file-providers-js-L76

@var-bin
Copy link

var-bin commented Apr 5, 2021

Thanks a bunch for such a quick response. I am going to try it right away. Cheers

@harshitsan
Copy link

I have doubt will this implementation make one account only and attach the providers into the user document? or different users for each provider?

@ishan123456789
Copy link
Author

Right this would make one account per email

@yuriyglushakov
Copy link

Thank you for the solution! Should it work for strapi -v4?

@bayramorhan
Copy link

any updates on this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment