@strapi/plugin-users-permissions 5.49.0 → 5.50.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/.eslintrc.cjs +1 -0
- package/admin/src/pages/AdvancedSettings/index.jsx +1 -1
- package/admin/src/pages/Roles/pages/ListPage/index.jsx +1 -1
- package/admin/src/translations/ja.json +46 -7
- package/dist/admin/pages/AdvancedSettings/index.js +1 -1
- package/dist/admin/pages/AdvancedSettings/index.js.map +1 -1
- package/dist/admin/pages/AdvancedSettings/index.mjs +1 -1
- package/dist/admin/pages/AdvancedSettings/index.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
- package/dist/admin/translations/ja.json.js +46 -7
- package/dist/admin/translations/ja.json.js.map +1 -1
- package/dist/admin/translations/ja.json.mjs +46 -7
- package/dist/admin/translations/ja.json.mjs.map +1 -1
- package/dist/server/controllers/auth.js +133 -114
- package/dist/server/controllers/auth.js.map +1 -1
- package/dist/server/controllers/auth.mjs +132 -113
- package/dist/server/controllers/auth.mjs.map +1 -1
- package/dist/server/controllers/validation/user.js +16 -9
- package/dist/server/controllers/validation/user.js.map +1 -1
- package/dist/server/controllers/validation/user.mjs +16 -9
- package/dist/server/controllers/validation/user.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 +29 -2
- package/dist/server/routes/content-api/validation.js.map +1 -1
- package/dist/server/routes/content-api/validation.mjs +29 -2
- package/dist/server/routes/content-api/validation.mjs.map +1 -1
- package/dist/server/services/jwt.js +8 -6
- package/dist/server/services/jwt.js.map +1 -1
- package/dist/server/services/jwt.mjs +8 -6
- package/dist/server/services/jwt.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/dist/server/strategies/users-permissions.js +8 -1
- package/dist/server/strategies/users-permissions.js.map +1 -1
- package/dist/server/strategies/users-permissions.mjs +8 -1
- package/dist/server/strategies/users-permissions.mjs.map +1 -1
- package/dist/server/utils/refresh-cookie-options.js +27 -0
- package/dist/server/utils/refresh-cookie-options.js.map +1 -0
- package/dist/server/utils/refresh-cookie-options.mjs +25 -0
- package/dist/server/utils/refresh-cookie-options.mjs.map +1 -0
- package/lint-staged.config.mjs +1 -0
- package/package.json +5 -5
- package/rollup.config.mjs +3 -3
- package/server/controllers/auth.js +162 -141
- package/server/controllers/validation/user.js +22 -9
- package/server/routes/content-api/auth.js +12 -0
- package/server/routes/content-api/validation.js +32 -2
- package/server/services/jwt.js +4 -3
- package/server/services/users-permissions.js +2 -0
- package/server/strategies/users-permissions.js +6 -1
- package/server/utils/refresh-cookie-options.js +20 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users-permissions.js","sources":["../../../server/strategies/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst { castArray, map, every, pipe } = require('lodash/fp');\nconst { ForbiddenError, UnauthorizedError } = require('@strapi/utils').errors;\n\nconst { getService } = require('../utils');\n\nconst getAdvancedSettings = () => {\n return strapi.store({ type: 'plugin', name: 'users-permissions' }).get({ key: 'advanced' });\n};\n\nconst authenticate = async (ctx) => {\n try {\n const token = await getService('jwt').getToken(ctx);\n\n if (token) {\n const { id } = token;\n\n // Invalid token\n if (id === undefined) {\n return { authenticated: false };\n }\n\n const user = await getService('user').fetchAuthenticatedUser(id);\n\n // No user associated to the token\n if (!user) {\n return { error: 'Invalid credentials' };\n }\n\n const advancedSettings = await getAdvancedSettings();\n\n // User not confirmed\n if (advancedSettings.email_confirmation && !user.confirmed) {\n return { error: 'Invalid credentials' };\n }\n\n // User blocked\n if (user.blocked) {\n return { error: 'Invalid credentials' };\n }\n\n // Fetch user's permissions\n const permissions = await Promise.resolve(user.role.id)\n .then(getService('permission').findRolePermissions)\n .then(map(getService('permission').toContentAPIPermission));\n\n // Generate an ability (content API engine) based on the given permissions\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);\n\n ctx.state.user = user;\n\n return {\n authenticated: true,\n credentials: user,\n ability,\n };\n }\n\n const publicPermissions = await getService('permission')\n .findPublicPermissions()\n .then(map(getService('permission').toContentAPIPermission));\n\n if (publicPermissions.length === 0) {\n return { authenticated: false };\n }\n\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(publicPermissions);\n\n return {\n authenticated: true,\n credentials: null,\n ability,\n };\n } catch (err) {\n return { authenticated: false };\n }\n};\n\nconst verify = async (auth, config) => {\n const { credentials: user, ability } = auth;\n\n if (!config.scope) {\n if (!user) {\n // A non authenticated user cannot access routes that do not have a scope\n throw new UnauthorizedError();\n } else {\n // An authenticated user can access non scoped routes\n return;\n }\n }\n\n // If no ability have been generated, then consider auth is missing\n if (!ability) {\n throw new UnauthorizedError();\n }\n\n const isAllowed = pipe(\n // Make sure we're dealing with an array\n castArray,\n // Transform the scope array into an action array\n every((scope) => ability.can(scope))\n )(config.scope);\n\n if (!isAllowed) {\n throw new ForbiddenError();\n }\n};\n\nmodule.exports = {\n name: 'users-permissions',\n authenticate,\n verify,\n};\n"],"names":["castArray","map","every","pipe","require$$0","ForbiddenError","UnauthorizedError","require$$1","errors","getService","require$$2","getAdvancedSettings","strapi","store","type","name","get","key","authenticate","ctx","token","getToken","id","undefined","authenticated","user","fetchAuthenticatedUser","error","advancedSettings","email_confirmation","confirmed","blocked","permissions","Promise","resolve","role","then","findRolePermissions","toContentAPIPermission","ability","contentAPI","engine","generateAbility","state","credentials","publicPermissions","findPublicPermissions","length","err","verify","auth","config","scope","isAllowed","can","usersPermissions"],"mappings":";;;;;;;;;;;;;;;;IAEA,MAAM,EAAEA,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAE,GAAGC,2BAAAA;AACxC,IAAA,MAAM,EAAEC,cAAc,EAAEC,iBAAiB,EAAE,GAAGC,4BAAyBC,MAAM;IAE7E,MAAM,EAAEC,UAAU,EAAE,GAAGC,eAAAA,EAAAA;AAEvB,IAAA,MAAMC,mBAAAA,GAAsB,IAAA;QAC1B,OAAOC,MAAAA,CAAOC,KAAK,CAAC;YAAEC,IAAAA,EAAM,QAAA;YAAUC,IAAAA,EAAM;AAAmB,SAAA,CAAA,CAAIC,GAAG,CAAC;YAAEC,GAAAA,EAAK;AAAU,SAAA,CAAA;AAC1F,IAAA,CAAA;AAEA,IAAA,MAAMC,eAAe,OAAOC,GAAAA,GAAAA;QAC1B,IAAI;AACF,YAAA,MAAMC,KAAAA,GAAQ,MAAMX,UAAAA,CAAW,KAAA,CAAA,CAAOY,QAAQ,CAACF,GAAAA,CAAAA;AAE/C,YAAA,IAAIC,KAAAA,EAAO;
|
|
1
|
+
{"version":3,"file":"users-permissions.js","sources":["../../../server/strategies/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst { castArray, map, every, pipe } = require('lodash/fp');\nconst { ForbiddenError, UnauthorizedError } = require('@strapi/utils').errors;\n\nconst { getService } = require('../utils');\n\nconst getAdvancedSettings = () => {\n return strapi.store({ type: 'plugin', name: 'users-permissions' }).get({ key: 'advanced' });\n};\n\nconst authenticate = async (ctx) => {\n try {\n const token = await getService('jwt').getToken(ctx);\n\n if (token) {\n const { id, sessionId } = token;\n\n // Invalid token\n if (id === undefined) {\n return { authenticated: false };\n }\n\n const user = await getService('user').fetchAuthenticatedUser(id);\n\n // No user associated to the token\n if (!user) {\n return { error: 'Invalid credentials' };\n }\n\n const advancedSettings = await getAdvancedSettings();\n\n // User not confirmed\n if (advancedSettings.email_confirmation && !user.confirmed) {\n return { error: 'Invalid credentials' };\n }\n\n // User blocked\n if (user.blocked) {\n return { error: 'Invalid credentials' };\n }\n\n // Fetch user's permissions\n const permissions = await Promise.resolve(user.role.id)\n .then(getService('permission').findRolePermissions)\n .then(map(getService('permission').toContentAPIPermission));\n\n // Generate an ability (content API engine) based on the given permissions\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);\n\n ctx.state.user = user;\n // Expose the session backing this request (refresh mode only) so endpoints\n // can flag the \"current\" session when listing active sessions.\n if (sessionId) {\n ctx.state.session = { id: sessionId };\n }\n\n return {\n authenticated: true,\n credentials: user,\n ability,\n };\n }\n\n const publicPermissions = await getService('permission')\n .findPublicPermissions()\n .then(map(getService('permission').toContentAPIPermission));\n\n if (publicPermissions.length === 0) {\n return { authenticated: false };\n }\n\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(publicPermissions);\n\n return {\n authenticated: true,\n credentials: null,\n ability,\n };\n } catch (err) {\n return { authenticated: false };\n }\n};\n\nconst verify = async (auth, config) => {\n const { credentials: user, ability } = auth;\n\n if (!config.scope) {\n if (!user) {\n // A non authenticated user cannot access routes that do not have a scope\n throw new UnauthorizedError();\n } else {\n // An authenticated user can access non scoped routes\n return;\n }\n }\n\n // If no ability have been generated, then consider auth is missing\n if (!ability) {\n throw new UnauthorizedError();\n }\n\n const isAllowed = pipe(\n // Make sure we're dealing with an array\n castArray,\n // Transform the scope array into an action array\n every((scope) => ability.can(scope))\n )(config.scope);\n\n if (!isAllowed) {\n throw new ForbiddenError();\n }\n};\n\nmodule.exports = {\n name: 'users-permissions',\n authenticate,\n verify,\n};\n"],"names":["castArray","map","every","pipe","require$$0","ForbiddenError","UnauthorizedError","require$$1","errors","getService","require$$2","getAdvancedSettings","strapi","store","type","name","get","key","authenticate","ctx","token","getToken","id","sessionId","undefined","authenticated","user","fetchAuthenticatedUser","error","advancedSettings","email_confirmation","confirmed","blocked","permissions","Promise","resolve","role","then","findRolePermissions","toContentAPIPermission","ability","contentAPI","engine","generateAbility","state","session","credentials","publicPermissions","findPublicPermissions","length","err","verify","auth","config","scope","isAllowed","can","usersPermissions"],"mappings":";;;;;;;;;;;;;;;;IAEA,MAAM,EAAEA,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAE,GAAGC,2BAAAA;AACxC,IAAA,MAAM,EAAEC,cAAc,EAAEC,iBAAiB,EAAE,GAAGC,4BAAyBC,MAAM;IAE7E,MAAM,EAAEC,UAAU,EAAE,GAAGC,eAAAA,EAAAA;AAEvB,IAAA,MAAMC,mBAAAA,GAAsB,IAAA;QAC1B,OAAOC,MAAAA,CAAOC,KAAK,CAAC;YAAEC,IAAAA,EAAM,QAAA;YAAUC,IAAAA,EAAM;AAAmB,SAAA,CAAA,CAAIC,GAAG,CAAC;YAAEC,GAAAA,EAAK;AAAU,SAAA,CAAA;AAC1F,IAAA,CAAA;AAEA,IAAA,MAAMC,eAAe,OAAOC,GAAAA,GAAAA;QAC1B,IAAI;AACF,YAAA,MAAMC,KAAAA,GAAQ,MAAMX,UAAAA,CAAW,KAAA,CAAA,CAAOY,QAAQ,CAACF,GAAAA,CAAAA;AAE/C,YAAA,IAAIC,KAAAA,EAAO;AACT,gBAAA,MAAM,EAAEE,EAAE,EAAEC,SAAS,EAAE,GAAGH,KAAAA;;AAG1B,gBAAA,IAAIE,OAAOE,SAAAA,EAAW;oBACpB,OAAO;wBAAEC,aAAAA,EAAe;;AAChC,gBAAA;AAEM,gBAAA,MAAMC,IAAAA,GAAO,MAAMjB,UAAAA,CAAW,MAAA,CAAA,CAAQkB,sBAAsB,CAACL,EAAAA,CAAAA;;AAG7D,gBAAA,IAAI,CAACI,IAAAA,EAAM;oBACT,OAAO;wBAAEE,KAAAA,EAAO;;AACxB,gBAAA;AAEM,gBAAA,MAAMC,mBAAmB,MAAMlB,mBAAAA,EAAAA;;AAG/B,gBAAA,IAAIkB,iBAAiBC,kBAAkB,IAAI,CAACJ,IAAAA,CAAKK,SAAS,EAAE;oBAC1D,OAAO;wBAAEH,KAAAA,EAAO;;AACxB,gBAAA;;gBAGM,IAAIF,IAAAA,CAAKM,OAAO,EAAE;oBAChB,OAAO;wBAAEJ,KAAAA,EAAO;;AACxB,gBAAA;;gBAGM,MAAMK,WAAAA,GAAc,MAAMC,OAAAA,CAAQC,OAAO,CAACT,IAAAA,CAAKU,IAAI,CAACd,EAAE,CAAA,CACnDe,IAAI,CAAC5B,UAAAA,CAAW,cAAc6B,mBAAmB,CAAA,CACjDD,IAAI,CAACpC,GAAAA,CAAIQ,UAAAA,CAAW,YAAA,CAAA,CAAc8B,sBAAsB,CAAA,CAAA;;gBAG3D,MAAMC,OAAAA,GAAU,MAAM5B,MAAAA,CAAO6B,UAAU,CAACR,WAAW,CAACS,MAAM,CAACC,eAAe,CAACV,WAAAA,CAAAA;gBAE3Ed,GAAAA,CAAIyB,KAAK,CAAClB,IAAI,GAAGA,IAAAA;;;AAGjB,gBAAA,IAAIH,SAAAA,EAAW;oBACbJ,GAAAA,CAAIyB,KAAK,CAACC,OAAO,GAAG;wBAAEvB,EAAAA,EAAIC;AAAS,qBAAA;AAC3C,gBAAA;gBAEM,OAAO;oBACLE,aAAAA,EAAe,IAAA;oBACfqB,WAAAA,EAAapB,IAAAA;AACbc,oBAAAA;AACR,iBAAA;AACA,YAAA;YAEI,MAAMO,iBAAAA,GAAoB,MAAMtC,UAAAA,CAAW,YAAA,CAAA,CACxCuC,qBAAqB,EAAA,CACrBX,IAAI,CAACpC,GAAAA,CAAIQ,UAAAA,CAAW,YAAA,CAAA,CAAc8B,sBAAsB,CAAA,CAAA;YAE3D,IAAIQ,iBAAAA,CAAkBE,MAAM,KAAK,CAAA,EAAG;gBAClC,OAAO;oBAAExB,aAAAA,EAAe;;AAC9B,YAAA;YAEI,MAAMe,OAAAA,GAAU,MAAM5B,MAAAA,CAAO6B,UAAU,CAACR,WAAW,CAACS,MAAM,CAACC,eAAe,CAACI,iBAAAA,CAAAA;YAE3E,OAAO;gBACLtB,aAAAA,EAAe,IAAA;gBACfqB,WAAAA,EAAa,IAAA;AACbN,gBAAAA;AACN,aAAA;AACA,QAAA,CAAA,CAAI,OAAOU,GAAAA,EAAK;YACZ,OAAO;gBAAEzB,aAAAA,EAAe;;AAC5B,QAAA;AACA,IAAA,CAAA;IAEA,MAAM0B,MAAAA,GAAS,OAAOC,IAAAA,EAAMC,MAAAA,GAAAA;AAC1B,QAAA,MAAM,EAAEP,WAAAA,EAAapB,IAAI,EAAEc,OAAO,EAAE,GAAGY,IAAAA;QAEvC,IAAI,CAACC,MAAAA,CAAOC,KAAK,EAAE;AACjB,YAAA,IAAI,CAAC5B,IAAAA,EAAM;;AAET,gBAAA,MAAM,IAAIpB,iBAAAA,EAAAA;YAChB,CAAA,MAAW;;AAEL,gBAAA;AACN,YAAA;AACA,QAAA;;AAGE,QAAA,IAAI,CAACkC,OAAAA,EAAS;AACZ,YAAA,MAAM,IAAIlC,iBAAAA,EAAAA;AACd,QAAA;QAEE,MAAMiD,SAAAA,GAAYpD;AAEhBH,QAAAA,SAAAA;AAEAE,QAAAA,KAAAA,CAAM,CAACoD,KAAAA,GAAUd,OAAAA,CAAQgB,GAAG,CAACF,KAAAA,CAAAA,CAAAA,CAAAA,CAC7BD,OAAOC,KAAK,CAAA;AAEd,QAAA,IAAI,CAACC,SAAAA,EAAW;AACd,YAAA,MAAM,IAAIlD,cAAAA,EAAAA;AACd,QAAA;AACA,IAAA,CAAA;IAEAoD,gBAAAA,GAAiB;QACf1C,IAAAA,EAAM,mBAAA;AACNG,QAAAA,YAAAA;AACAiC,QAAAA;AACF,KAAA;;;;;;"}
|
|
@@ -22,7 +22,7 @@ function requireUsersPermissions() {
|
|
|
22
22
|
try {
|
|
23
23
|
const token = await getService('jwt').getToken(ctx);
|
|
24
24
|
if (token) {
|
|
25
|
-
const { id } = token;
|
|
25
|
+
const { id, sessionId } = token;
|
|
26
26
|
// Invalid token
|
|
27
27
|
if (id === undefined) {
|
|
28
28
|
return {
|
|
@@ -54,6 +54,13 @@ function requireUsersPermissions() {
|
|
|
54
54
|
// Generate an ability (content API engine) based on the given permissions
|
|
55
55
|
const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);
|
|
56
56
|
ctx.state.user = user;
|
|
57
|
+
// Expose the session backing this request (refresh mode only) so endpoints
|
|
58
|
+
// can flag the "current" session when listing active sessions.
|
|
59
|
+
if (sessionId) {
|
|
60
|
+
ctx.state.session = {
|
|
61
|
+
id: sessionId
|
|
62
|
+
};
|
|
63
|
+
}
|
|
57
64
|
return {
|
|
58
65
|
authenticated: true,
|
|
59
66
|
credentials: user,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users-permissions.mjs","sources":["../../../server/strategies/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst { castArray, map, every, pipe } = require('lodash/fp');\nconst { ForbiddenError, UnauthorizedError } = require('@strapi/utils').errors;\n\nconst { getService } = require('../utils');\n\nconst getAdvancedSettings = () => {\n return strapi.store({ type: 'plugin', name: 'users-permissions' }).get({ key: 'advanced' });\n};\n\nconst authenticate = async (ctx) => {\n try {\n const token = await getService('jwt').getToken(ctx);\n\n if (token) {\n const { id } = token;\n\n // Invalid token\n if (id === undefined) {\n return { authenticated: false };\n }\n\n const user = await getService('user').fetchAuthenticatedUser(id);\n\n // No user associated to the token\n if (!user) {\n return { error: 'Invalid credentials' };\n }\n\n const advancedSettings = await getAdvancedSettings();\n\n // User not confirmed\n if (advancedSettings.email_confirmation && !user.confirmed) {\n return { error: 'Invalid credentials' };\n }\n\n // User blocked\n if (user.blocked) {\n return { error: 'Invalid credentials' };\n }\n\n // Fetch user's permissions\n const permissions = await Promise.resolve(user.role.id)\n .then(getService('permission').findRolePermissions)\n .then(map(getService('permission').toContentAPIPermission));\n\n // Generate an ability (content API engine) based on the given permissions\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);\n\n ctx.state.user = user;\n\n return {\n authenticated: true,\n credentials: user,\n ability,\n };\n }\n\n const publicPermissions = await getService('permission')\n .findPublicPermissions()\n .then(map(getService('permission').toContentAPIPermission));\n\n if (publicPermissions.length === 0) {\n return { authenticated: false };\n }\n\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(publicPermissions);\n\n return {\n authenticated: true,\n credentials: null,\n ability,\n };\n } catch (err) {\n return { authenticated: false };\n }\n};\n\nconst verify = async (auth, config) => {\n const { credentials: user, ability } = auth;\n\n if (!config.scope) {\n if (!user) {\n // A non authenticated user cannot access routes that do not have a scope\n throw new UnauthorizedError();\n } else {\n // An authenticated user can access non scoped routes\n return;\n }\n }\n\n // If no ability have been generated, then consider auth is missing\n if (!ability) {\n throw new UnauthorizedError();\n }\n\n const isAllowed = pipe(\n // Make sure we're dealing with an array\n castArray,\n // Transform the scope array into an action array\n every((scope) => ability.can(scope))\n )(config.scope);\n\n if (!isAllowed) {\n throw new ForbiddenError();\n }\n};\n\nmodule.exports = {\n name: 'users-permissions',\n authenticate,\n verify,\n};\n"],"names":["castArray","map","every","pipe","require$$0","ForbiddenError","UnauthorizedError","require$$1","errors","getService","require$$2","getAdvancedSettings","strapi","store","type","name","get","key","authenticate","ctx","token","getToken","id","undefined","authenticated","user","fetchAuthenticatedUser","error","advancedSettings","email_confirmation","confirmed","blocked","permissions","Promise","resolve","role","then","findRolePermissions","toContentAPIPermission","ability","contentAPI","engine","generateAbility","state","credentials","publicPermissions","findPublicPermissions","length","err","verify","auth","config","scope","isAllowed","can","usersPermissions"],"mappings":";;;;;;;;;IAEA,MAAM,EAAEA,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAE,GAAGC,UAAAA;AACxC,IAAA,MAAM,EAAEC,cAAc,EAAEC,iBAAiB,EAAE,GAAGC,WAAyBC,MAAM;IAE7E,MAAM,EAAEC,UAAU,EAAE,GAAGC,YAAAA,EAAAA;AAEvB,IAAA,MAAMC,mBAAAA,GAAsB,IAAA;QAC1B,OAAOC,MAAAA,CAAOC,KAAK,CAAC;YAAEC,IAAAA,EAAM,QAAA;YAAUC,IAAAA,EAAM;AAAmB,SAAA,CAAA,CAAIC,GAAG,CAAC;YAAEC,GAAAA,EAAK;AAAU,SAAA,CAAA;AAC1F,IAAA,CAAA;AAEA,IAAA,MAAMC,eAAe,OAAOC,GAAAA,GAAAA;QAC1B,IAAI;AACF,YAAA,MAAMC,KAAAA,GAAQ,MAAMX,UAAAA,CAAW,KAAA,CAAA,CAAOY,QAAQ,CAACF,GAAAA,CAAAA;AAE/C,YAAA,IAAIC,KAAAA,EAAO;
|
|
1
|
+
{"version":3,"file":"users-permissions.mjs","sources":["../../../server/strategies/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst { castArray, map, every, pipe } = require('lodash/fp');\nconst { ForbiddenError, UnauthorizedError } = require('@strapi/utils').errors;\n\nconst { getService } = require('../utils');\n\nconst getAdvancedSettings = () => {\n return strapi.store({ type: 'plugin', name: 'users-permissions' }).get({ key: 'advanced' });\n};\n\nconst authenticate = async (ctx) => {\n try {\n const token = await getService('jwt').getToken(ctx);\n\n if (token) {\n const { id, sessionId } = token;\n\n // Invalid token\n if (id === undefined) {\n return { authenticated: false };\n }\n\n const user = await getService('user').fetchAuthenticatedUser(id);\n\n // No user associated to the token\n if (!user) {\n return { error: 'Invalid credentials' };\n }\n\n const advancedSettings = await getAdvancedSettings();\n\n // User not confirmed\n if (advancedSettings.email_confirmation && !user.confirmed) {\n return { error: 'Invalid credentials' };\n }\n\n // User blocked\n if (user.blocked) {\n return { error: 'Invalid credentials' };\n }\n\n // Fetch user's permissions\n const permissions = await Promise.resolve(user.role.id)\n .then(getService('permission').findRolePermissions)\n .then(map(getService('permission').toContentAPIPermission));\n\n // Generate an ability (content API engine) based on the given permissions\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);\n\n ctx.state.user = user;\n // Expose the session backing this request (refresh mode only) so endpoints\n // can flag the \"current\" session when listing active sessions.\n if (sessionId) {\n ctx.state.session = { id: sessionId };\n }\n\n return {\n authenticated: true,\n credentials: user,\n ability,\n };\n }\n\n const publicPermissions = await getService('permission')\n .findPublicPermissions()\n .then(map(getService('permission').toContentAPIPermission));\n\n if (publicPermissions.length === 0) {\n return { authenticated: false };\n }\n\n const ability = await strapi.contentAPI.permissions.engine.generateAbility(publicPermissions);\n\n return {\n authenticated: true,\n credentials: null,\n ability,\n };\n } catch (err) {\n return { authenticated: false };\n }\n};\n\nconst verify = async (auth, config) => {\n const { credentials: user, ability } = auth;\n\n if (!config.scope) {\n if (!user) {\n // A non authenticated user cannot access routes that do not have a scope\n throw new UnauthorizedError();\n } else {\n // An authenticated user can access non scoped routes\n return;\n }\n }\n\n // If no ability have been generated, then consider auth is missing\n if (!ability) {\n throw new UnauthorizedError();\n }\n\n const isAllowed = pipe(\n // Make sure we're dealing with an array\n castArray,\n // Transform the scope array into an action array\n every((scope) => ability.can(scope))\n )(config.scope);\n\n if (!isAllowed) {\n throw new ForbiddenError();\n }\n};\n\nmodule.exports = {\n name: 'users-permissions',\n authenticate,\n verify,\n};\n"],"names":["castArray","map","every","pipe","require$$0","ForbiddenError","UnauthorizedError","require$$1","errors","getService","require$$2","getAdvancedSettings","strapi","store","type","name","get","key","authenticate","ctx","token","getToken","id","sessionId","undefined","authenticated","user","fetchAuthenticatedUser","error","advancedSettings","email_confirmation","confirmed","blocked","permissions","Promise","resolve","role","then","findRolePermissions","toContentAPIPermission","ability","contentAPI","engine","generateAbility","state","session","credentials","publicPermissions","findPublicPermissions","length","err","verify","auth","config","scope","isAllowed","can","usersPermissions"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var refreshCookieOptions;
|
|
4
|
+
var hasRequiredRefreshCookieOptions;
|
|
5
|
+
function requireRefreshCookieOptions() {
|
|
6
|
+
if (hasRequiredRefreshCookieOptions) return refreshCookieOptions;
|
|
7
|
+
hasRequiredRefreshCookieOptions = 1;
|
|
8
|
+
const buildRefreshCookieOptions = (upSessions, isProduction)=>{
|
|
9
|
+
const isSecure = typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
|
|
10
|
+
return {
|
|
11
|
+
httpOnly: true,
|
|
12
|
+
secure: isSecure,
|
|
13
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
14
|
+
path: upSessions.cookie?.path ?? '/',
|
|
15
|
+
domain: upSessions.cookie?.domain,
|
|
16
|
+
maxAge: upSessions.cookie?.maxAge,
|
|
17
|
+
overwrite: true
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
refreshCookieOptions = {
|
|
21
|
+
buildRefreshCookieOptions
|
|
22
|
+
};
|
|
23
|
+
return refreshCookieOptions;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.__require = requireRefreshCookieOptions;
|
|
27
|
+
//# sourceMappingURL=refresh-cookie-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-cookie-options.js","sources":["../../../server/utils/refresh-cookie-options.js"],"sourcesContent":["'use strict';\n\nconst buildRefreshCookieOptions = (upSessions, isProduction) => {\n const isSecure =\n typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;\n\n return {\n httpOnly: true,\n secure: isSecure,\n sameSite: upSessions.cookie?.sameSite ?? 'lax',\n path: upSessions.cookie?.path ?? '/',\n domain: upSessions.cookie?.domain,\n maxAge: upSessions.cookie?.maxAge,\n overwrite: true,\n };\n};\n\nmodule.exports = {\n buildRefreshCookieOptions,\n};\n"],"names":["buildRefreshCookieOptions","upSessions","isProduction","isSecure","cookie","secure","httpOnly","sameSite","path","domain","maxAge","overwrite","refreshCookieOptions"],"mappings":";;;;;;;IAEA,MAAMA,yBAAAA,GAA4B,CAACC,UAAAA,EAAYC,YAAAA,GAAAA;QAC7C,MAAMC,QAAAA,GACJ,OAAOF,UAAAA,CAAWG,MAAM,EAAEC,WAAW,SAAA,GAAYJ,UAAAA,CAAWG,MAAM,EAAEC,MAAAA,GAASH,YAAAA;QAE/E,OAAO;YACLI,QAAAA,EAAU,IAAA;YACVD,MAAAA,EAAQF,QAAAA;YACRI,QAAAA,EAAUN,UAAAA,CAAWG,MAAM,EAAEG,QAAAA,IAAY,KAAA;YACzCC,IAAAA,EAAMP,UAAAA,CAAWG,MAAM,EAAEI,IAAAA,IAAQ,GAAA;YACjCC,MAAAA,EAAQR,UAAAA,CAAWG,MAAM,EAAEK,MAAAA;YAC3BC,MAAAA,EAAQT,UAAAA,CAAWG,MAAM,EAAEM,MAAAA;YAC3BC,SAAAA,EAAW;AACf,SAAA;AACA,IAAA,CAAA;IAEAC,oBAAAA,GAAiB;AACfZ,QAAAA;AACF,KAAA;;;;;;"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var refreshCookieOptions;
|
|
2
|
+
var hasRequiredRefreshCookieOptions;
|
|
3
|
+
function requireRefreshCookieOptions() {
|
|
4
|
+
if (hasRequiredRefreshCookieOptions) return refreshCookieOptions;
|
|
5
|
+
hasRequiredRefreshCookieOptions = 1;
|
|
6
|
+
const buildRefreshCookieOptions = (upSessions, isProduction)=>{
|
|
7
|
+
const isSecure = typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
|
|
8
|
+
return {
|
|
9
|
+
httpOnly: true,
|
|
10
|
+
secure: isSecure,
|
|
11
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
12
|
+
path: upSessions.cookie?.path ?? '/',
|
|
13
|
+
domain: upSessions.cookie?.domain,
|
|
14
|
+
maxAge: upSessions.cookie?.maxAge,
|
|
15
|
+
overwrite: true
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
refreshCookieOptions = {
|
|
19
|
+
buildRefreshCookieOptions
|
|
20
|
+
};
|
|
21
|
+
return refreshCookieOptions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { requireRefreshCookieOptions as __require };
|
|
25
|
+
//# sourceMappingURL=refresh-cookie-options.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-cookie-options.mjs","sources":["../../../server/utils/refresh-cookie-options.js"],"sourcesContent":["'use strict';\n\nconst buildRefreshCookieOptions = (upSessions, isProduction) => {\n const isSecure =\n typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;\n\n return {\n httpOnly: true,\n secure: isSecure,\n sameSite: upSessions.cookie?.sameSite ?? 'lax',\n path: upSessions.cookie?.path ?? '/',\n domain: upSessions.cookie?.domain,\n maxAge: upSessions.cookie?.maxAge,\n overwrite: true,\n };\n};\n\nmodule.exports = {\n buildRefreshCookieOptions,\n};\n"],"names":["buildRefreshCookieOptions","upSessions","isProduction","isSecure","cookie","secure","httpOnly","sameSite","path","domain","maxAge","overwrite","refreshCookieOptions"],"mappings":";;;;;IAEA,MAAMA,yBAAAA,GAA4B,CAACC,UAAAA,EAAYC,YAAAA,GAAAA;QAC7C,MAAMC,QAAAA,GACJ,OAAOF,UAAAA,CAAWG,MAAM,EAAEC,WAAW,SAAA,GAAYJ,UAAAA,CAAWG,MAAM,EAAEC,MAAAA,GAASH,YAAAA;QAE/E,OAAO;YACLI,QAAAA,EAAU,IAAA;YACVD,MAAAA,EAAQF,QAAAA;YACRI,QAAAA,EAAUN,UAAAA,CAAWG,MAAM,EAAEG,QAAAA,IAAY,KAAA;YACzCC,IAAAA,EAAMP,UAAAA,CAAWG,MAAM,EAAEI,IAAAA,IAAQ,GAAA;YACjCC,MAAAA,EAAQR,UAAAA,CAAWG,MAAM,EAAEK,MAAAA;YAC3BC,MAAAA,EAAQT,UAAAA,CAAWG,MAAM,EAAEM,MAAAA;YAC3BC,SAAAA,EAAW;AACf,SAAA;AACA,IAAA,CAAA;IAEAC,oBAAAA,GAAiB;AACfZ,QAAAA;AACF,KAAA;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../../../lint-staged.shared.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.50.0",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"homepage": "https://strapi.io",
|
|
6
6
|
"bugs": {
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"watch": "run -T rollup -c -w"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@strapi/design-system": "2.2.
|
|
57
|
-
"@strapi/icons": "2.2.
|
|
58
|
-
"@strapi/utils": "5.
|
|
56
|
+
"@strapi/design-system": "2.2.1",
|
|
57
|
+
"@strapi/icons": "2.2.1",
|
|
58
|
+
"@strapi/utils": "5.50.0",
|
|
59
59
|
"bcryptjs": "2.4.3",
|
|
60
60
|
"formik": "2.4.5",
|
|
61
61
|
"grant": "5.4.24",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"zod": "3.25.67"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@strapi/strapi": "5.
|
|
78
|
+
"@strapi/strapi": "5.50.0",
|
|
79
79
|
"@testing-library/dom": "10.4.1",
|
|
80
80
|
"@testing-library/react": "16.3.2",
|
|
81
81
|
"@testing-library/user-event": "14.6.1",
|
package/rollup.config.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineConfig } from 'rollup';
|
|
2
|
-
import {
|
|
2
|
+
import { baseConfig } from '../../../rollup.utils.mjs';
|
|
3
3
|
|
|
4
4
|
export default defineConfig([
|
|
5
5
|
baseConfig({
|
|
@@ -11,9 +11,9 @@ export default defineConfig([
|
|
|
11
11
|
}),
|
|
12
12
|
baseConfig({
|
|
13
13
|
input: {
|
|
14
|
-
index: './server/index.js'
|
|
14
|
+
index: './server/index.js',
|
|
15
15
|
},
|
|
16
16
|
rootDir: './server',
|
|
17
17
|
outDir: './dist/server',
|
|
18
|
-
})
|
|
18
|
+
}),
|
|
19
19
|
]);
|
|
@@ -12,6 +12,7 @@ const _ = require('lodash');
|
|
|
12
12
|
const { concat, compact, isArray } = require('lodash/fp');
|
|
13
13
|
const utils = require('@strapi/utils');
|
|
14
14
|
const { getService } = require('../utils');
|
|
15
|
+
const { buildRefreshCookieOptions } = require('../utils/refresh-cookie-options');
|
|
15
16
|
const {
|
|
16
17
|
validateCallbackBody,
|
|
17
18
|
validateRegisterBody,
|
|
@@ -23,6 +24,7 @@ const {
|
|
|
23
24
|
} = require('./validation/auth');
|
|
24
25
|
|
|
25
26
|
const { ApplicationError, ValidationError, ForbiddenError } = utils.errors;
|
|
27
|
+
const { buildSessionMetadata, sanitizeSessionEntry, sortSessionsForDisplay } = utils;
|
|
26
28
|
|
|
27
29
|
const sanitizeUser = (user, ctx) => {
|
|
28
30
|
const { auth } = ctx.state;
|
|
@@ -37,6 +39,102 @@ const extractDeviceId = (requestBody) => {
|
|
|
37
39
|
return typeof deviceId === 'string' && deviceId.length > 0 ? deviceId : undefined;
|
|
38
40
|
};
|
|
39
41
|
|
|
42
|
+
const buildSessionMetadataFromContext = (ctx) =>
|
|
43
|
+
buildSessionMetadata({
|
|
44
|
+
userAgent: ctx.request.headers['user-agent'],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const sendRefreshAuthResponse = async (strapi, ctx, user, { metadata } = {}) => {
|
|
48
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
49
|
+
const tokenOptions = { type: 'refresh', ...(metadata ? { metadata } : {}) };
|
|
50
|
+
|
|
51
|
+
const refresh = await strapi
|
|
52
|
+
.sessionManager('users-permissions')
|
|
53
|
+
.generateRefreshToken(String(user.id), deviceId, tokenOptions);
|
|
54
|
+
|
|
55
|
+
const access = await strapi
|
|
56
|
+
.sessionManager('users-permissions')
|
|
57
|
+
.generateAccessToken(refresh.token);
|
|
58
|
+
if ('error' in access) {
|
|
59
|
+
throw new ApplicationError('Invalid credentials');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
63
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
64
|
+
|
|
65
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
66
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
67
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
68
|
+
ctx.cookies.set(cookieName, refresh.token, buildRefreshCookieOptions(upSessions, isProduction));
|
|
69
|
+
return ctx.send({ jwt: access.token, user: await sanitizeUser(user, ctx) });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return ctx.send({
|
|
73
|
+
jwt: access.token,
|
|
74
|
+
refreshToken: refresh.token,
|
|
75
|
+
user: await sanitizeUser(user, ctx),
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const reissueTokensAfterPasswordChange = async (strapi, ctx, user) => {
|
|
80
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
81
|
+
|
|
82
|
+
await strapi.sessionManager('users-permissions').invalidateRefreshToken(String(user.id));
|
|
83
|
+
|
|
84
|
+
const newDeviceId = deviceId || crypto.randomUUID();
|
|
85
|
+
const refresh = await strapi
|
|
86
|
+
.sessionManager('users-permissions')
|
|
87
|
+
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
88
|
+
|
|
89
|
+
const access = await strapi
|
|
90
|
+
.sessionManager('users-permissions')
|
|
91
|
+
.generateAccessToken(refresh.token);
|
|
92
|
+
if ('error' in access) {
|
|
93
|
+
throw new ApplicationError('Invalid credentials');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return ctx.send({
|
|
97
|
+
jwt: access.token,
|
|
98
|
+
refreshToken: refresh.token,
|
|
99
|
+
user: await sanitizeUser(user, ctx),
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const revokeLogoutSessions = async (strapi, ctx, userId, { scope, deviceId, body }) => {
|
|
104
|
+
const sessionManager = strapi.sessionManager('users-permissions');
|
|
105
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
106
|
+
|
|
107
|
+
if (scope === 'all') {
|
|
108
|
+
await sessionManager.invalidateRefreshToken(userId);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (deviceId) {
|
|
113
|
+
await sessionManager.invalidateRefreshToken(userId, deviceId);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let currentSessionId = ctx.state.session?.id;
|
|
118
|
+
const cookieName = upSessions?.cookie?.name || 'strapi_up_refresh';
|
|
119
|
+
const refreshToken =
|
|
120
|
+
ctx.cookies.get(cookieName) ||
|
|
121
|
+
(typeof body.refreshToken === 'string' ? body.refreshToken : undefined);
|
|
122
|
+
|
|
123
|
+
if (refreshToken) {
|
|
124
|
+
const validation = await sessionManager.validateRefreshToken(refreshToken);
|
|
125
|
+
if (validation.isValid) {
|
|
126
|
+
currentSessionId = validation.sessionId;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (currentSessionId) {
|
|
131
|
+
await sessionManager.revokeSessionById(userId, currentSessionId);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
await sessionManager.invalidateRefreshToken(userId);
|
|
136
|
+
};
|
|
137
|
+
|
|
40
138
|
module.exports = ({ strapi }) => ({
|
|
41
139
|
async callback(ctx) {
|
|
42
140
|
const provider = ctx.params.provider || 'local';
|
|
@@ -94,46 +192,8 @@ module.exports = ({ strapi }) => ({
|
|
|
94
192
|
|
|
95
193
|
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
96
194
|
if (mode === 'refresh') {
|
|
97
|
-
|
|
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),
|
|
195
|
+
return sendRefreshAuthResponse(strapi, ctx, user, {
|
|
196
|
+
metadata: buildSessionMetadataFromContext(ctx),
|
|
137
197
|
});
|
|
138
198
|
}
|
|
139
199
|
|
|
@@ -153,44 +213,8 @@ module.exports = ({ strapi }) => ({
|
|
|
153
213
|
|
|
154
214
|
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
155
215
|
if (mode === 'refresh') {
|
|
156
|
-
|
|
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),
|
|
216
|
+
return sendRefreshAuthResponse(strapi, ctx, user, {
|
|
217
|
+
metadata: buildSessionMetadataFromContext(ctx),
|
|
194
218
|
});
|
|
195
219
|
}
|
|
196
220
|
|
|
@@ -233,28 +257,7 @@ module.exports = ({ strapi }) => ({
|
|
|
233
257
|
|
|
234
258
|
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
235
259
|
if (mode === 'refresh') {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// Invalidate all sessions when password changes for security
|
|
239
|
-
await strapi.sessionManager('users-permissions').invalidateRefreshToken(String(user.id));
|
|
240
|
-
|
|
241
|
-
const newDeviceId = deviceId || crypto.randomUUID();
|
|
242
|
-
const refresh = await strapi
|
|
243
|
-
.sessionManager('users-permissions')
|
|
244
|
-
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
245
|
-
|
|
246
|
-
const access = await strapi
|
|
247
|
-
.sessionManager('users-permissions')
|
|
248
|
-
.generateAccessToken(refresh.token);
|
|
249
|
-
if ('error' in access) {
|
|
250
|
-
throw new ApplicationError('Invalid credentials');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return ctx.send({
|
|
254
|
-
jwt: access.token,
|
|
255
|
-
refreshToken: refresh.token,
|
|
256
|
-
user: await sanitizeUser(user, ctx),
|
|
257
|
-
});
|
|
260
|
+
return reissueTokensAfterPasswordChange(strapi, ctx, user);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
return ctx.send({
|
|
@@ -290,28 +293,7 @@ module.exports = ({ strapi }) => ({
|
|
|
290
293
|
|
|
291
294
|
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
292
295
|
if (mode === 'refresh') {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
// Invalidate all sessions when password is reset for security
|
|
296
|
-
await strapi.sessionManager('users-permissions').invalidateRefreshToken(String(user.id));
|
|
297
|
-
|
|
298
|
-
const newDeviceId = deviceId || crypto.randomUUID();
|
|
299
|
-
const refresh = await strapi
|
|
300
|
-
.sessionManager('users-permissions')
|
|
301
|
-
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
302
|
-
|
|
303
|
-
const access = await strapi
|
|
304
|
-
.sessionManager('users-permissions')
|
|
305
|
-
.generateAccessToken(refresh.token);
|
|
306
|
-
if ('error' in access) {
|
|
307
|
-
throw new ApplicationError('Invalid credentials');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return ctx.send({
|
|
311
|
-
jwt: access.token,
|
|
312
|
-
refreshToken: refresh.token,
|
|
313
|
-
user: await sanitizeUser(user, ctx),
|
|
314
|
-
});
|
|
296
|
+
return reissueTokensAfterPasswordChange(strapi, ctx, user);
|
|
315
297
|
}
|
|
316
298
|
|
|
317
299
|
return ctx.send({
|
|
@@ -355,18 +337,11 @@ module.exports = ({ strapi }) => ({
|
|
|
355
337
|
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
356
338
|
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
357
339
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
secure: isSecure,
|
|
364
|
-
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
365
|
-
path: upSessions.cookie?.path ?? '/',
|
|
366
|
-
domain: upSessions.cookie?.domain,
|
|
367
|
-
overwrite: true,
|
|
368
|
-
};
|
|
369
|
-
ctx.cookies.set(cookieName, rotation.token, cookieOptions);
|
|
340
|
+
ctx.cookies.set(
|
|
341
|
+
cookieName,
|
|
342
|
+
rotation.token,
|
|
343
|
+
buildRefreshCookieOptions(upSessions, isProduction)
|
|
344
|
+
);
|
|
370
345
|
return ctx.send({ jwt: result.token });
|
|
371
346
|
}
|
|
372
347
|
return ctx.send({ jwt: result.token, refreshToken: rotation.token });
|
|
@@ -377,21 +352,22 @@ module.exports = ({ strapi }) => ({
|
|
|
377
352
|
return ctx.notFound();
|
|
378
353
|
}
|
|
379
354
|
|
|
380
|
-
// Invalidate all sessions for the authenticated user, or by deviceId if provided
|
|
381
355
|
if (!ctx.state.user) {
|
|
382
356
|
return ctx.unauthorized('Missing authentication');
|
|
383
357
|
}
|
|
384
358
|
|
|
385
|
-
const
|
|
359
|
+
const userId = String(ctx.state.user.id);
|
|
360
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
361
|
+
const body = ctx.request.body || {};
|
|
362
|
+
const scope = typeof body.scope === 'string' ? body.scope : undefined;
|
|
363
|
+
const deviceId = extractDeviceId(body);
|
|
364
|
+
|
|
386
365
|
try {
|
|
387
|
-
await strapi
|
|
388
|
-
.sessionManager('users-permissions')
|
|
389
|
-
.invalidateRefreshToken(String(ctx.state.user.id), deviceId);
|
|
366
|
+
await revokeLogoutSessions(strapi, ctx, userId, { scope, deviceId, body });
|
|
390
367
|
} catch (err) {
|
|
391
368
|
strapi.log.error('UP logout failed', err);
|
|
392
369
|
}
|
|
393
370
|
|
|
394
|
-
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
395
371
|
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
396
372
|
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
397
373
|
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
@@ -399,6 +375,48 @@ module.exports = ({ strapi }) => ({
|
|
|
399
375
|
}
|
|
400
376
|
return ctx.send({ ok: true });
|
|
401
377
|
},
|
|
378
|
+
async getSessions(ctx) {
|
|
379
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
380
|
+
if (mode !== 'refresh') {
|
|
381
|
+
return ctx.notFound();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!ctx.state.user) {
|
|
385
|
+
return ctx.unauthorized('Missing authentication');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const currentSessionId = ctx.state.session?.id;
|
|
389
|
+
const sessions = await strapi
|
|
390
|
+
.sessionManager('users-permissions')
|
|
391
|
+
.listSessions(String(ctx.state.user.id));
|
|
392
|
+
|
|
393
|
+
const data = sortSessionsForDisplay(
|
|
394
|
+
sessions.map((session) => sanitizeSessionEntry(session, currentSessionId))
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
return ctx.send({ data });
|
|
398
|
+
},
|
|
399
|
+
async revokeSession(ctx) {
|
|
400
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
401
|
+
if (mode !== 'refresh') {
|
|
402
|
+
return ctx.notFound();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!ctx.state.user) {
|
|
406
|
+
return ctx.unauthorized('Missing authentication');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const { sessionId } = ctx.params;
|
|
410
|
+
const revoked = await strapi
|
|
411
|
+
.sessionManager('users-permissions')
|
|
412
|
+
.revokeSessionById(String(ctx.state.user.id), sessionId);
|
|
413
|
+
|
|
414
|
+
if (!revoked) {
|
|
415
|
+
return ctx.notFound('Session not found');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return ctx.send({ data: {} });
|
|
419
|
+
},
|
|
402
420
|
async connect(ctx, next) {
|
|
403
421
|
const grant = require('grant').koa();
|
|
404
422
|
|
|
@@ -617,7 +635,10 @@ module.exports = ({ strapi }) => ({
|
|
|
617
635
|
|
|
618
636
|
const refresh = await strapi
|
|
619
637
|
.sessionManager('users-permissions')
|
|
620
|
-
.generateRefreshToken(String(user.id), deviceId, {
|
|
638
|
+
.generateRefreshToken(String(user.id), deviceId, {
|
|
639
|
+
type: 'refresh',
|
|
640
|
+
metadata: buildSessionMetadataFromContext(ctx),
|
|
641
|
+
});
|
|
621
642
|
|
|
622
643
|
const access = await strapi
|
|
623
644
|
.sessionManager('users-permissions')
|