@strapi/plugin-users-permissions 0.0.0-next.f0bfcece1007e5aa527570187635aefc86db536e → 0.0.0-next.f0f36e3df4b18f167036dcbca529dcb933bf4e1d
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/admin/src/components/Permissions/PermissionRow/SubCategory.jsx +8 -1
- package/admin/src/components/Policies/index.jsx +1 -0
- package/admin/src/components/UsersPermissions/index.jsx +1 -0
- package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +2 -1
- package/admin/src/pages/Providers/index.jsx +74 -76
- package/admin/src/pages/Roles/pages/CreatePage.jsx +20 -7
- package/admin/src/pages/Roles/pages/EditPage.jsx +12 -7
- package/admin/src/pages/Roles/pages/ListPage/index.jsx +91 -93
- package/admin/src/translations/en.json +1 -0
- package/dist/admin/components/FormModal/Input/index.js.map +1 -1
- package/dist/admin/components/FormModal/Input/index.mjs.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.js +3 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.js.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs +3 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/index.js.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/index.mjs.map +1 -1
- package/dist/admin/components/Policies/index.js +1 -0
- package/dist/admin/components/Policies/index.js.map +1 -1
- package/dist/admin/components/Policies/index.mjs +1 -0
- package/dist/admin/components/Policies/index.mjs.map +1 -1
- package/dist/admin/components/UsersPermissions/index.js +1 -0
- package/dist/admin/components/UsersPermissions/index.js.map +1 -1
- package/dist/admin/components/UsersPermissions/index.mjs +1 -0
- package/dist/admin/components/UsersPermissions/index.mjs.map +1 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.js +2 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.js.map +1 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs +2 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs.map +1 -1
- package/dist/admin/pages/Providers/index.js +84 -88
- package/dist/admin/pages/Providers/index.js.map +1 -1
- package/dist/admin/pages/Providers/index.mjs +84 -88
- package/dist/admin/pages/Providers/index.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/CreatePage.js +14 -1
- package/dist/admin/pages/Roles/pages/CreatePage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/CreatePage.mjs +16 -3
- package/dist/admin/pages/Roles/pages/CreatePage.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.js +13 -3
- package/dist/admin/pages/Roles/pages/EditPage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.mjs +14 -4
- package/dist/admin/pages/Roles/pages/EditPage.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js +95 -99
- package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs +95 -99
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
- package/dist/admin/translations/en.json.js +1 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +1 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/getTrad.js.map +1 -1
- package/dist/admin/utils/getTrad.mjs.map +1 -1
- package/dist/admin/utils/prefixPluginTranslations.js.map +1 -1
- package/dist/admin/utils/prefixPluginTranslations.mjs.map +1 -1
- package/dist/server/bootstrap/index.js +28 -7
- package/dist/server/bootstrap/index.js.map +1 -1
- package/dist/server/bootstrap/index.mjs +28 -7
- package/dist/server/bootstrap/index.mjs.map +1 -1
- package/dist/server/config.js +16 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +16 -0
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/controllers/auth.js +204 -3
- package/dist/server/controllers/auth.js.map +1 -1
- package/dist/server/controllers/auth.mjs +204 -3
- package/dist/server/controllers/auth.mjs.map +1 -1
- package/dist/server/controllers/content-manager-user.js.map +1 -1
- package/dist/server/controllers/content-manager-user.mjs.map +1 -1
- package/dist/server/graphql/resolvers-configs.js.map +1 -1
- package/dist/server/graphql/resolvers-configs.mjs.map +1 -1
- package/dist/server/middlewares/rateLimit.js.map +1 -1
- package/dist/server/middlewares/rateLimit.mjs.map +1 -1
- package/dist/server/routes/content-api/auth.js +16 -0
- package/dist/server/routes/content-api/auth.js.map +1 -1
- package/dist/server/routes/content-api/auth.mjs +16 -0
- package/dist/server/routes/content-api/auth.mjs.map +1 -1
- package/dist/server/routes/content-api/validation.js +1 -0
- package/dist/server/routes/content-api/validation.js.map +1 -1
- package/dist/server/routes/content-api/validation.mjs +1 -0
- package/dist/server/routes/content-api/validation.mjs.map +1 -1
- package/dist/server/services/constants.js +19 -0
- package/dist/server/services/constants.js.map +1 -0
- package/dist/server/services/constants.mjs +17 -0
- package/dist/server/services/constants.mjs.map +1 -0
- package/dist/server/services/jwt.js +45 -2
- package/dist/server/services/jwt.js.map +1 -1
- package/dist/server/services/jwt.mjs +45 -2
- package/dist/server/services/jwt.mjs.map +1 -1
- package/dist/server/services/providers-registry.js.map +1 -1
- package/dist/server/services/providers-registry.mjs.map +1 -1
- package/dist/server/services/role.js.map +1 -1
- package/dist/server/services/role.mjs.map +1 -1
- package/dist/server/services/user.js +29 -20
- package/dist/server/services/user.js.map +1 -1
- package/dist/server/services/user.mjs +29 -20
- package/dist/server/services/user.mjs.map +1 -1
- package/dist/server/services/users-permissions.js +8 -0
- package/dist/server/services/users-permissions.js.map +1 -1
- package/dist/server/services/users-permissions.mjs +8 -0
- package/dist/server/services/users-permissions.mjs.map +1 -1
- package/package.json +9 -9
- package/server/bootstrap/index.js +31 -0
- package/server/config.js +22 -0
- package/server/controllers/auth.js +248 -8
- package/server/routes/content-api/auth.js +12 -0
- package/server/routes/content-api/validation.js +1 -0
- package/server/services/constants.js +9 -0
- package/server/services/jwt.js +50 -2
- package/server/services/user.js +11 -0
- package/server/services/users-permissions.js +2 -0
|
@@ -31,6 +31,12 @@ const sanitizeUser = (user, ctx) => {
|
|
|
31
31
|
return strapi.contentAPI.sanitize.output(user, userSchema, { auth });
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
const extractDeviceId = (requestBody) => {
|
|
35
|
+
const { deviceId } = requestBody || {};
|
|
36
|
+
|
|
37
|
+
return typeof deviceId === 'string' && deviceId.length > 0 ? deviceId : undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
module.exports = ({ strapi }) => ({
|
|
35
41
|
async callback(ctx) {
|
|
36
42
|
const provider = ctx.params.provider || 'local';
|
|
@@ -86,6 +92,51 @@ module.exports = ({ strapi }) => ({
|
|
|
86
92
|
throw new ApplicationError('Your account has been blocked by an administrator');
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
96
|
+
if (mode === 'refresh') {
|
|
97
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
98
|
+
|
|
99
|
+
const refresh = await strapi
|
|
100
|
+
.sessionManager('users-permissions')
|
|
101
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
102
|
+
|
|
103
|
+
const access = await strapi
|
|
104
|
+
.sessionManager('users-permissions')
|
|
105
|
+
.generateAccessToken(refresh.token);
|
|
106
|
+
if ('error' in access) {
|
|
107
|
+
throw new ApplicationError('Invalid credentials');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
111
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
112
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
113
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
114
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
115
|
+
const isSecure =
|
|
116
|
+
typeof upSessions.cookie?.secure === 'boolean'
|
|
117
|
+
? upSessions.cookie?.secure
|
|
118
|
+
: isProduction;
|
|
119
|
+
|
|
120
|
+
const cookieOptions = {
|
|
121
|
+
httpOnly: true,
|
|
122
|
+
secure: isSecure,
|
|
123
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
124
|
+
path: upSessions.cookie?.path ?? '/',
|
|
125
|
+
domain: upSessions.cookie?.domain,
|
|
126
|
+
overwrite: true,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
ctx.cookies.set(cookieName, refresh.token, cookieOptions);
|
|
130
|
+
return ctx.send({ jwt: access.token, user: await sanitizeUser(user, ctx) });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return ctx.send({
|
|
134
|
+
jwt: access.token,
|
|
135
|
+
refreshToken: refresh.token,
|
|
136
|
+
user: await sanitizeUser(user, ctx),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
89
140
|
return ctx.send({
|
|
90
141
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
91
142
|
user: await sanitizeUser(user, ctx),
|
|
@@ -100,6 +151,49 @@ module.exports = ({ strapi }) => ({
|
|
|
100
151
|
throw new ForbiddenError('Your account has been blocked by an administrator');
|
|
101
152
|
}
|
|
102
153
|
|
|
154
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
155
|
+
if (mode === 'refresh') {
|
|
156
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
157
|
+
|
|
158
|
+
const refresh = await strapi
|
|
159
|
+
.sessionManager('users-permissions')
|
|
160
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
161
|
+
|
|
162
|
+
const access = await strapi
|
|
163
|
+
.sessionManager('users-permissions')
|
|
164
|
+
.generateAccessToken(refresh.token);
|
|
165
|
+
if ('error' in access) {
|
|
166
|
+
throw new ApplicationError('Invalid credentials');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
170
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
171
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
172
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
173
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
174
|
+
const isSecure =
|
|
175
|
+
typeof upSessions.cookie?.secure === 'boolean'
|
|
176
|
+
? upSessions.cookie?.secure
|
|
177
|
+
: isProduction;
|
|
178
|
+
|
|
179
|
+
const cookieOptions = {
|
|
180
|
+
httpOnly: true,
|
|
181
|
+
secure: isSecure,
|
|
182
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
183
|
+
path: upSessions.cookie?.path ?? '/',
|
|
184
|
+
domain: upSessions.cookie?.domain,
|
|
185
|
+
overwrite: true,
|
|
186
|
+
};
|
|
187
|
+
ctx.cookies.set(cookieName, refresh.token, cookieOptions);
|
|
188
|
+
return ctx.send({ jwt: access.token, user: await sanitizeUser(user, ctx) });
|
|
189
|
+
}
|
|
190
|
+
return ctx.send({
|
|
191
|
+
jwt: access.token,
|
|
192
|
+
refreshToken: refresh.token,
|
|
193
|
+
user: await sanitizeUser(user, ctx),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
103
197
|
return ctx.send({
|
|
104
198
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
105
199
|
user: await sanitizeUser(user, ctx),
|
|
@@ -137,7 +231,37 @@ module.exports = ({ strapi }) => ({
|
|
|
137
231
|
|
|
138
232
|
await getService('user').edit(user.id, { password });
|
|
139
233
|
|
|
140
|
-
|
|
234
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
235
|
+
if (mode === 'refresh') {
|
|
236
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
237
|
+
|
|
238
|
+
if (deviceId) {
|
|
239
|
+
// Invalidate sessions: specific device if deviceId provided
|
|
240
|
+
await strapi
|
|
241
|
+
.sessionManager('users-permissions')
|
|
242
|
+
.invalidateRefreshToken(String(user.id), deviceId);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const newDeviceId = deviceId || crypto.randomUUID();
|
|
246
|
+
const refresh = await strapi
|
|
247
|
+
.sessionManager('users-permissions')
|
|
248
|
+
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
249
|
+
|
|
250
|
+
const access = await strapi
|
|
251
|
+
.sessionManager('users-permissions')
|
|
252
|
+
.generateAccessToken(refresh.token);
|
|
253
|
+
if ('error' in access) {
|
|
254
|
+
throw new ApplicationError('Invalid credentials');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return ctx.send({
|
|
258
|
+
jwt: access.token,
|
|
259
|
+
refreshToken: refresh.token,
|
|
260
|
+
user: await sanitizeUser(user, ctx),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return ctx.send({
|
|
141
265
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
142
266
|
user: await sanitizeUser(user, ctx),
|
|
143
267
|
});
|
|
@@ -168,13 +292,115 @@ module.exports = ({ strapi }) => ({
|
|
|
168
292
|
password,
|
|
169
293
|
});
|
|
170
294
|
|
|
171
|
-
|
|
172
|
-
|
|
295
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
296
|
+
if (mode === 'refresh') {
|
|
297
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
298
|
+
|
|
299
|
+
if (deviceId) {
|
|
300
|
+
// Invalidate sessions: specific device if deviceId provided
|
|
301
|
+
await strapi
|
|
302
|
+
.sessionManager('users-permissions')
|
|
303
|
+
.invalidateRefreshToken(String(user.id), deviceId);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const newDeviceId = deviceId || crypto.randomUUID();
|
|
307
|
+
const refresh = await strapi
|
|
308
|
+
.sessionManager('users-permissions')
|
|
309
|
+
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
310
|
+
|
|
311
|
+
const access = await strapi
|
|
312
|
+
.sessionManager('users-permissions')
|
|
313
|
+
.generateAccessToken(refresh.token);
|
|
314
|
+
if ('error' in access) {
|
|
315
|
+
throw new ApplicationError('Invalid credentials');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return ctx.send({
|
|
319
|
+
jwt: access.token,
|
|
320
|
+
refreshToken: refresh.token,
|
|
321
|
+
user: await sanitizeUser(user, ctx),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return ctx.send({
|
|
173
326
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
174
327
|
user: await sanitizeUser(user, ctx),
|
|
175
328
|
});
|
|
176
329
|
},
|
|
330
|
+
async refresh(ctx) {
|
|
331
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
332
|
+
if (mode !== 'refresh') {
|
|
333
|
+
return ctx.notFound();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const { refreshToken } = ctx.request.body || {};
|
|
337
|
+
if (!refreshToken || typeof refreshToken !== 'string') {
|
|
338
|
+
return ctx.badRequest('Missing refresh token');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const rotation = await strapi
|
|
342
|
+
.sessionManager('users-permissions')
|
|
343
|
+
.rotateRefreshToken(refreshToken);
|
|
344
|
+
if ('error' in rotation) {
|
|
345
|
+
return ctx.unauthorized('Invalid refresh token');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const result = await strapi
|
|
349
|
+
.sessionManager('users-permissions')
|
|
350
|
+
.generateAccessToken(rotation.token);
|
|
351
|
+
if ('error' in result) {
|
|
352
|
+
return ctx.unauthorized('Invalid refresh token');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
356
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
357
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
358
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
359
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
360
|
+
const isSecure =
|
|
361
|
+
typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
|
|
362
|
+
|
|
363
|
+
const cookieOptions = {
|
|
364
|
+
httpOnly: true,
|
|
365
|
+
secure: isSecure,
|
|
366
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
367
|
+
path: upSessions.cookie?.path ?? '/',
|
|
368
|
+
domain: upSessions.cookie?.domain,
|
|
369
|
+
overwrite: true,
|
|
370
|
+
};
|
|
371
|
+
ctx.cookies.set(cookieName, rotation.token, cookieOptions);
|
|
372
|
+
return ctx.send({ jwt: result.token });
|
|
373
|
+
}
|
|
374
|
+
return ctx.send({ jwt: result.token, refreshToken: rotation.token });
|
|
375
|
+
},
|
|
376
|
+
async logout(ctx) {
|
|
377
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
378
|
+
if (mode !== 'refresh') {
|
|
379
|
+
return ctx.notFound();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Invalidate all sessions for the authenticated user, or by deviceId if provided
|
|
383
|
+
if (!ctx.state.user) {
|
|
384
|
+
return ctx.unauthorized('Missing authentication');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
388
|
+
try {
|
|
389
|
+
await strapi
|
|
390
|
+
.sessionManager('users-permissions')
|
|
391
|
+
.invalidateRefreshToken(String(ctx.state.user.id), deviceId);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
strapi.log.error('UP logout failed', err);
|
|
394
|
+
}
|
|
177
395
|
|
|
396
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
397
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
398
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
399
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
400
|
+
ctx.cookies.set(cookieName, '', { expires: new Date(0) });
|
|
401
|
+
}
|
|
402
|
+
return ctx.send({ ok: true });
|
|
403
|
+
},
|
|
178
404
|
async connect(ctx, next) {
|
|
179
405
|
const grant = require('grant').koa();
|
|
180
406
|
|
|
@@ -387,12 +613,26 @@ module.exports = ({ strapi }) => ({
|
|
|
387
613
|
return ctx.send({ user: sanitizedUser });
|
|
388
614
|
}
|
|
389
615
|
|
|
390
|
-
const
|
|
616
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
617
|
+
if (mode === 'refresh') {
|
|
618
|
+
const deviceId = extractDeviceId(ctx.request.body) || crypto.randomUUID();
|
|
391
619
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
620
|
+
const refresh = await strapi
|
|
621
|
+
.sessionManager('users-permissions')
|
|
622
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
623
|
+
|
|
624
|
+
const access = await strapi
|
|
625
|
+
.sessionManager('users-permissions')
|
|
626
|
+
.generateAccessToken(refresh.token);
|
|
627
|
+
if ('error' in access) {
|
|
628
|
+
throw new ApplicationError('Invalid credentials');
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return ctx.send({ jwt: access.token, refreshToken: refresh.token, user: sanitizedUser });
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const jwt = getService('jwt').issue(_.pick(user, ['id']));
|
|
635
|
+
return ctx.send({ jwt, user: sanitizedUser });
|
|
396
636
|
},
|
|
397
637
|
|
|
398
638
|
async emailConfirmation(ctx, next, returnUser) {
|
|
@@ -114,5 +114,17 @@ module.exports = (strapi) => {
|
|
|
114
114
|
},
|
|
115
115
|
response: validator.authResponseSchema,
|
|
116
116
|
},
|
|
117
|
+
{
|
|
118
|
+
method: 'POST',
|
|
119
|
+
path: '/auth/refresh',
|
|
120
|
+
handler: 'auth.refresh',
|
|
121
|
+
config: { prefix: '' },
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
method: 'POST',
|
|
125
|
+
path: '/auth/logout',
|
|
126
|
+
handler: 'auth.logout',
|
|
127
|
+
config: { prefix: '' },
|
|
128
|
+
},
|
|
117
129
|
];
|
|
118
130
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
DEFAULT_ACCESS_TOKEN_LIFESPAN: 10 * 60, // 10 minutes
|
|
5
|
+
DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN: 30 * 24 * 60 * 60, // 30 days
|
|
6
|
+
DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN: 14 * 24 * 60 * 60, // 14 days
|
|
7
|
+
DEFAULT_MAX_SESSION_LIFESPAN: 1 * 24 * 60 * 60, // 1 day
|
|
8
|
+
DEFAULT_IDLE_SESSION_LIFESPAN: 2 * 60 * 60, // 2 hours
|
|
9
|
+
};
|
package/server/services/jwt.js
CHANGED
|
@@ -29,6 +29,32 @@ module.exports = ({ strapi }) => ({
|
|
|
29
29
|
},
|
|
30
30
|
|
|
31
31
|
issue(payload, jwtOptions = {}) {
|
|
32
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
33
|
+
|
|
34
|
+
if (mode === 'refresh') {
|
|
35
|
+
const userId = String(payload.id ?? payload.userId ?? '');
|
|
36
|
+
if (!userId) {
|
|
37
|
+
throw new Error('Cannot issue token: missing user id');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const issueRefreshToken = async () => {
|
|
41
|
+
const refresh = await strapi
|
|
42
|
+
.sessionManager('users-permissions')
|
|
43
|
+
.generateRefreshToken(userId, undefined, { type: 'refresh' });
|
|
44
|
+
|
|
45
|
+
const access = await strapi
|
|
46
|
+
.sessionManager('users-permissions')
|
|
47
|
+
.generateAccessToken(refresh.token);
|
|
48
|
+
if ('error' in access) {
|
|
49
|
+
throw new Error('Failed to generate access token');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return access.token;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return issueRefreshToken();
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
_.defaults(jwtOptions, strapi.config.get('plugin::users-permissions.jwt'));
|
|
33
59
|
return jwt.sign(
|
|
34
60
|
_.clone(payload.toJSON ? payload.toJSON() : payload),
|
|
@@ -37,12 +63,34 @@ module.exports = ({ strapi }) => ({
|
|
|
37
63
|
);
|
|
38
64
|
},
|
|
39
65
|
|
|
40
|
-
verify(token) {
|
|
66
|
+
async verify(token) {
|
|
67
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
68
|
+
|
|
69
|
+
if (mode === 'refresh') {
|
|
70
|
+
// Accept only access tokens minted by the SessionManager for UP
|
|
71
|
+
const result = strapi.sessionManager('users-permissions').validateAccessToken(token);
|
|
72
|
+
if (!result.isValid || result.payload.type !== 'access') {
|
|
73
|
+
throw new Error('Invalid token.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const user = await strapi.db
|
|
77
|
+
.query('plugin::users-permissions.user')
|
|
78
|
+
.findOne({ where: { id: Number(result.payload.userId) || result.payload.userId } });
|
|
79
|
+
if (!user) {
|
|
80
|
+
throw new Error('Invalid token.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { id: user.id };
|
|
84
|
+
}
|
|
85
|
+
|
|
41
86
|
return new Promise((resolve, reject) => {
|
|
87
|
+
const jwtConfig = strapi.config.get('plugin::users-permissions.jwt', {});
|
|
88
|
+
const algorithms = jwtConfig && jwtConfig.algorithm ? [jwtConfig.algorithm] : undefined;
|
|
89
|
+
|
|
42
90
|
jwt.verify(
|
|
43
91
|
token,
|
|
44
92
|
strapi.config.get('plugin::users-permissions.jwtSecret'),
|
|
45
|
-
{},
|
|
93
|
+
algorithms ? { algorithms } : {},
|
|
46
94
|
(err, tokenPayload = {}) => {
|
|
47
95
|
if (err) {
|
|
48
96
|
return reject(new Error('Invalid token.'));
|
package/server/services/user.js
CHANGED
|
@@ -16,6 +16,11 @@ const { getService } = require('../utils');
|
|
|
16
16
|
|
|
17
17
|
const USER_MODEL_UID = 'plugin::users-permissions.user';
|
|
18
18
|
|
|
19
|
+
const getSessionManager = () => {
|
|
20
|
+
const manager = strapi.sessionManager;
|
|
21
|
+
return manager ?? null;
|
|
22
|
+
};
|
|
23
|
+
|
|
19
24
|
module.exports = ({ strapi }) => ({
|
|
20
25
|
/**
|
|
21
26
|
* Promise to count users
|
|
@@ -112,6 +117,12 @@ module.exports = ({ strapi }) => ({
|
|
|
112
117
|
* @return {Promise}
|
|
113
118
|
*/
|
|
114
119
|
async remove(params) {
|
|
120
|
+
// Invalidate sessions for all affected users
|
|
121
|
+
const sessionManager = getSessionManager();
|
|
122
|
+
if (sessionManager && sessionManager.hasOrigin('users-permissions') && params.id) {
|
|
123
|
+
await sessionManager('users-permissions').invalidateRefreshToken(String(params.id));
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
return strapi.db.query(USER_MODEL_UID).delete({ where: params });
|
|
116
127
|
},
|
|
117
128
|
|
|
@@ -20,6 +20,8 @@ const DEFAULT_PERMISSIONS = [
|
|
|
20
20
|
{ action: 'plugin::users-permissions.auth.register', roleType: 'public' },
|
|
21
21
|
{ action: 'plugin::users-permissions.auth.emailConfirmation', roleType: 'public' },
|
|
22
22
|
{ action: 'plugin::users-permissions.auth.sendEmailConfirmation', roleType: 'public' },
|
|
23
|
+
{ action: 'plugin::users-permissions.auth.refresh', roleType: 'public' },
|
|
24
|
+
{ action: 'plugin::users-permissions.auth.logout', roleType: 'authenticated' },
|
|
23
25
|
{ action: 'plugin::users-permissions.user.me', roleType: 'authenticated' },
|
|
24
26
|
{ action: 'plugin::users-permissions.auth.changePassword', roleType: 'authenticated' },
|
|
25
27
|
];
|