@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.
Files changed (59) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/admin/src/pages/AdvancedSettings/index.jsx +1 -1
  3. package/admin/src/pages/Roles/pages/ListPage/index.jsx +1 -1
  4. package/admin/src/translations/ja.json +46 -7
  5. package/dist/admin/pages/AdvancedSettings/index.js +1 -1
  6. package/dist/admin/pages/AdvancedSettings/index.js.map +1 -1
  7. package/dist/admin/pages/AdvancedSettings/index.mjs +1 -1
  8. package/dist/admin/pages/AdvancedSettings/index.mjs.map +1 -1
  9. package/dist/admin/pages/Roles/pages/ListPage/index.js +1 -1
  10. package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
  11. package/dist/admin/pages/Roles/pages/ListPage/index.mjs +1 -1
  12. package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
  13. package/dist/admin/translations/ja.json.js +46 -7
  14. package/dist/admin/translations/ja.json.js.map +1 -1
  15. package/dist/admin/translations/ja.json.mjs +46 -7
  16. package/dist/admin/translations/ja.json.mjs.map +1 -1
  17. package/dist/server/controllers/auth.js +133 -114
  18. package/dist/server/controllers/auth.js.map +1 -1
  19. package/dist/server/controllers/auth.mjs +132 -113
  20. package/dist/server/controllers/auth.mjs.map +1 -1
  21. package/dist/server/controllers/validation/user.js +16 -9
  22. package/dist/server/controllers/validation/user.js.map +1 -1
  23. package/dist/server/controllers/validation/user.mjs +16 -9
  24. package/dist/server/controllers/validation/user.mjs.map +1 -1
  25. package/dist/server/routes/content-api/auth.js +16 -0
  26. package/dist/server/routes/content-api/auth.js.map +1 -1
  27. package/dist/server/routes/content-api/auth.mjs +16 -0
  28. package/dist/server/routes/content-api/auth.mjs.map +1 -1
  29. package/dist/server/routes/content-api/validation.js +29 -2
  30. package/dist/server/routes/content-api/validation.js.map +1 -1
  31. package/dist/server/routes/content-api/validation.mjs +29 -2
  32. package/dist/server/routes/content-api/validation.mjs.map +1 -1
  33. package/dist/server/services/jwt.js +8 -6
  34. package/dist/server/services/jwt.js.map +1 -1
  35. package/dist/server/services/jwt.mjs +8 -6
  36. package/dist/server/services/jwt.mjs.map +1 -1
  37. package/dist/server/services/users-permissions.js +8 -0
  38. package/dist/server/services/users-permissions.js.map +1 -1
  39. package/dist/server/services/users-permissions.mjs +8 -0
  40. package/dist/server/services/users-permissions.mjs.map +1 -1
  41. package/dist/server/strategies/users-permissions.js +8 -1
  42. package/dist/server/strategies/users-permissions.js.map +1 -1
  43. package/dist/server/strategies/users-permissions.mjs +8 -1
  44. package/dist/server/strategies/users-permissions.mjs.map +1 -1
  45. package/dist/server/utils/refresh-cookie-options.js +27 -0
  46. package/dist/server/utils/refresh-cookie-options.js.map +1 -0
  47. package/dist/server/utils/refresh-cookie-options.mjs +25 -0
  48. package/dist/server/utils/refresh-cookie-options.mjs.map +1 -0
  49. package/lint-staged.config.mjs +1 -0
  50. package/package.json +5 -5
  51. package/rollup.config.mjs +3 -3
  52. package/server/controllers/auth.js +162 -141
  53. package/server/controllers/validation/user.js +22 -9
  54. package/server/routes/content-api/auth.js +12 -0
  55. package/server/routes/content-api/validation.js +32 -2
  56. package/server/services/jwt.js +4 -3
  57. package/server/services/users-permissions.js +2 -0
  58. package/server/strategies/users-permissions.js +6 -1
  59. 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":"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"}
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":"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"}
@@ -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;gBACT,MAAM,EAAEE,EAAE,EAAE,GAAGF,KAAAA;;AAGf,gBAAA,IAAIE,OAAOC,SAAAA,EAAW;oBACpB,OAAO;wBAAEC,aAAAA,EAAe;;AAChC,gBAAA;AAEM,gBAAA,MAAMC,IAAAA,GAAO,MAAMhB,UAAAA,CAAW,MAAA,CAAA,CAAQiB,sBAAsB,CAACJ,EAAAA,CAAAA;;AAG7D,gBAAA,IAAI,CAACG,IAAAA,EAAM;oBACT,OAAO;wBAAEE,KAAAA,EAAO;;AACxB,gBAAA;AAEM,gBAAA,MAAMC,mBAAmB,MAAMjB,mBAAAA,EAAAA;;AAG/B,gBAAA,IAAIiB,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,CAACb,EAAE,CAAA,CACnDc,IAAI,CAAC3B,UAAAA,CAAW,cAAc4B,mBAAmB,CAAA,CACjDD,IAAI,CAACnC,GAAAA,CAAIQ,UAAAA,CAAW,YAAA,CAAA,CAAc6B,sBAAsB,CAAA,CAAA;;gBAG3D,MAAMC,OAAAA,GAAU,MAAM3B,MAAAA,CAAO4B,UAAU,CAACR,WAAW,CAACS,MAAM,CAACC,eAAe,CAACV,WAAAA,CAAAA;gBAE3Eb,GAAAA,CAAIwB,KAAK,CAAClB,IAAI,GAAGA,IAAAA;gBAEjB,OAAO;oBACLD,aAAAA,EAAe,IAAA;oBACfoB,WAAAA,EAAanB,IAAAA;AACbc,oBAAAA;AACR,iBAAA;AACA,YAAA;YAEI,MAAMM,iBAAAA,GAAoB,MAAMpC,UAAAA,CAAW,YAAA,CAAA,CACxCqC,qBAAqB,EAAA,CACrBV,IAAI,CAACnC,GAAAA,CAAIQ,UAAAA,CAAW,YAAA,CAAA,CAAc6B,sBAAsB,CAAA,CAAA;YAE3D,IAAIO,iBAAAA,CAAkBE,MAAM,KAAK,CAAA,EAAG;gBAClC,OAAO;oBAAEvB,aAAAA,EAAe;;AAC9B,YAAA;YAEI,MAAMe,OAAAA,GAAU,MAAM3B,MAAAA,CAAO4B,UAAU,CAACR,WAAW,CAACS,MAAM,CAACC,eAAe,CAACG,iBAAAA,CAAAA;YAE3E,OAAO;gBACLrB,aAAAA,EAAe,IAAA;gBACfoB,WAAAA,EAAa,IAAA;AACbL,gBAAAA;AACN,aAAA;AACA,QAAA,CAAA,CAAI,OAAOS,GAAAA,EAAK;YACZ,OAAO;gBAAExB,aAAAA,EAAe;;AAC5B,QAAA;AACA,IAAA,CAAA;IAEA,MAAMyB,MAAAA,GAAS,OAAOC,IAAAA,EAAMC,MAAAA,GAAAA;AAC1B,QAAA,MAAM,EAAEP,WAAAA,EAAanB,IAAI,EAAEc,OAAO,EAAE,GAAGW,IAAAA;QAEvC,IAAI,CAACC,MAAAA,CAAOC,KAAK,EAAE;AACjB,YAAA,IAAI,CAAC3B,IAAAA,EAAM;;AAET,gBAAA,MAAM,IAAInB,iBAAAA,EAAAA;YAChB,CAAA,MAAW;;AAEL,gBAAA;AACN,YAAA;AACA,QAAA;;AAGE,QAAA,IAAI,CAACiC,OAAAA,EAAS;AACZ,YAAA,MAAM,IAAIjC,iBAAAA,EAAAA;AACd,QAAA;QAEE,MAAM+C,SAAAA,GAAYlD;AAEhBH,QAAAA,SAAAA;AAEAE,QAAAA,KAAAA,CAAM,CAACkD,KAAAA,GAAUb,OAAAA,CAAQe,GAAG,CAACF,KAAAA,CAAAA,CAAAA,CAAAA,CAC7BD,OAAOC,KAAK,CAAA;AAEd,QAAA,IAAI,CAACC,SAAAA,EAAW;AACd,YAAA,MAAM,IAAIhD,cAAAA,EAAAA;AACd,QAAA;AACA,IAAA,CAAA;IAEAkD,gBAAAA,GAAiB;QACfxC,IAAAA,EAAM,mBAAA;AACNG,QAAAA,YAAAA;AACA+B,QAAAA;AACF,KAAA;;;;;;"}
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.49.0",
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.0",
57
- "@strapi/icons": "2.2.0",
58
- "@strapi/utils": "5.49.0",
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.49.0",
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 { baseConfig } from '../../../rollup.utils.mjs';
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
- 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),
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
- 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),
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
- const deviceId = extractDeviceId(ctx.request.body);
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
- const deviceId = extractDeviceId(ctx.request.body);
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
- const isSecure =
359
- typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
360
-
361
- const cookieOptions = {
362
- httpOnly: true,
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 deviceId = extractDeviceId(ctx.request.body);
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, { type: 'refresh' });
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')