@strapi/plugin-users-permissions 0.0.0-next.d9724d67b33363354d7171a9f2265e1c42485e13 → 0.0.0-next.da19c0501ff87d14fb664b55b8e0630d3c548485
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/pages/Providers/index.jsx +74 -76
- package/admin/src/pages/Roles/pages/CreatePage.jsx +4 -13
- package/admin/src/pages/Roles/pages/EditPage.jsx +4 -13
- package/admin/src/pages/Roles/pages/ListPage/index.jsx +91 -93
- package/admin/src/translations/en.json +1 -0
- 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 +2 -1
- package/dist/admin/pages/Roles/pages/CreatePage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/CreatePage.mjs +3 -2
- package/dist/admin/pages/Roles/pages/CreatePage.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.js +2 -1
- package/dist/admin/pages/Roles/pages/EditPage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.mjs +3 -2
- package/dist/admin/pages/Roles/pages/EditPage.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/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/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/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/package.json +5 -5
- 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
|
@@ -21,13 +21,17 @@ function requireUser() {
|
|
|
21
21
|
const { toNumber, getOr } = require$$0$1;
|
|
22
22
|
const { getService } = requireUtils();
|
|
23
23
|
const USER_MODEL_UID = 'plugin::users-permissions.user';
|
|
24
|
-
|
|
24
|
+
const getSessionManager = ()=>{
|
|
25
|
+
const manager = strapi.sessionManager;
|
|
26
|
+
return manager ?? null;
|
|
27
|
+
};
|
|
28
|
+
user = ({ strapi: strapi1 })=>({
|
|
25
29
|
/**
|
|
26
30
|
* Promise to count users
|
|
27
31
|
*
|
|
28
32
|
* @return {Promise}
|
|
29
33
|
*/ count (params) {
|
|
30
|
-
return
|
|
34
|
+
return strapi1.db.query(USER_MODEL_UID).count({
|
|
31
35
|
where: params
|
|
32
36
|
});
|
|
33
37
|
},
|
|
@@ -39,7 +43,7 @@ function requireUser() {
|
|
|
39
43
|
* @param {object} values - The object containing the fields to be hashed.
|
|
40
44
|
* @return {object} The values object with hashed password fields if they were present.
|
|
41
45
|
*/ async ensureHashedPasswords (values) {
|
|
42
|
-
const attributes =
|
|
46
|
+
const attributes = strapi1.getModel(USER_MODEL_UID).attributes;
|
|
43
47
|
for(const key in values){
|
|
44
48
|
if (attributes[key] && attributes[key].type === 'password') {
|
|
45
49
|
// Check if a custom encryption.rounds has been set on the password attribute
|
|
@@ -53,7 +57,7 @@ function requireUser() {
|
|
|
53
57
|
* Promise to add a/an user.
|
|
54
58
|
* @return {Promise}
|
|
55
59
|
*/ async add (values) {
|
|
56
|
-
return
|
|
60
|
+
return strapi1.db.query(USER_MODEL_UID).create({
|
|
57
61
|
data: await this.ensureHashedPasswords(values),
|
|
58
62
|
populate: [
|
|
59
63
|
'role'
|
|
@@ -66,7 +70,7 @@ function requireUser() {
|
|
|
66
70
|
* @param {object} params
|
|
67
71
|
* @return {Promise}
|
|
68
72
|
*/ async edit (userId, params = {}) {
|
|
69
|
-
return
|
|
73
|
+
return strapi1.db.query(USER_MODEL_UID).update({
|
|
70
74
|
where: {
|
|
71
75
|
id: userId
|
|
72
76
|
},
|
|
@@ -80,8 +84,8 @@ function requireUser() {
|
|
|
80
84
|
* Promise to fetch a/an user.
|
|
81
85
|
* @return {Promise}
|
|
82
86
|
*/ fetch (id, params) {
|
|
83
|
-
const query =
|
|
84
|
-
return
|
|
87
|
+
const query = strapi1.get('query-params').transform(USER_MODEL_UID, params ?? {});
|
|
88
|
+
return strapi1.db.query(USER_MODEL_UID).findOne({
|
|
85
89
|
...query,
|
|
86
90
|
where: {
|
|
87
91
|
$and: [
|
|
@@ -97,7 +101,7 @@ function requireUser() {
|
|
|
97
101
|
* Promise to fetch authenticated user.
|
|
98
102
|
* @return {Promise}
|
|
99
103
|
*/ fetchAuthenticatedUser (id) {
|
|
100
|
-
return
|
|
104
|
+
return strapi1.db.query(USER_MODEL_UID).findOne({
|
|
101
105
|
where: {
|
|
102
106
|
id
|
|
103
107
|
},
|
|
@@ -110,14 +114,19 @@ function requireUser() {
|
|
|
110
114
|
* Promise to fetch all users.
|
|
111
115
|
* @return {Promise}
|
|
112
116
|
*/ fetchAll (params) {
|
|
113
|
-
const query =
|
|
114
|
-
return
|
|
117
|
+
const query = strapi1.get('query-params').transform(USER_MODEL_UID, params ?? {});
|
|
118
|
+
return strapi1.db.query(USER_MODEL_UID).findMany(query);
|
|
115
119
|
},
|
|
116
120
|
/**
|
|
117
121
|
* Promise to remove a/an user.
|
|
118
122
|
* @return {Promise}
|
|
119
123
|
*/ async remove (params) {
|
|
120
|
-
|
|
124
|
+
// Invalidate sessions for all affected users
|
|
125
|
+
const sessionManager = getSessionManager();
|
|
126
|
+
if (sessionManager && sessionManager.hasOrigin('users-permissions') && params.id) {
|
|
127
|
+
await sessionManager('users-permissions').invalidateRefreshToken(String(params.id));
|
|
128
|
+
}
|
|
129
|
+
return strapi1.db.query(USER_MODEL_UID).delete({
|
|
121
130
|
where: params
|
|
122
131
|
});
|
|
123
132
|
},
|
|
@@ -126,29 +135,29 @@ function requireUser() {
|
|
|
126
135
|
},
|
|
127
136
|
async sendConfirmationEmail (user) {
|
|
128
137
|
const userPermissionService = getService('users-permissions');
|
|
129
|
-
const pluginStore = await
|
|
138
|
+
const pluginStore = await strapi1.store({
|
|
130
139
|
type: 'plugin',
|
|
131
140
|
name: 'users-permissions'
|
|
132
141
|
});
|
|
133
|
-
const userSchema =
|
|
142
|
+
const userSchema = strapi1.getModel(USER_MODEL_UID);
|
|
134
143
|
const settings = await pluginStore.get({
|
|
135
144
|
key: 'email'
|
|
136
145
|
}).then((storeEmail)=>storeEmail.email_confirmation.options);
|
|
137
146
|
// Sanitize the template's user information
|
|
138
147
|
const sanitizedUserInfo = await sanitize.sanitizers.defaultSanitizeOutput({
|
|
139
148
|
schema: userSchema,
|
|
140
|
-
getModel:
|
|
149
|
+
getModel: strapi1.getModel.bind(strapi1)
|
|
141
150
|
}, user);
|
|
142
151
|
const confirmationToken = crypto.randomBytes(20).toString('hex');
|
|
143
152
|
await this.edit(user.id, {
|
|
144
153
|
confirmationToken
|
|
145
154
|
});
|
|
146
|
-
const apiPrefix =
|
|
155
|
+
const apiPrefix = strapi1.config.get('api.rest.prefix');
|
|
147
156
|
try {
|
|
148
157
|
settings.message = await userPermissionService.template(settings.message, {
|
|
149
|
-
URL: urlJoin(
|
|
150
|
-
SERVER_URL:
|
|
151
|
-
ADMIN_URL:
|
|
158
|
+
URL: urlJoin(strapi1.config.get('server.absoluteUrl'), apiPrefix, '/auth/email-confirmation'),
|
|
159
|
+
SERVER_URL: strapi1.config.get('server.absoluteUrl'),
|
|
160
|
+
ADMIN_URL: strapi1.config.get('admin.absoluteUrl'),
|
|
152
161
|
USER: sanitizedUserInfo,
|
|
153
162
|
CODE: confirmationToken
|
|
154
163
|
});
|
|
@@ -156,11 +165,11 @@ function requireUser() {
|
|
|
156
165
|
USER: sanitizedUserInfo
|
|
157
166
|
});
|
|
158
167
|
} catch {
|
|
159
|
-
|
|
168
|
+
strapi1.log.error('[plugin::users-permissions.sendConfirmationEmail]: Failed to generate a template for "user confirmation email". Please make sure your email template is valid and does not contain invalid characters or patterns');
|
|
160
169
|
return;
|
|
161
170
|
}
|
|
162
171
|
// Send an email to the user.
|
|
163
|
-
await
|
|
172
|
+
await strapi1.plugin('email').service('email').send({
|
|
164
173
|
to: user.email,
|
|
165
174
|
from: settings.from.email && settings.from.name ? `${settings.from.name} <${settings.from.email}>` : undefined,
|
|
166
175
|
replyTo: settings.response_email,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.mjs","sources":["../../../server/services/user.js"],"sourcesContent":["'use strict';\n\n/**\n * User.js service\n *\n * @description: A set of functions similar to controller's actions to avoid code duplication.\n */\n\nconst crypto = require('crypto');\nconst bcrypt = require('bcryptjs');\nconst urlJoin = require('url-join');\n\nconst { sanitize } = require('@strapi/utils');\nconst { toNumber, getOr } = require('lodash/fp');\nconst { getService } = require('../utils');\n\nconst USER_MODEL_UID = 'plugin::users-permissions.user';\n\nmodule.exports = ({ strapi }) => ({\n /**\n * Promise to count users\n *\n * @return {Promise}\n */\n\n count(params) {\n return strapi.db.query(USER_MODEL_UID).count({ where: params });\n },\n\n /**\n * Hashes password fields in the provided values object if they are present.\n * It checks each key in the values object against the model's attributes and\n * hashes it if the attribute type is 'password',\n *\n * @param {object} values - The object containing the fields to be hashed.\n * @return {object} The values object with hashed password fields if they were present.\n */\n async ensureHashedPasswords(values) {\n const attributes = strapi.getModel(USER_MODEL_UID).attributes;\n\n for (const key in values) {\n if (attributes[key] && attributes[key].type === 'password') {\n // Check if a custom encryption.rounds has been set on the password attribute\n const rounds = toNumber(getOr(10, 'encryption.rounds', attributes[key]));\n values[key] = await bcrypt.hash(values[key], rounds);\n }\n }\n\n return values;\n },\n\n /**\n * Promise to add a/an user.\n * @return {Promise}\n */\n async add(values) {\n return strapi.db.query(USER_MODEL_UID).create({\n data: await this.ensureHashedPasswords(values),\n populate: ['role'],\n });\n },\n\n /**\n * Promise to edit a/an user.\n * @param {string} userId\n * @param {object} params\n * @return {Promise}\n */\n async edit(userId, params = {}) {\n return strapi.db.query(USER_MODEL_UID).update({\n where: { id: userId },\n data: await this.ensureHashedPasswords(params),\n populate: ['role'],\n });\n },\n\n /**\n * Promise to fetch a/an user.\n * @return {Promise}\n */\n fetch(id, params) {\n const query = strapi.get('query-params').transform(USER_MODEL_UID, params ?? {});\n\n return strapi.db.query(USER_MODEL_UID).findOne({\n ...query,\n where: {\n $and: [{ id }, query.where || {}],\n },\n });\n },\n\n /**\n * Promise to fetch authenticated user.\n * @return {Promise}\n */\n fetchAuthenticatedUser(id) {\n return strapi.db.query(USER_MODEL_UID).findOne({ where: { id }, populate: ['role'] });\n },\n\n /**\n * Promise to fetch all users.\n * @return {Promise}\n */\n fetchAll(params) {\n const query = strapi.get('query-params').transform(USER_MODEL_UID, params ?? {});\n\n return strapi.db.query(USER_MODEL_UID).findMany(query);\n },\n\n /**\n * Promise to remove a/an user.\n * @return {Promise}\n */\n async remove(params) {\n return strapi.db.query(USER_MODEL_UID).delete({ where: params });\n },\n\n validatePassword(password, hash) {\n return bcrypt.compare(password, hash);\n },\n\n async sendConfirmationEmail(user) {\n const userPermissionService = getService('users-permissions');\n const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });\n const userSchema = strapi.getModel(USER_MODEL_UID);\n\n const settings = await pluginStore\n .get({ key: 'email' })\n .then((storeEmail) => storeEmail.email_confirmation.options);\n\n // Sanitize the template's user information\n const sanitizedUserInfo = await sanitize.sanitizers.defaultSanitizeOutput(\n {\n schema: userSchema,\n getModel: strapi.getModel.bind(strapi),\n },\n user\n );\n\n const confirmationToken = crypto.randomBytes(20).toString('hex');\n\n await this.edit(user.id, { confirmationToken });\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n\n try {\n settings.message = await userPermissionService.template(settings.message, {\n URL: urlJoin(\n strapi.config.get('server.absoluteUrl'),\n apiPrefix,\n '/auth/email-confirmation'\n ),\n SERVER_URL: strapi.config.get('server.absoluteUrl'),\n ADMIN_URL: strapi.config.get('admin.absoluteUrl'),\n USER: sanitizedUserInfo,\n CODE: confirmationToken,\n });\n\n settings.object = await userPermissionService.template(settings.object, {\n USER: sanitizedUserInfo,\n });\n } catch {\n strapi.log.error(\n '[plugin::users-permissions.sendConfirmationEmail]: Failed to generate a template for \"user confirmation email\". Please make sure your email template is valid and does not contain invalid characters or patterns'\n );\n return;\n }\n\n // Send an email to the user.\n await strapi\n .plugin('email')\n .service('email')\n .send({\n to: user.email,\n from:\n settings.from.email && settings.from.name\n ? `${settings.from.name} <${settings.from.email}>`\n : undefined,\n replyTo: settings.response_email,\n subject: settings.object,\n text: settings.message,\n html: settings.message,\n });\n },\n});\n"],"names":["crypto","require$$0","bcrypt","require$$1","urlJoin","require$$2","sanitize","require$$3","toNumber","getOr","require$$4","getService","require$$5","USER_MODEL_UID","user","strapi","count","params","db","query","where","ensureHashedPasswords","values","attributes","getModel","key","type","rounds","hash","add","create","data","populate","edit","userId","update","id","fetch","get","transform","findOne","$and","fetchAuthenticatedUser","fetchAll","findMany","remove","delete","validatePassword","password","compare","sendConfirmationEmail","userPermissionService","pluginStore","store","name","userSchema","settings","then","storeEmail","email_confirmation","options","sanitizedUserInfo","sanitizers","defaultSanitizeOutput","schema","bind","confirmationToken","randomBytes","toString","apiPrefix","config","message","template","URL","SERVER_URL","ADMIN_URL","USER","CODE","object","log","error","plugin","service","send","to","email","from","undefined","replyTo","response_email","subject","text","html"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"user.mjs","sources":["../../../server/services/user.js"],"sourcesContent":["'use strict';\n\n/**\n * User.js service\n *\n * @description: A set of functions similar to controller's actions to avoid code duplication.\n */\n\nconst crypto = require('crypto');\nconst bcrypt = require('bcryptjs');\nconst urlJoin = require('url-join');\n\nconst { sanitize } = require('@strapi/utils');\nconst { toNumber, getOr } = require('lodash/fp');\nconst { getService } = require('../utils');\n\nconst USER_MODEL_UID = 'plugin::users-permissions.user';\n\nconst getSessionManager = () => {\n const manager = strapi.sessionManager;\n return manager ?? null;\n};\n\nmodule.exports = ({ strapi }) => ({\n /**\n * Promise to count users\n *\n * @return {Promise}\n */\n\n count(params) {\n return strapi.db.query(USER_MODEL_UID).count({ where: params });\n },\n\n /**\n * Hashes password fields in the provided values object if they are present.\n * It checks each key in the values object against the model's attributes and\n * hashes it if the attribute type is 'password',\n *\n * @param {object} values - The object containing the fields to be hashed.\n * @return {object} The values object with hashed password fields if they were present.\n */\n async ensureHashedPasswords(values) {\n const attributes = strapi.getModel(USER_MODEL_UID).attributes;\n\n for (const key in values) {\n if (attributes[key] && attributes[key].type === 'password') {\n // Check if a custom encryption.rounds has been set on the password attribute\n const rounds = toNumber(getOr(10, 'encryption.rounds', attributes[key]));\n values[key] = await bcrypt.hash(values[key], rounds);\n }\n }\n\n return values;\n },\n\n /**\n * Promise to add a/an user.\n * @return {Promise}\n */\n async add(values) {\n return strapi.db.query(USER_MODEL_UID).create({\n data: await this.ensureHashedPasswords(values),\n populate: ['role'],\n });\n },\n\n /**\n * Promise to edit a/an user.\n * @param {string} userId\n * @param {object} params\n * @return {Promise}\n */\n async edit(userId, params = {}) {\n return strapi.db.query(USER_MODEL_UID).update({\n where: { id: userId },\n data: await this.ensureHashedPasswords(params),\n populate: ['role'],\n });\n },\n\n /**\n * Promise to fetch a/an user.\n * @return {Promise}\n */\n fetch(id, params) {\n const query = strapi.get('query-params').transform(USER_MODEL_UID, params ?? {});\n\n return strapi.db.query(USER_MODEL_UID).findOne({\n ...query,\n where: {\n $and: [{ id }, query.where || {}],\n },\n });\n },\n\n /**\n * Promise to fetch authenticated user.\n * @return {Promise}\n */\n fetchAuthenticatedUser(id) {\n return strapi.db.query(USER_MODEL_UID).findOne({ where: { id }, populate: ['role'] });\n },\n\n /**\n * Promise to fetch all users.\n * @return {Promise}\n */\n fetchAll(params) {\n const query = strapi.get('query-params').transform(USER_MODEL_UID, params ?? {});\n\n return strapi.db.query(USER_MODEL_UID).findMany(query);\n },\n\n /**\n * Promise to remove a/an user.\n * @return {Promise}\n */\n async remove(params) {\n // Invalidate sessions for all affected users\n const sessionManager = getSessionManager();\n if (sessionManager && sessionManager.hasOrigin('users-permissions') && params.id) {\n await sessionManager('users-permissions').invalidateRefreshToken(String(params.id));\n }\n\n return strapi.db.query(USER_MODEL_UID).delete({ where: params });\n },\n\n validatePassword(password, hash) {\n return bcrypt.compare(password, hash);\n },\n\n async sendConfirmationEmail(user) {\n const userPermissionService = getService('users-permissions');\n const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });\n const userSchema = strapi.getModel(USER_MODEL_UID);\n\n const settings = await pluginStore\n .get({ key: 'email' })\n .then((storeEmail) => storeEmail.email_confirmation.options);\n\n // Sanitize the template's user information\n const sanitizedUserInfo = await sanitize.sanitizers.defaultSanitizeOutput(\n {\n schema: userSchema,\n getModel: strapi.getModel.bind(strapi),\n },\n user\n );\n\n const confirmationToken = crypto.randomBytes(20).toString('hex');\n\n await this.edit(user.id, { confirmationToken });\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n\n try {\n settings.message = await userPermissionService.template(settings.message, {\n URL: urlJoin(\n strapi.config.get('server.absoluteUrl'),\n apiPrefix,\n '/auth/email-confirmation'\n ),\n SERVER_URL: strapi.config.get('server.absoluteUrl'),\n ADMIN_URL: strapi.config.get('admin.absoluteUrl'),\n USER: sanitizedUserInfo,\n CODE: confirmationToken,\n });\n\n settings.object = await userPermissionService.template(settings.object, {\n USER: sanitizedUserInfo,\n });\n } catch {\n strapi.log.error(\n '[plugin::users-permissions.sendConfirmationEmail]: Failed to generate a template for \"user confirmation email\". Please make sure your email template is valid and does not contain invalid characters or patterns'\n );\n return;\n }\n\n // Send an email to the user.\n await strapi\n .plugin('email')\n .service('email')\n .send({\n to: user.email,\n from:\n settings.from.email && settings.from.name\n ? `${settings.from.name} <${settings.from.email}>`\n : undefined,\n replyTo: settings.response_email,\n subject: settings.object,\n text: settings.message,\n html: settings.message,\n });\n },\n});\n"],"names":["crypto","require$$0","bcrypt","require$$1","urlJoin","require$$2","sanitize","require$$3","toNumber","getOr","require$$4","getService","require$$5","USER_MODEL_UID","getSessionManager","manager","strapi","sessionManager","user","count","params","db","query","where","ensureHashedPasswords","values","attributes","getModel","key","type","rounds","hash","add","create","data","populate","edit","userId","update","id","fetch","get","transform","findOne","$and","fetchAuthenticatedUser","fetchAll","findMany","remove","hasOrigin","invalidateRefreshToken","String","delete","validatePassword","password","compare","sendConfirmationEmail","userPermissionService","pluginStore","store","name","userSchema","settings","then","storeEmail","email_confirmation","options","sanitizedUserInfo","sanitizers","defaultSanitizeOutput","schema","bind","confirmationToken","randomBytes","toString","apiPrefix","config","message","template","URL","SERVER_URL","ADMIN_URL","USER","CODE","object","log","error","plugin","service","send","to","email","from","undefined","replyTo","response_email","subject","text","html"],"mappings":";;;;;;;;;;;;AAEA;;;;AAIA,KAEA,MAAMA,MAASC,GAAAA,UAAAA;AACf,IAAA,MAAMC,MAASC,GAAAA,UAAAA;AACf,IAAA,MAAMC,OAAUC,GAAAA,UAAAA;IAEhB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,YAAAA;AACrB,IAAA,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGC,YAAAA;IAC5B,MAAM,EAAEC,UAAU,EAAE,GAAGC,YAAAA,EAAAA;AAEvB,IAAA,MAAMC,cAAiB,GAAA,gCAAA;AAEvB,IAAA,MAAMC,iBAAoB,GAAA,IAAA;QACxB,MAAMC,OAAAA,GAAUC,OAAOC,cAAc;AACrC,QAAA,OAAOF,OAAW,IAAA,IAAA;AACpB,KAAA;AAEAG,IAAAA,IAAAA,GAAiB,CAAC,EAAEF,MAAAA,EAAAA,OAAM,EAAE,IAAM;AAClC;;;;AAIA,OAEEG,OAAMC,MAAM,EAAA;AACV,gBAAA,OAAOJ,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgBM,KAAK,CAAC;oBAAEI,KAAOH,EAAAA;AAAM,iBAAA,CAAA;AAC7D,aAAA;AAEH;;;;;;;OAQE,MAAMI,uBAAsBC,MAAM,EAAA;AAChC,gBAAA,MAAMC,UAAaV,GAAAA,OAAAA,CAAOW,QAAQ,CAACd,gBAAgBa,UAAU;gBAE7D,IAAK,MAAME,OAAOH,MAAQ,CAAA;oBACxB,IAAIC,UAAU,CAACE,GAAAA,CAAI,IAAIF,UAAU,CAACE,GAAI,CAAA,CAACC,IAAI,KAAK,UAAY,EAAA;;AAE1D,wBAAA,MAAMC,SAAStB,QAASC,CAAAA,KAAAA,CAAM,IAAI,mBAAqBiB,EAAAA,UAAU,CAACE,GAAI,CAAA,CAAA,CAAA;wBACtEH,MAAM,CAACG,GAAI,CAAA,GAAG,MAAM1B,MAAAA,CAAO6B,IAAI,CAACN,MAAM,CAACG,GAAAA,CAAI,EAAEE,MAAAA,CAAAA;AAC9C;AACF;gBAED,OAAOL,MAAAA;AACR,aAAA;AAEH;;;OAIE,MAAMO,KAAIP,MAAM,EAAA;AACd,gBAAA,OAAOT,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgBoB,MAAM,CAAC;AAC5CC,oBAAAA,IAAAA,EAAM,MAAM,IAAI,CAACV,qBAAqB,CAACC,MAAAA,CAAAA;oBACvCU,QAAU,EAAA;AAAC,wBAAA;AAAO;AACxB,iBAAA,CAAA;AACG,aAAA;AAEH;;;;;AAKA,OACE,MAAMC,IAAKC,CAAAA,CAAAA,MAAM,EAAEjB,MAAAA,GAAS,EAAE,EAAA;AAC5B,gBAAA,OAAOJ,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgByB,MAAM,CAAC;oBAC5Cf,KAAO,EAAA;wBAAEgB,EAAIF,EAAAA;AAAQ,qBAAA;AACrBH,oBAAAA,IAAAA,EAAM,MAAM,IAAI,CAACV,qBAAqB,CAACJ,MAAAA,CAAAA;oBACvCe,QAAU,EAAA;AAAC,wBAAA;AAAO;AACxB,iBAAA,CAAA;AACG,aAAA;AAEH;;;OAIEK,KAAAA,CAAAA,CAAMD,EAAE,EAAEnB,MAAM,EAAA;gBACd,MAAME,KAAAA,GAAQN,QAAOyB,GAAG,CAAC,gBAAgBC,SAAS,CAAC7B,cAAgBO,EAAAA,MAAAA,IAAU,EAAA,CAAA;AAE7E,gBAAA,OAAOJ,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgB8B,OAAO,CAAC;AAC7C,oBAAA,GAAGrB,KAAK;oBACRC,KAAO,EAAA;wBACLqB,IAAM,EAAA;AAAC,4BAAA;AAAEL,gCAAAA;AAAE,6BAAA;4BAAIjB,KAAMC,CAAAA,KAAK,IAAI;AAAG;AAClC;AACP,iBAAA,CAAA;AACG,aAAA;AAEH;;;AAGA,OACEsB,wBAAuBN,EAAE,EAAA;AACvB,gBAAA,OAAOvB,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgB8B,OAAO,CAAC;oBAAEpB,KAAO,EAAA;AAAEgB,wBAAAA;AAAE,qBAAA;oBAAIJ,QAAU,EAAA;AAAC,wBAAA;AAAO;AAAE,iBAAA,CAAA;AACrF,aAAA;AAEH;;;AAGA,OACEW,UAAS1B,MAAM,EAAA;gBACb,MAAME,KAAAA,GAAQN,QAAOyB,GAAG,CAAC,gBAAgBC,SAAS,CAAC7B,cAAgBO,EAAAA,MAAAA,IAAU,EAAA,CAAA;AAE7E,gBAAA,OAAOJ,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgBkC,QAAQ,CAACzB,KAAAA,CAAAA;AACjD,aAAA;AAEH;;;OAIE,MAAM0B,QAAO5B,MAAM,EAAA;;AAEjB,gBAAA,MAAMH,cAAiBH,GAAAA,iBAAAA,EAAAA;AACvB,gBAAA,IAAIG,kBAAkBA,cAAegC,CAAAA,SAAS,CAAC,mBAAwB7B,CAAAA,IAAAA,MAAAA,CAAOmB,EAAE,EAAE;AAChF,oBAAA,MAAMtB,eAAe,mBAAqBiC,CAAAA,CAAAA,sBAAsB,CAACC,MAAAA,CAAO/B,OAAOmB,EAAE,CAAA,CAAA;AAClF;AAED,gBAAA,OAAOvB,QAAOK,EAAE,CAACC,KAAK,CAACT,cAAAA,CAAAA,CAAgBuC,MAAM,CAAC;oBAAE7B,KAAOH,EAAAA;AAAM,iBAAA,CAAA;AAC9D,aAAA;YAEDiC,gBAAiBC,CAAAA,CAAAA,QAAQ,EAAEvB,IAAI,EAAA;gBAC7B,OAAO7B,MAAAA,CAAOqD,OAAO,CAACD,QAAUvB,EAAAA,IAAAA,CAAAA;AACjC,aAAA;AAED,YAAA,MAAMyB,uBAAsBtC,IAAI,EAAA;AAC9B,gBAAA,MAAMuC,wBAAwB9C,UAAW,CAAA,mBAAA,CAAA;AACzC,gBAAA,MAAM+C,WAAc,GAAA,MAAM1C,OAAO2C,CAAAA,KAAK,CAAC;oBAAE9B,IAAM,EAAA,QAAA;oBAAU+B,IAAM,EAAA;AAAmB,iBAAA,CAAA;gBAClF,MAAMC,UAAAA,GAAa7C,OAAOW,CAAAA,QAAQ,CAACd,cAAAA,CAAAA;AAEnC,gBAAA,MAAMiD,QAAW,GAAA,MAAMJ,WACpBjB,CAAAA,GAAG,CAAC;oBAAEb,GAAK,EAAA;mBACXmC,IAAI,CAAC,CAACC,aAAeA,UAAWC,CAAAA,kBAAkB,CAACC,OAAO,CAAA;;AAG7D,gBAAA,MAAMC,oBAAoB,MAAM7D,QAAAA,CAAS8D,UAAU,CAACC,qBAAqB,CACvE;oBACEC,MAAQT,EAAAA,UAAAA;AACRlC,oBAAAA,QAAAA,EAAUX,OAAOW,CAAAA,QAAQ,CAAC4C,IAAI,CAACvD,OAAAA;iBAEjCE,EAAAA,IAAAA,CAAAA;AAGF,gBAAA,MAAMsD,oBAAoBxE,MAAOyE,CAAAA,WAAW,CAAC,EAAA,CAAA,CAAIC,QAAQ,CAAC,KAAA,CAAA;AAE1D,gBAAA,MAAM,IAAI,CAACtC,IAAI,CAAClB,IAAAA,CAAKqB,EAAE,EAAE;AAAEiC,oBAAAA;AAAiB,iBAAA,CAAA;AAE5C,gBAAA,MAAMG,SAAY3D,GAAAA,OAAAA,CAAO4D,MAAM,CAACnC,GAAG,CAAC,iBAAA,CAAA;gBAEpC,IAAI;oBACFqB,QAASe,CAAAA,OAAO,GAAG,MAAMpB,qBAAAA,CAAsBqB,QAAQ,CAAChB,QAAAA,CAASe,OAAO,EAAE;AACxEE,wBAAAA,GAAAA,EAAK3E,QACHY,OAAO4D,CAAAA,MAAM,CAACnC,GAAG,CAAC,uBAClBkC,SACA,EAAA,0BAAA,CAAA;AAEFK,wBAAAA,UAAAA,EAAYhE,OAAO4D,CAAAA,MAAM,CAACnC,GAAG,CAAC,oBAAA,CAAA;AAC9BwC,wBAAAA,SAAAA,EAAWjE,OAAO4D,CAAAA,MAAM,CAACnC,GAAG,CAAC,mBAAA,CAAA;wBAC7ByC,IAAMf,EAAAA,iBAAAA;wBACNgB,IAAMX,EAAAA;AACd,qBAAA,CAAA;oBAEMV,QAASsB,CAAAA,MAAM,GAAG,MAAM3B,qBAAAA,CAAsBqB,QAAQ,CAAChB,QAAAA,CAASsB,MAAM,EAAE;wBACtEF,IAAMf,EAAAA;AACd,qBAAA,CAAA;AACA,iBAAA,CAAM,OAAM;oBACNnD,OAAOqE,CAAAA,GAAG,CAACC,KAAK,CACd,mNAAA,CAAA;AAEF,oBAAA;AACD;;gBAGD,MAAMtE,OAAAA,CACHuE,MAAM,CAAC,OAAA,CAAA,CACPC,OAAO,CAAC,OAAA,CAAA,CACRC,IAAI,CAAC;AACJC,oBAAAA,EAAAA,EAAIxE,KAAKyE,KAAK;oBACdC,IACE9B,EAAAA,QAAAA,CAAS8B,IAAI,CAACD,KAAK,IAAI7B,QAAS8B,CAAAA,IAAI,CAAChC,IAAI,GACrC,CAAC,EAAEE,QAAAA,CAAS8B,IAAI,CAAChC,IAAI,CAAC,EAAE,EAAEE,QAAAA,CAAS8B,IAAI,CAACD,KAAK,CAAC,CAAC,CAAC,GAChDE,SAAAA;AACNC,oBAAAA,OAAAA,EAAShC,SAASiC,cAAc;AAChCC,oBAAAA,OAAAA,EAASlC,SAASsB,MAAM;AACxBa,oBAAAA,IAAAA,EAAMnC,SAASe,OAAO;AACtBqB,oBAAAA,IAAAA,EAAMpC,SAASe;AACvB,iBAAA,CAAA;AACG;SACH,CAAA;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "0.0.0-next.
|
|
3
|
+
"version": "0.0.0-next.da19c0501ff87d14fb664b55b8e0630d3c548485",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"watch": "run -T rollup -c -w"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@strapi/design-system": "2.0.0-rc.
|
|
52
|
-
"@strapi/icons": "2.0.0-rc.
|
|
53
|
-
"@strapi/utils": "0.0.0-next.
|
|
51
|
+
"@strapi/design-system": "2.0.0-rc.30",
|
|
52
|
+
"@strapi/icons": "2.0.0-rc.30",
|
|
53
|
+
"@strapi/utils": "0.0.0-next.da19c0501ff87d14fb664b55b8e0630d3c548485",
|
|
54
54
|
"bcryptjs": "2.4.3",
|
|
55
55
|
"formik": "2.4.5",
|
|
56
56
|
"grant": "^5.4.8",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"zod": "3.25.67"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@strapi/strapi": "0.0.0-next.
|
|
73
|
+
"@strapi/strapi": "0.0.0-next.da19c0501ff87d14fb664b55b8e0630d3c548485",
|
|
74
74
|
"@testing-library/dom": "10.1.0",
|
|
75
75
|
"@testing-library/react": "15.0.7",
|
|
76
76
|
"@testing-library/user-event": "14.5.2",
|
|
@@ -11,6 +11,18 @@ const crypto = require('crypto');
|
|
|
11
11
|
const _ = require('lodash');
|
|
12
12
|
const { getService } = require('../utils');
|
|
13
13
|
const usersPermissionsActions = require('./users-permissions-actions');
|
|
14
|
+
const {
|
|
15
|
+
DEFAULT_ACCESS_TOKEN_LIFESPAN,
|
|
16
|
+
DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
|
|
17
|
+
DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
|
|
18
|
+
DEFAULT_MAX_SESSION_LIFESPAN,
|
|
19
|
+
DEFAULT_IDLE_SESSION_LIFESPAN,
|
|
20
|
+
} = require('../services/constants');
|
|
21
|
+
|
|
22
|
+
const getSessionManager = () => {
|
|
23
|
+
const manager = strapi.sessionManager;
|
|
24
|
+
return manager ?? null;
|
|
25
|
+
};
|
|
14
26
|
|
|
15
27
|
const initGrant = async (pluginStore) => {
|
|
16
28
|
const allProviders = getService('providers-registry').getAll();
|
|
@@ -113,6 +125,25 @@ module.exports = async ({ strapi }) => {
|
|
|
113
125
|
|
|
114
126
|
await getService('users-permissions').initialize();
|
|
115
127
|
|
|
128
|
+
// Define users-permissions origin configuration for sessionManager
|
|
129
|
+
const upConfig = strapi.config.get('plugin::users-permissions');
|
|
130
|
+
const sessionManager = getSessionManager();
|
|
131
|
+
|
|
132
|
+
if (sessionManager) {
|
|
133
|
+
sessionManager.defineOrigin('users-permissions', {
|
|
134
|
+
jwtSecret: upConfig.jwtSecret || strapi.config.get('admin.auth.secret'),
|
|
135
|
+
accessTokenLifespan: upConfig.sessions?.accessTokenLifespan || DEFAULT_ACCESS_TOKEN_LIFESPAN,
|
|
136
|
+
maxRefreshTokenLifespan:
|
|
137
|
+
upConfig.sessions?.maxRefreshTokenLifespan || DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
|
|
138
|
+
idleRefreshTokenLifespan:
|
|
139
|
+
upConfig.sessions?.idleRefreshTokenLifespan || DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
|
|
140
|
+
maxSessionLifespan: upConfig.sessions?.maxSessionLifespan || DEFAULT_MAX_SESSION_LIFESPAN,
|
|
141
|
+
idleSessionLifespan: upConfig.sessions?.idleSessionLifespan || DEFAULT_IDLE_SESSION_LIFESPAN,
|
|
142
|
+
algorithm: upConfig.jwt?.algorithm,
|
|
143
|
+
jwtOptions: upConfig.jwt || {},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
116
147
|
if (!strapi.config.get('plugin::users-permissions.jwtSecret')) {
|
|
117
148
|
if (process.env.NODE_ENV !== 'development') {
|
|
118
149
|
throw new Error(
|
package/server/config.js
CHANGED
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const {
|
|
4
|
+
DEFAULT_ACCESS_TOKEN_LIFESPAN,
|
|
5
|
+
DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
|
|
6
|
+
DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
|
|
7
|
+
DEFAULT_MAX_SESSION_LIFESPAN,
|
|
8
|
+
DEFAULT_IDLE_SESSION_LIFESPAN,
|
|
9
|
+
} = require('./services/constants');
|
|
10
|
+
|
|
3
11
|
module.exports = {
|
|
4
12
|
default: ({ env }) => ({
|
|
5
13
|
jwtSecret: env('JWT_SECRET'),
|
|
6
14
|
jwt: {
|
|
7
15
|
expiresIn: '30d',
|
|
8
16
|
},
|
|
17
|
+
/**
|
|
18
|
+
* JWT management mode for the Content API authentication
|
|
19
|
+
* - "legacy-support": use plugin JWTs (backward compatible)
|
|
20
|
+
* - "refresh": use SessionManager (access/refresh tokens)
|
|
21
|
+
*/
|
|
22
|
+
jwtManagement: 'legacy-support',
|
|
23
|
+
sessions: {
|
|
24
|
+
accessTokenLifespan: DEFAULT_ACCESS_TOKEN_LIFESPAN,
|
|
25
|
+
maxRefreshTokenLifespan: DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
|
|
26
|
+
idleRefreshTokenLifespan: DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
|
|
27
|
+
maxSessionLifespan: DEFAULT_MAX_SESSION_LIFESPAN,
|
|
28
|
+
idleSessionLifespan: DEFAULT_IDLE_SESSION_LIFESPAN,
|
|
29
|
+
httpOnly: false,
|
|
30
|
+
},
|
|
9
31
|
ratelimit: {
|
|
10
32
|
interval: 60000,
|
|
11
33
|
max: 10,
|
|
@@ -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
|
+
};
|