@strapi/plugin-users-permissions 4.1.7 → 4.1.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -8
- package/server/bootstrap/index.js +13 -4
- package/server/controllers/auth.js +7 -14
- package/server/services/providers-list.js +152 -348
- package/server/services/providers.js +58 -69
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.10-beta.0",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,15 +27,14 @@
|
|
|
27
27
|
"test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@
|
|
31
|
-
"@strapi/
|
|
32
|
-
"@strapi/utils": "4.1.7",
|
|
30
|
+
"@strapi/helper-plugin": "4.1.10-beta.0",
|
|
31
|
+
"@strapi/utils": "4.1.10-beta.0",
|
|
33
32
|
"bcryptjs": "2.4.3",
|
|
34
33
|
"grant-koa": "5.4.8",
|
|
35
34
|
"jsonwebtoken": "^8.1.0",
|
|
36
35
|
"koa2-ratelimit": "^0.9.0",
|
|
37
36
|
"lodash": "4.17.21",
|
|
38
|
-
"purest": "
|
|
37
|
+
"purest": "4.0.2",
|
|
39
38
|
"react": "^17.0.2",
|
|
40
39
|
"react-dom": "^17.0.2",
|
|
41
40
|
"react-intl": "5.20.2",
|
|
@@ -44,8 +43,7 @@
|
|
|
44
43
|
"react-router-dom": "5.2.0",
|
|
45
44
|
"redux-saga": "^0.16.0",
|
|
46
45
|
"request": "^2.83.0",
|
|
47
|
-
"url-join": "4.0.1"
|
|
48
|
-
"uuid": "^3.1.0"
|
|
46
|
+
"url-join": "4.0.1"
|
|
49
47
|
},
|
|
50
48
|
"devDependencies": {
|
|
51
49
|
"koa": "^2.13.1"
|
|
@@ -61,5 +59,5 @@
|
|
|
61
59
|
"required": true,
|
|
62
60
|
"kind": "plugin"
|
|
63
61
|
},
|
|
64
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "a9082d9a3b26bf0abeb8e45d4507d5329e9ae733"
|
|
65
63
|
}
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* This gives you an opportunity to set up your data model,
|
|
8
8
|
* run jobs, or perform some special logic.
|
|
9
9
|
*/
|
|
10
|
+
const crypto = require('crypto');
|
|
10
11
|
const _ = require('lodash');
|
|
11
12
|
const urljoin = require('url-join');
|
|
12
|
-
const uuid = require('uuid/v4');
|
|
13
13
|
const { getService } = require('../utils');
|
|
14
14
|
const getGrantConfig = require('./grant-config');
|
|
15
15
|
|
|
@@ -29,13 +29,22 @@ module.exports = async ({ strapi }) => {
|
|
|
29
29
|
await getService('users-permissions').initialize();
|
|
30
30
|
|
|
31
31
|
if (!strapi.config.get('plugin.users-permissions.jwtSecret')) {
|
|
32
|
-
|
|
32
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Missing jwtSecret. Please, set configuration variable "jwtSecret" for the users-permissions plugin in config/plugins.js (ex: you can generate one using Node with \`crypto.randomBytes(16).toString('base64')\`).
|
|
35
|
+
For security reasons, prefer storing the secret in an environment variable and read it in config/plugins.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const jwtSecret = crypto.randomBytes(16).toString('base64');
|
|
40
|
+
|
|
33
41
|
strapi.config.set('plugin.users-permissions.jwtSecret', jwtSecret);
|
|
34
42
|
|
|
35
43
|
if (!process.env.JWT_SECRET) {
|
|
36
|
-
|
|
44
|
+
const envPath = process.env.ENV_PATH || '.env';
|
|
45
|
+
strapi.fs.appendFile(envPath, `JWT_SECRET=${jwtSecret}\n`);
|
|
37
46
|
strapi.log.info(
|
|
38
|
-
|
|
47
|
+
`The Users & Permissions plugin automatically generated a jwt secret and stored it in ${envPath} under the name JWT_SECRET.`
|
|
39
48
|
);
|
|
40
49
|
}
|
|
41
50
|
}
|
|
@@ -34,7 +34,7 @@ module.exports = {
|
|
|
34
34
|
const provider = ctx.params.provider || 'local';
|
|
35
35
|
const params = ctx.request.body;
|
|
36
36
|
|
|
37
|
-
const store =
|
|
37
|
+
const store = strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
38
38
|
|
|
39
39
|
if (provider === 'local') {
|
|
40
40
|
if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
|
|
@@ -101,22 +101,15 @@ module.exports = {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// Connect the user with the third-party provider.
|
|
104
|
-
let user;
|
|
105
|
-
let error;
|
|
106
104
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
const user = await getService('providers').connect(provider, ctx.query);
|
|
106
|
+
ctx.send({
|
|
107
|
+
jwt: getService('jwt').issue({ id: user.id }),
|
|
108
|
+
user: await sanitizeUser(user, ctx),
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
113
111
|
throw new ApplicationError(error.message);
|
|
114
112
|
}
|
|
115
|
-
|
|
116
|
-
ctx.send({
|
|
117
|
-
jwt: getService('jwt').issue({ id: user.id }),
|
|
118
|
-
user: await sanitizeUser(user, ctx),
|
|
119
|
-
});
|
|
120
113
|
}
|
|
121
114
|
},
|
|
122
115
|
|
|
@@ -1,51 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require('lodash');
|
|
4
3
|
const jwt = require('jsonwebtoken');
|
|
5
|
-
const request = require('request');
|
|
6
4
|
// Purest strategies.
|
|
7
|
-
const purest = require('purest')
|
|
8
|
-
const purestConfig = require('@purest/providers');
|
|
5
|
+
const purest = require('purest');
|
|
9
6
|
|
|
10
|
-
module.exports = async ({ provider, access_token,
|
|
7
|
+
module.exports = async ({ provider, access_token, query, providers }) => {
|
|
11
8
|
switch (provider) {
|
|
12
9
|
case 'discord': {
|
|
13
|
-
const discord = purest({
|
|
14
|
-
|
|
15
|
-
config: {
|
|
16
|
-
discord: {
|
|
17
|
-
'https://discordapp.com/api/': {
|
|
18
|
-
__domain: {
|
|
19
|
-
auth: {
|
|
20
|
-
auth: { bearer: '[0]' },
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
'{endpoint}': {
|
|
24
|
-
__path: {
|
|
25
|
-
alias: '__default',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
discord
|
|
33
|
-
.query()
|
|
10
|
+
const discord = purest({ provider: 'discord' });
|
|
11
|
+
return discord
|
|
34
12
|
.get('users/@me')
|
|
35
13
|
.auth(access_token)
|
|
36
|
-
.request(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
email: body.email,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
14
|
+
.request()
|
|
15
|
+
.then(({ body }) => {
|
|
16
|
+
// Combine username and discriminator because discord username is not unique
|
|
17
|
+
var username = `${body.username}#${body.discriminator}`;
|
|
18
|
+
return {
|
|
19
|
+
username,
|
|
20
|
+
email: body.email,
|
|
21
|
+
};
|
|
47
22
|
});
|
|
48
|
-
break;
|
|
49
23
|
}
|
|
50
24
|
case 'cognito': {
|
|
51
25
|
// get the id_token
|
|
@@ -53,60 +27,43 @@ module.exports = async ({ provider, access_token, callback, query, providers })
|
|
|
53
27
|
// decode the jwt token
|
|
54
28
|
const tokenPayload = jwt.decode(idToken);
|
|
55
29
|
if (!tokenPayload) {
|
|
56
|
-
|
|
30
|
+
throw new Error('unable to decode jwt token');
|
|
57
31
|
} else {
|
|
58
|
-
|
|
32
|
+
return {
|
|
59
33
|
username: tokenPayload['cognito:username'],
|
|
60
34
|
email: tokenPayload.email,
|
|
61
|
-
}
|
|
35
|
+
};
|
|
62
36
|
}
|
|
63
|
-
break;
|
|
64
37
|
}
|
|
65
38
|
case 'facebook': {
|
|
66
|
-
const facebook = purest({
|
|
67
|
-
provider: 'facebook',
|
|
68
|
-
config: purestConfig,
|
|
69
|
-
});
|
|
39
|
+
const facebook = purest({ provider: 'facebook' });
|
|
70
40
|
|
|
71
|
-
facebook
|
|
72
|
-
.
|
|
73
|
-
.get('me?fields=name,email')
|
|
41
|
+
return facebook
|
|
42
|
+
.get('me')
|
|
74
43
|
.auth(access_token)
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
email: body.email,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
break;
|
|
44
|
+
.qs({ fields: 'name,email' })
|
|
45
|
+
.request()
|
|
46
|
+
.then(({ body }) => ({
|
|
47
|
+
username: body.name,
|
|
48
|
+
email: body.email,
|
|
49
|
+
}));
|
|
86
50
|
}
|
|
87
51
|
case 'google': {
|
|
88
|
-
const google = purest({ provider: 'google'
|
|
52
|
+
const google = purest({ provider: 'google' });
|
|
89
53
|
|
|
90
|
-
google
|
|
54
|
+
return google
|
|
91
55
|
.query('oauth')
|
|
92
56
|
.get('tokeninfo')
|
|
93
57
|
.qs({ access_token })
|
|
94
|
-
.request(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
username: body.email.split('@')[0],
|
|
100
|
-
email: body.email,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
break;
|
|
58
|
+
.request()
|
|
59
|
+
.then(({ body }) => ({
|
|
60
|
+
username: body.email.split('@')[0],
|
|
61
|
+
email: body.email,
|
|
62
|
+
}));
|
|
105
63
|
}
|
|
106
64
|
case 'github': {
|
|
107
65
|
const github = purest({
|
|
108
66
|
provider: 'github',
|
|
109
|
-
config: purestConfig,
|
|
110
67
|
defaults: {
|
|
111
68
|
headers: {
|
|
112
69
|
'user-agent': 'strapi',
|
|
@@ -114,360 +71,207 @@ module.exports = async ({ provider, access_token, callback, query, providers })
|
|
|
114
71
|
},
|
|
115
72
|
});
|
|
116
73
|
|
|
117
|
-
github
|
|
118
|
-
.query()
|
|
74
|
+
return github
|
|
119
75
|
.get('user')
|
|
120
76
|
.auth(access_token)
|
|
121
|
-
.request(
|
|
122
|
-
|
|
123
|
-
return callback(err);
|
|
124
|
-
}
|
|
125
|
-
|
|
77
|
+
.request()
|
|
78
|
+
.then(({ body: userbody }) => {
|
|
126
79
|
// This is the public email on the github profile
|
|
127
80
|
if (userbody.email) {
|
|
128
|
-
return
|
|
81
|
+
return {
|
|
129
82
|
username: userbody.login,
|
|
130
83
|
email: userbody.email,
|
|
131
|
-
}
|
|
84
|
+
};
|
|
132
85
|
}
|
|
133
|
-
|
|
134
86
|
// Get the email with Github's user/emails API
|
|
135
|
-
github
|
|
136
|
-
.query()
|
|
87
|
+
return github
|
|
137
88
|
.get('user/emails')
|
|
138
89
|
.auth(access_token)
|
|
139
|
-
.request(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return callback(null, {
|
|
90
|
+
.request()
|
|
91
|
+
.then(({ body: emailsbody }) => {
|
|
92
|
+
return {
|
|
145
93
|
username: userbody.login,
|
|
146
94
|
email: Array.isArray(emailsbody)
|
|
147
95
|
? emailsbody.find(email => email.primary === true).email
|
|
148
96
|
: null,
|
|
149
|
-
}
|
|
97
|
+
};
|
|
150
98
|
});
|
|
151
99
|
});
|
|
152
|
-
break;
|
|
153
100
|
}
|
|
154
101
|
case 'microsoft': {
|
|
155
|
-
const microsoft = purest({
|
|
156
|
-
provider: 'microsoft',
|
|
157
|
-
config: purestConfig,
|
|
158
|
-
});
|
|
102
|
+
const microsoft = purest({ provider: 'microsoft' });
|
|
159
103
|
|
|
160
|
-
microsoft
|
|
161
|
-
.query()
|
|
104
|
+
return microsoft
|
|
162
105
|
.get('me')
|
|
163
106
|
.auth(access_token)
|
|
164
|
-
.request(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
username: body.userPrincipalName,
|
|
170
|
-
email: body.userPrincipalName,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
break;
|
|
107
|
+
.request()
|
|
108
|
+
.then(({ body }) => ({
|
|
109
|
+
username: body.userPrincipalName,
|
|
110
|
+
email: body.userPrincipalName,
|
|
111
|
+
}));
|
|
175
112
|
}
|
|
176
113
|
case 'twitter': {
|
|
177
114
|
const twitter = purest({
|
|
178
115
|
provider: 'twitter',
|
|
179
|
-
config: purestConfig,
|
|
180
|
-
key: providers.twitter.key,
|
|
181
|
-
secret: providers.twitter.secret,
|
|
182
116
|
});
|
|
183
117
|
|
|
184
|
-
twitter
|
|
185
|
-
.query()
|
|
118
|
+
return twitter
|
|
186
119
|
.get('account/verify_credentials')
|
|
187
120
|
.auth(access_token, query.access_secret)
|
|
188
121
|
.qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
|
|
189
|
-
.request(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
username: body.screen_name,
|
|
195
|
-
email: body.email,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
break;
|
|
122
|
+
.request()
|
|
123
|
+
.then(({ body }) => ({
|
|
124
|
+
username: body.screen_name,
|
|
125
|
+
email: body.email,
|
|
126
|
+
}));
|
|
200
127
|
}
|
|
201
128
|
case 'instagram': {
|
|
202
|
-
const instagram = purest({
|
|
203
|
-
provider: 'instagram',
|
|
204
|
-
key: providers.instagram.key,
|
|
205
|
-
secret: providers.instagram.secret,
|
|
206
|
-
config: purestConfig,
|
|
207
|
-
});
|
|
129
|
+
const instagram = purest({ provider: 'instagram' });
|
|
208
130
|
|
|
209
|
-
instagram
|
|
210
|
-
.query()
|
|
131
|
+
return instagram
|
|
211
132
|
.get('me')
|
|
212
|
-
.
|
|
213
|
-
.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
break;
|
|
133
|
+
.auth(access_token)
|
|
134
|
+
.qs({ fields: 'id,username' })
|
|
135
|
+
.request()
|
|
136
|
+
.then(({ body }) => ({
|
|
137
|
+
username: body.username,
|
|
138
|
+
email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
|
|
139
|
+
}));
|
|
224
140
|
}
|
|
225
141
|
case 'vk': {
|
|
226
|
-
const vk = purest({
|
|
227
|
-
provider: 'vk',
|
|
228
|
-
config: purestConfig,
|
|
229
|
-
});
|
|
142
|
+
const vk = purest({ provider: 'vk' });
|
|
230
143
|
|
|
231
|
-
vk
|
|
144
|
+
return vk
|
|
232
145
|
.get('users.get')
|
|
233
|
-
.
|
|
234
|
-
.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
email: query.raw.email,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
break;
|
|
146
|
+
.auth(access_token)
|
|
147
|
+
.qs({ id: query.raw.user_id, v: '5.122' })
|
|
148
|
+
.request()
|
|
149
|
+
.then(({ body }) => ({
|
|
150
|
+
username: `${body.response[0].last_name} ${body.response[0].first_name}`,
|
|
151
|
+
email: query.raw.email,
|
|
152
|
+
}));
|
|
245
153
|
}
|
|
246
154
|
case 'twitch': {
|
|
247
155
|
const twitch = purest({
|
|
248
156
|
provider: 'twitch',
|
|
249
157
|
config: {
|
|
250
158
|
twitch: {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
'helix/{endpoint}': {
|
|
261
|
-
__path: {
|
|
262
|
-
alias: '__default',
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
'oauth2/{endpoint}': {
|
|
266
|
-
__path: {
|
|
267
|
-
alias: 'oauth',
|
|
268
|
-
},
|
|
159
|
+
default: {
|
|
160
|
+
origin: 'https://api.twitch.tv',
|
|
161
|
+
path: 'helix/{path}',
|
|
162
|
+
headers: {
|
|
163
|
+
Authorization: 'Bearer {auth}',
|
|
164
|
+
'Client-Id': '{auth}',
|
|
269
165
|
},
|
|
270
166
|
},
|
|
271
167
|
},
|
|
272
168
|
},
|
|
273
169
|
});
|
|
274
170
|
|
|
275
|
-
twitch
|
|
171
|
+
return twitch
|
|
276
172
|
.get('users')
|
|
277
173
|
.auth(access_token, providers.twitch.key)
|
|
278
|
-
.request(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
username: body.data[0].login,
|
|
284
|
-
email: body.data[0].email,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
break;
|
|
174
|
+
.request()
|
|
175
|
+
.then(({ body }) => ({
|
|
176
|
+
username: body.data[0].login,
|
|
177
|
+
email: body.data[0].email,
|
|
178
|
+
}));
|
|
289
179
|
}
|
|
290
180
|
case 'linkedin': {
|
|
291
|
-
const linkedIn = purest({
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
});
|
|
309
|
-
try {
|
|
310
|
-
const getDetailsRequest = () => {
|
|
311
|
-
return new Promise((resolve, reject) => {
|
|
312
|
-
linkedIn
|
|
313
|
-
.query()
|
|
314
|
-
.get('me')
|
|
315
|
-
.auth(access_token)
|
|
316
|
-
.request((err, res, body) => {
|
|
317
|
-
if (err) {
|
|
318
|
-
return reject(err);
|
|
319
|
-
}
|
|
320
|
-
resolve(body);
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const getEmailRequest = () => {
|
|
326
|
-
return new Promise((resolve, reject) => {
|
|
327
|
-
linkedIn
|
|
328
|
-
.query()
|
|
329
|
-
.get('emailAddress?q=members&projection=(elements*(handle~))')
|
|
330
|
-
.auth(access_token)
|
|
331
|
-
.request((err, res, body) => {
|
|
332
|
-
if (err) {
|
|
333
|
-
return reject(err);
|
|
334
|
-
}
|
|
335
|
-
resolve(body);
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
};
|
|
181
|
+
const linkedIn = purest({ provider: 'linkedin' });
|
|
182
|
+
const {
|
|
183
|
+
body: { localizedFirstName },
|
|
184
|
+
} = await linkedIn
|
|
185
|
+
.get('me')
|
|
186
|
+
.auth(access_token)
|
|
187
|
+
.request();
|
|
188
|
+
const {
|
|
189
|
+
body: { elements },
|
|
190
|
+
} = await linkedIn
|
|
191
|
+
.get('emailAddress?q=members&projection=(elements*(handle~))')
|
|
192
|
+
.auth(access_token)
|
|
193
|
+
.request();
|
|
339
194
|
|
|
340
|
-
|
|
341
|
-
const { elements } = await getEmailRequest();
|
|
342
|
-
const email = elements[0]['handle~'];
|
|
195
|
+
const email = elements[0]['handle~'];
|
|
343
196
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
} catch (err) {
|
|
349
|
-
callback(err);
|
|
350
|
-
}
|
|
351
|
-
break;
|
|
197
|
+
return {
|
|
198
|
+
username: localizedFirstName,
|
|
199
|
+
email: email.emailAddress,
|
|
200
|
+
};
|
|
352
201
|
}
|
|
353
202
|
case 'reddit': {
|
|
354
203
|
const reddit = purest({
|
|
355
204
|
provider: 'reddit',
|
|
356
|
-
config:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
205
|
+
config: {
|
|
206
|
+
reddit: {
|
|
207
|
+
default: {
|
|
208
|
+
origin: 'https://oauth.reddit.com',
|
|
209
|
+
path: 'api/{version}/{path}',
|
|
210
|
+
version: 'v1',
|
|
211
|
+
headers: {
|
|
212
|
+
Authorization: 'Bearer {auth}',
|
|
213
|
+
'user-agent': 'strapi',
|
|
214
|
+
},
|
|
215
|
+
},
|
|
360
216
|
},
|
|
361
217
|
},
|
|
362
218
|
});
|
|
363
219
|
|
|
364
|
-
reddit
|
|
365
|
-
.query('auth')
|
|
220
|
+
return reddit
|
|
366
221
|
.get('me')
|
|
367
222
|
.auth(access_token)
|
|
368
|
-
.request(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
username: body.name,
|
|
374
|
-
email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
break;
|
|
223
|
+
.request()
|
|
224
|
+
.then(({ body }) => ({
|
|
225
|
+
username: body.name,
|
|
226
|
+
email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
|
|
227
|
+
}));
|
|
379
228
|
}
|
|
380
229
|
case 'auth0': {
|
|
381
|
-
const
|
|
382
|
-
purestAuth0Conf[`https://${providers.auth0.subdomain}.auth0.com`] = {
|
|
383
|
-
__domain: {
|
|
384
|
-
auth: {
|
|
385
|
-
auth: { bearer: '[0]' },
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
'{endpoint}': {
|
|
389
|
-
__path: {
|
|
390
|
-
alias: '__default',
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
};
|
|
394
|
-
const auth0 = purest({
|
|
395
|
-
provider: 'auth0',
|
|
396
|
-
config: {
|
|
397
|
-
auth0: purestAuth0Conf,
|
|
398
|
-
},
|
|
399
|
-
});
|
|
230
|
+
const auth0 = purest({ provider: 'auth0' });
|
|
400
231
|
|
|
401
|
-
auth0
|
|
232
|
+
return auth0
|
|
402
233
|
.get('userinfo')
|
|
234
|
+
.subdomain(providers.auth0.subdomain)
|
|
403
235
|
.auth(access_token)
|
|
404
|
-
.request(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const username =
|
|
409
|
-
body.username || body.nickname || body.name || body.email.split('@')[0];
|
|
410
|
-
const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
|
|
236
|
+
.request()
|
|
237
|
+
.then(({ body }) => {
|
|
238
|
+
const username = body.username || body.nickname || body.name || body.email.split('@')[0];
|
|
239
|
+
const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
|
|
411
240
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
241
|
+
return {
|
|
242
|
+
username,
|
|
243
|
+
email,
|
|
244
|
+
};
|
|
417
245
|
});
|
|
418
|
-
break;
|
|
419
246
|
}
|
|
420
247
|
case 'cas': {
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
config: {
|
|
425
|
-
cas: {
|
|
426
|
-
[provider_url]: {
|
|
427
|
-
__domain: {
|
|
428
|
-
auth: {
|
|
429
|
-
auth: { bearer: '[0]' },
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
'{endpoint}': {
|
|
433
|
-
__path: {
|
|
434
|
-
alias: '__default',
|
|
435
|
-
},
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
},
|
|
439
|
-
},
|
|
440
|
-
});
|
|
441
|
-
cas
|
|
442
|
-
.query()
|
|
248
|
+
const cas = purest({ provider: 'cas' });
|
|
249
|
+
|
|
250
|
+
return cas
|
|
443
251
|
.get('oidc/profile')
|
|
252
|
+
.subdomain(providers.cas.subdomain)
|
|
444
253
|
.auth(access_token)
|
|
445
|
-
.request(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
'CAS Response Body did not contain required attributes: ' + JSON.stringify(body)
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
callback(null, {
|
|
462
|
-
username,
|
|
463
|
-
email,
|
|
464
|
-
});
|
|
254
|
+
.request()
|
|
255
|
+
.then(({ body }) => {
|
|
256
|
+
// CAS attribute may be in body.attributes or "FLAT", depending on CAS config
|
|
257
|
+
const username = body.attributes
|
|
258
|
+
? body.attributes.strapiusername || body.id || body.sub
|
|
259
|
+
: body.strapiusername || body.id || body.sub;
|
|
260
|
+
const email = body.attributes
|
|
261
|
+
? body.attributes.strapiemail || body.attributes.email
|
|
262
|
+
: body.strapiemail || body.email;
|
|
263
|
+
if (!username || !email) {
|
|
264
|
+
strapi.log.warn(
|
|
265
|
+
'CAS Response Body did not contain required attributes: ' + JSON.stringify(body)
|
|
266
|
+
);
|
|
465
267
|
}
|
|
268
|
+
return {
|
|
269
|
+
username,
|
|
270
|
+
email,
|
|
271
|
+
};
|
|
466
272
|
});
|
|
467
|
-
break;
|
|
468
273
|
}
|
|
469
274
|
default:
|
|
470
|
-
|
|
471
|
-
break;
|
|
275
|
+
throw new Error('Unknown provider.');
|
|
472
276
|
}
|
|
473
277
|
};
|
|
@@ -18,17 +18,16 @@ module.exports = ({ strapi }) => {
|
|
|
18
18
|
* Helper to get profiles
|
|
19
19
|
*
|
|
20
20
|
* @param {String} provider
|
|
21
|
-
* @param {Function} callback
|
|
22
21
|
*/
|
|
23
22
|
|
|
24
|
-
const getProfile = async (provider, query
|
|
23
|
+
const getProfile = async (provider, query) => {
|
|
25
24
|
const access_token = query.access_token || query.code || query.oauth_token;
|
|
26
25
|
|
|
27
26
|
const providers = await strapi
|
|
28
27
|
.store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
|
|
29
28
|
.get();
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
return providerRequest({ provider, query, access_token, providers });
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
/**
|
|
@@ -46,79 +45,69 @@ module.exports = ({ strapi }) => {
|
|
|
46
45
|
|
|
47
46
|
return new Promise((resolve, reject) => {
|
|
48
47
|
if (!access_token) {
|
|
49
|
-
return reject(
|
|
48
|
+
return reject({ message: 'No access_token.' });
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
// Get the profile.
|
|
53
|
-
getProfile(provider, query
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const email = _.toLower(profile.email);
|
|
59
|
-
|
|
60
|
-
// We need at least the mail.
|
|
61
|
-
if (!email) {
|
|
62
|
-
return reject([null, { message: 'Email was not available.' }]);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const users = await strapi.query('plugin::users-permissions.user').findMany({
|
|
67
|
-
where: { email },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const advanced = await strapi
|
|
71
|
-
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
72
|
-
.get();
|
|
73
|
-
|
|
74
|
-
const user = _.find(users, { provider });
|
|
75
|
-
|
|
76
|
-
if (_.isEmpty(user) && !advanced.allow_register) {
|
|
77
|
-
return resolve([
|
|
78
|
-
null,
|
|
79
|
-
[{ messages: [{ id: 'Auth.advanced.allow_register' }] }],
|
|
80
|
-
'Register action is actually not available.',
|
|
81
|
-
]);
|
|
82
|
-
}
|
|
52
|
+
getProfile(provider, query)
|
|
53
|
+
.then(async profile => {
|
|
54
|
+
const email = _.toLower(profile.email);
|
|
83
55
|
|
|
84
|
-
|
|
85
|
-
|
|
56
|
+
// We need at least the mail.
|
|
57
|
+
if (!email) {
|
|
58
|
+
return reject({ message: 'Email was not available.' });
|
|
86
59
|
}
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
61
|
+
try {
|
|
62
|
+
const users = await strapi.query('plugin::users-permissions.user').findMany({
|
|
63
|
+
where: { email },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const advanced = await strapi
|
|
67
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
68
|
+
.get();
|
|
69
|
+
|
|
70
|
+
const user = _.find(users, { provider });
|
|
71
|
+
|
|
72
|
+
if (_.isEmpty(user) && !advanced.allow_register) {
|
|
73
|
+
return reject({ message: 'Register action is actually not available.' });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!_.isEmpty(user)) {
|
|
77
|
+
return resolve(user);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
!_.isEmpty(_.find(users, user => user.provider !== provider)) &&
|
|
82
|
+
advanced.unique_email
|
|
83
|
+
) {
|
|
84
|
+
return reject({ message: 'Email is already taken.' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Retrieve default role.
|
|
88
|
+
const defaultRole = await strapi
|
|
89
|
+
.query('plugin::users-permissions.role')
|
|
90
|
+
.findOne({ where: { type: advanced.default_role } });
|
|
91
|
+
|
|
92
|
+
// Create the new user.
|
|
93
|
+
const params = {
|
|
94
|
+
...profile,
|
|
95
|
+
email, // overwrite with lowercased email
|
|
96
|
+
provider,
|
|
97
|
+
role: defaultRole.id,
|
|
98
|
+
confirmed: true,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createdUser = await strapi
|
|
102
|
+
.query('plugin::users-permissions.user')
|
|
103
|
+
.create({ data: params });
|
|
104
|
+
|
|
105
|
+
return resolve(createdUser);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
reject(err);
|
|
97
108
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const defaultRole = await strapi
|
|
101
|
-
.query('plugin::users-permissions.role')
|
|
102
|
-
.findOne({ where: { type: advanced.default_role } });
|
|
103
|
-
|
|
104
|
-
// Create the new user.
|
|
105
|
-
const params = {
|
|
106
|
-
...profile,
|
|
107
|
-
email, // overwrite with lowercased email
|
|
108
|
-
provider,
|
|
109
|
-
role: defaultRole.id,
|
|
110
|
-
confirmed: true,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const createdUser = await strapi
|
|
114
|
-
.query('plugin::users-permissions.user')
|
|
115
|
-
.create({ data: params });
|
|
116
|
-
|
|
117
|
-
return resolve([createdUser, null]);
|
|
118
|
-
} catch (err) {
|
|
119
|
-
reject([null, err]);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
109
|
+
})
|
|
110
|
+
.catch(reject);
|
|
122
111
|
});
|
|
123
112
|
};
|
|
124
113
|
|