@strapi/core 0.0.0-experimental.864696ab15724578c0d6eea176c168b29a039255 → 0.0.0-experimental.8a41bddd1fbbac61aa43a961ea1ff74ac586cf55
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.
Potentially problematic release.
This version of @strapi/core might be problematic. Click here for more details.
- package/dist/Strapi.d.ts +1 -0
- package/dist/Strapi.d.ts.map +1 -1
- package/dist/Strapi.js +5 -1
- package/dist/Strapi.js.map +1 -1
- package/dist/Strapi.mjs +5 -1
- package/dist/Strapi.mjs.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/constants.mjs +4 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/core-api/controller/index.d.ts.map +1 -1
- package/dist/core-api/controller/index.js +2 -1
- package/dist/core-api/controller/index.js.map +1 -1
- package/dist/core-api/controller/index.mjs +2 -1
- package/dist/core-api/controller/index.mjs.map +1 -1
- package/dist/core-api/controller/transform.d.ts +3 -2
- package/dist/core-api/controller/transform.d.ts.map +1 -1
- package/dist/core-api/controller/transform.js +13 -3
- package/dist/core-api/controller/transform.js.map +1 -1
- package/dist/core-api/controller/transform.mjs +13 -3
- package/dist/core-api/controller/transform.mjs.map +1 -1
- package/dist/core-api/routes/index.d.ts +4 -22
- package/dist/core-api/routes/index.d.ts.map +1 -1
- package/dist/core-api/routes/index.js +150 -8
- package/dist/core-api/routes/index.js.map +1 -1
- package/dist/core-api/routes/index.mjs +131 -8
- package/dist/core-api/routes/index.mjs.map +1 -1
- package/dist/core-api/routes/validation/attributes.d.ts +244 -0
- package/dist/core-api/routes/validation/attributes.d.ts.map +1 -0
- package/dist/core-api/routes/validation/attributes.js +560 -0
- package/dist/core-api/routes/validation/attributes.js.map +1 -0
- package/dist/core-api/routes/validation/attributes.mjs +521 -0
- package/dist/core-api/routes/validation/attributes.mjs.map +1 -0
- package/dist/core-api/routes/validation/common.d.ts +105 -0
- package/dist/core-api/routes/validation/common.d.ts.map +1 -0
- package/dist/core-api/routes/validation/common.js +116 -0
- package/dist/core-api/routes/validation/common.js.map +1 -0
- package/dist/core-api/routes/validation/common.mjs +95 -0
- package/dist/core-api/routes/validation/common.mjs.map +1 -0
- package/dist/core-api/routes/validation/component.d.ts +34 -0
- package/dist/core-api/routes/validation/component.d.ts.map +1 -0
- package/dist/core-api/routes/validation/component.js +45 -0
- package/dist/core-api/routes/validation/component.js.map +1 -0
- package/dist/core-api/routes/validation/component.mjs +43 -0
- package/dist/core-api/routes/validation/component.mjs.map +1 -0
- package/dist/core-api/routes/validation/constants.d.ts +8 -0
- package/dist/core-api/routes/validation/constants.d.ts.map +1 -0
- package/dist/core-api/routes/validation/constants.js +18 -0
- package/dist/core-api/routes/validation/constants.js.map +1 -0
- package/dist/core-api/routes/validation/constants.mjs +16 -0
- package/dist/core-api/routes/validation/constants.mjs.map +1 -0
- package/dist/core-api/routes/validation/content-type.d.ts +128 -0
- package/dist/core-api/routes/validation/content-type.d.ts.map +1 -0
- package/dist/core-api/routes/validation/content-type.js +201 -0
- package/dist/core-api/routes/validation/content-type.js.map +1 -0
- package/dist/core-api/routes/validation/content-type.mjs +180 -0
- package/dist/core-api/routes/validation/content-type.mjs.map +1 -0
- package/dist/core-api/routes/validation/index.d.ts +5 -0
- package/dist/core-api/routes/validation/index.d.ts.map +1 -0
- package/dist/core-api/routes/validation/mappers.d.ts +105 -0
- package/dist/core-api/routes/validation/mappers.d.ts.map +1 -0
- package/dist/core-api/routes/validation/mappers.js +273 -0
- package/dist/core-api/routes/validation/mappers.js.map +1 -0
- package/dist/core-api/routes/validation/mappers.mjs +249 -0
- package/dist/core-api/routes/validation/mappers.mjs.map +1 -0
- package/dist/core-api/routes/validation/utils.d.ts +47 -0
- package/dist/core-api/routes/validation/utils.d.ts.map +1 -0
- package/dist/core-api/routes/validation/utils.js +128 -0
- package/dist/core-api/routes/validation/utils.js.map +1 -0
- package/dist/core-api/routes/validation/utils.mjs +106 -0
- package/dist/core-api/routes/validation/utils.mjs.map +1 -0
- package/dist/domain/content-type/index.d.ts.map +1 -1
- package/dist/domain/content-type/index.js +17 -1
- package/dist/domain/content-type/index.js.map +1 -1
- package/dist/domain/content-type/index.mjs +17 -1
- package/dist/domain/content-type/index.mjs.map +1 -1
- package/dist/domain/module/index.d.ts.map +1 -1
- package/dist/domain/module/index.js +3 -0
- package/dist/domain/module/index.js.map +1 -1
- package/dist/domain/module/index.mjs +3 -0
- package/dist/domain/module/index.mjs.map +1 -1
- package/dist/factories.d.ts +3 -1
- package/dist/factories.d.ts.map +1 -1
- package/dist/factories.js +10 -2
- package/dist/factories.js.map +1 -1
- package/dist/factories.mjs +10 -3
- package/dist/factories.mjs.map +1 -1
- package/dist/loaders/plugins/index.js +1 -1
- package/dist/loaders/plugins/index.js.map +1 -1
- package/dist/loaders/plugins/index.mjs +1 -1
- package/dist/loaders/plugins/index.mjs.map +1 -1
- package/dist/middlewares/cors.d.ts +9 -1
- package/dist/middlewares/cors.d.ts.map +1 -1
- package/dist/middlewares/cors.js +39 -17
- package/dist/middlewares/cors.js.map +1 -1
- package/dist/middlewares/cors.mjs +39 -18
- package/dist/middlewares/cors.mjs.map +1 -1
- package/dist/migrations/first-published-at.d.ts +4 -0
- package/dist/migrations/first-published-at.d.ts.map +1 -0
- package/dist/migrations/first-published-at.js +51 -0
- package/dist/migrations/first-published-at.js.map +1 -0
- package/dist/migrations/first-published-at.mjs +49 -0
- package/dist/migrations/first-published-at.mjs.map +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +5 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +5 -0
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/package.json.js +15 -12
- package/dist/package.json.js.map +1 -1
- package/dist/package.json.mjs +15 -12
- package/dist/package.json.mjs.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/index.mjs +2 -0
- package/dist/providers/index.mjs.map +1 -1
- package/dist/providers/sessionManager.d.ts +3 -0
- package/dist/providers/sessionManager.d.ts.map +1 -0
- package/dist/providers/sessionManager.js +21 -0
- package/dist/providers/sessionManager.js.map +1 -0
- package/dist/providers/sessionManager.mjs +19 -0
- package/dist/providers/sessionManager.mjs.map +1 -0
- package/dist/services/content-api/index.d.ts +1 -1
- package/dist/services/content-api/index.d.ts.map +1 -1
- package/dist/services/content-api/index.js +1 -1
- package/dist/services/content-api/index.js.map +1 -1
- package/dist/services/content-api/index.mjs +2 -2
- package/dist/services/content-api/index.mjs.map +1 -1
- package/dist/services/content-source-maps.d.ts +13 -0
- package/dist/services/content-source-maps.d.ts.map +1 -0
- package/dist/services/content-source-maps.js +108 -0
- package/dist/services/content-source-maps.js.map +1 -0
- package/dist/services/content-source-maps.mjs +106 -0
- package/dist/services/content-source-maps.mjs.map +1 -0
- package/dist/services/document-service/components.d.ts +6 -1
- package/dist/services/document-service/components.d.ts.map +1 -1
- package/dist/services/document-service/components.js +97 -0
- package/dist/services/document-service/components.js.map +1 -1
- package/dist/services/document-service/components.mjs +97 -1
- package/dist/services/document-service/components.mjs.map +1 -1
- package/dist/services/document-service/first-published-at.d.ts +7 -0
- package/dist/services/document-service/first-published-at.d.ts.map +1 -0
- package/dist/services/document-service/first-published-at.js +31 -0
- package/dist/services/document-service/first-published-at.js.map +1 -0
- package/dist/services/document-service/first-published-at.mjs +28 -0
- package/dist/services/document-service/first-published-at.mjs.map +1 -0
- package/dist/services/document-service/repository.d.ts.map +1 -1
- package/dist/services/document-service/repository.js +11 -4
- package/dist/services/document-service/repository.js.map +1 -1
- package/dist/services/document-service/repository.mjs +12 -5
- package/dist/services/document-service/repository.mjs.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.d.ts +19 -2
- package/dist/services/document-service/utils/unidirectional-relations.d.ts.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.js +21 -6
- package/dist/services/document-service/utils/unidirectional-relations.js.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.mjs +21 -6
- package/dist/services/document-service/utils/unidirectional-relations.mjs.map +1 -1
- package/dist/services/metrics/index.d.ts +1 -1
- package/dist/services/metrics/index.d.ts.map +1 -1
- package/dist/services/metrics/index.js +11 -9
- package/dist/services/metrics/index.js.map +1 -1
- package/dist/services/metrics/index.mjs +11 -9
- package/dist/services/metrics/index.mjs.map +1 -1
- package/dist/services/metrics/middleware.d.ts +2 -1
- package/dist/services/metrics/middleware.d.ts.map +1 -1
- package/dist/services/metrics/middleware.js +2 -2
- package/dist/services/metrics/middleware.js.map +1 -1
- package/dist/services/metrics/middleware.mjs +2 -2
- package/dist/services/metrics/middleware.mjs.map +1 -1
- package/dist/services/metrics/sender.d.ts.map +1 -1
- package/dist/services/metrics/sender.js +2 -2
- package/dist/services/metrics/sender.js.map +1 -1
- package/dist/services/metrics/sender.mjs +2 -2
- package/dist/services/metrics/sender.mjs.map +1 -1
- package/dist/services/server/register-routes.js +22 -2
- package/dist/services/server/register-routes.js.map +1 -1
- package/dist/services/server/register-routes.mjs +22 -2
- package/dist/services/server/register-routes.mjs.map +1 -1
- package/dist/services/server/routing.d.ts +10 -0
- package/dist/services/server/routing.d.ts.map +1 -1
- package/dist/services/server/routing.js +7 -1
- package/dist/services/server/routing.js.map +1 -1
- package/dist/services/server/routing.mjs +7 -1
- package/dist/services/server/routing.mjs.map +1 -1
- package/dist/services/session-manager.d.ts +160 -0
- package/dist/services/session-manager.d.ts.map +1 -0
- package/dist/services/session-manager.js +476 -0
- package/dist/services/session-manager.js.map +1 -0
- package/dist/services/session-manager.mjs +473 -0
- package/dist/services/session-manager.mjs.map +1 -0
- package/package.json +15 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.mjs","sources":["../../../src/services/server/routing.ts"],"sourcesContent":["import Router from '@koa/router';\nimport { has } from 'lodash/fp';\nimport { yup } from '@strapi/utils';\nimport type { Core } from '@strapi/types';\n\nimport createEndpointComposer from './compose-endpoint';\n\nconst policyOrMiddlewareSchema = yup.lazy((value) => {\n if (typeof value === 'string') {\n return yup.string().required();\n }\n\n if (typeof value === 'function') {\n return yup.mixed().isFunction();\n }\n\n return yup.object({\n name: yup.string().required(),\n options: yup.object().notRequired(), // any options\n });\n});\n\nconst routeSchema = yup.object({\n method: yup.string().oneOf(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'ALL']).required(),\n path: yup.string().required(),\n handler: yup.lazy((value) => {\n if (typeof value === 'string') {\n return yup.string().required();\n }\n\n if (Array.isArray(value)) {\n return yup.array().required();\n }\n\n return yup.mixed().isFunction().required();\n }),\n config: yup\n .object({\n auth: yup.lazy((value) => {\n if (value === false) {\n return yup.boolean().required();\n }\n\n return yup.object({\n scope: yup.array().of(yup.string()).required(),\n });\n }),\n policies: yup\n .array()\n // FIXME: fixed in yup v1\n .of(policyOrMiddlewareSchema as any)\n .notRequired(),\n middlewares: yup\n .array()\n // FIXME: fixed in yup v1\n .of(policyOrMiddlewareSchema as any)\n .notRequired(),\n })\n .notRequired(),\n});\n\nconst validateRouteConfig = (routeConfig: Core.RouteInput) => {\n try {\n return routeSchema.validateSync(routeConfig, {\n strict: true,\n abortEarly: false,\n stripUnknown: true,\n });\n } catch (error) {\n if (error instanceof yup.ValidationError) {\n throw new Error(`Invalid route config ${error.message}`);\n }\n }\n};\n\nconst createRouteManager = (strapi: Core.Strapi, opts: { type?: string } = {}) => {\n const { type } = opts;\n\n const composeEndpoint = createEndpointComposer(strapi);\n\n const createRoute = (route: Core.RouteInput, router: Router) => {\n validateRouteConfig(route);\n\n // NOTE: the router type is used to tag controller actions and for authentication / authorization so we need to pass this info down to the route level\n const routeWithInfo = Object.assign(route, {\n info: {\n ...(route.info ?? {}),\n type: type
|
|
1
|
+
{"version":3,"file":"routing.mjs","sources":["../../../src/services/server/routing.ts"],"sourcesContent":["import Router from '@koa/router';\nimport { has } from 'lodash/fp';\nimport { yup } from '@strapi/utils';\nimport type { Core } from '@strapi/types';\n\nimport createEndpointComposer from './compose-endpoint';\n\nconst policyOrMiddlewareSchema = yup.lazy((value) => {\n if (typeof value === 'string') {\n return yup.string().required();\n }\n\n if (typeof value === 'function') {\n return yup.mixed().isFunction();\n }\n\n return yup.object({\n name: yup.string().required(),\n options: yup.object().notRequired(), // any options\n });\n});\n\nconst routeSchema = yup.object({\n method: yup.string().oneOf(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'ALL']).required(),\n path: yup.string().required(),\n handler: yup.lazy((value) => {\n if (typeof value === 'string') {\n return yup.string().required();\n }\n\n if (Array.isArray(value)) {\n return yup.array().required();\n }\n\n return yup.mixed().isFunction().required();\n }),\n request: yup\n .object({\n params: yup.object().notRequired(),\n query: yup.object().notRequired(),\n body: yup.object().notRequired(),\n })\n .notRequired(),\n response: yup.object().notRequired(),\n config: yup\n .object({\n auth: yup.lazy((value) => {\n if (value === false) {\n return yup.boolean().required();\n }\n\n return yup.object({\n scope: yup.array().of(yup.string()).required(),\n });\n }),\n policies: yup\n .array()\n // FIXME: fixed in yup v1\n .of(policyOrMiddlewareSchema as any)\n .notRequired(),\n middlewares: yup\n .array()\n // FIXME: fixed in yup v1\n .of(policyOrMiddlewareSchema as any)\n .notRequired(),\n })\n .notRequired(),\n});\n\nconst validateRouteConfig = (routeConfig: Core.RouteInput) => {\n try {\n return routeSchema.validateSync(routeConfig, {\n strict: true,\n abortEarly: false,\n stripUnknown: true,\n });\n } catch (error) {\n if (error instanceof yup.ValidationError) {\n throw new Error(`Invalid route config ${error.message}`);\n }\n }\n};\n\nconst createRouteManager = (strapi: Core.Strapi, opts: { type?: string } = {}) => {\n const { type } = opts;\n\n const composeEndpoint = createEndpointComposer(strapi);\n\n const createRoute = (route: Core.RouteInput, router: Router) => {\n validateRouteConfig(route);\n\n // NOTE: the router type is used to tag controller actions and for authentication / authorization so we need to pass this info down to the route level\n const routeWithInfo = Object.assign(route, {\n info: {\n ...(route.info ?? {}),\n type: type ?? 'api',\n },\n });\n\n composeEndpoint(routeWithInfo, { router });\n };\n\n const addRoutes = (routes: Core.Router | Core.RouteInput[], router: Router) => {\n if (Array.isArray(routes)) {\n routes.forEach((route) => createRoute(route, router));\n } else if (routes.routes) {\n const subRouter = new Router({ prefix: routes.prefix });\n\n routes.routes.forEach((route) => {\n const hasPrefix = has('prefix', route.config);\n createRoute(route, hasPrefix ? router : subRouter);\n });\n\n return router.use(subRouter.routes(), subRouter.allowedMethods());\n }\n };\n\n return {\n addRoutes,\n };\n};\n\nexport { validateRouteConfig, createRouteManager };\n"],"names":["policyOrMiddlewareSchema","yup","lazy","value","string","required","mixed","isFunction","object","name","options","notRequired","routeSchema","method","oneOf","path","handler","Array","isArray","array","request","params","query","body","response","config","auth","boolean","scope","of","policies","middlewares","validateRouteConfig","routeConfig","validateSync","strict","abortEarly","stripUnknown","error","ValidationError","Error","message","createRouteManager","strapi","opts","type","composeEndpoint","createEndpointComposer","createRoute","route","router","routeWithInfo","Object","assign","info","addRoutes","routes","forEach","subRouter","Router","prefix","hasPrefix","has","use","allowedMethods"],"mappings":";;;;;AAOA,MAAMA,wBAA2BC,GAAAA,GAAAA,CAAIC,IAAI,CAAC,CAACC,KAAAA,GAAAA;IACzC,IAAI,OAAOA,UAAU,QAAU,EAAA;QAC7B,OAAOF,GAAAA,CAAIG,MAAM,EAAA,CAAGC,QAAQ,EAAA;AAC9B;IAEA,IAAI,OAAOF,UAAU,UAAY,EAAA;QAC/B,OAAOF,GAAAA,CAAIK,KAAK,EAAA,CAAGC,UAAU,EAAA;AAC/B;IAEA,OAAON,GAAAA,CAAIO,MAAM,CAAC;QAChBC,IAAMR,EAAAA,GAAAA,CAAIG,MAAM,EAAA,CAAGC,QAAQ,EAAA;QAC3BK,OAAST,EAAAA,GAAAA,CAAIO,MAAM,EAAA,CAAGG,WAAW;AACnC,KAAA,CAAA;AACF,CAAA,CAAA;AAEA,MAAMC,WAAAA,GAAcX,GAAIO,CAAAA,MAAM,CAAC;AAC7BK,IAAAA,MAAAA,EAAQZ,GAAIG,CAAAA,MAAM,EAAGU,CAAAA,KAAK,CAAC;AAAC,QAAA,KAAA;AAAO,QAAA,MAAA;AAAQ,QAAA,KAAA;AAAO,QAAA,OAAA;AAAS,QAAA,QAAA;AAAU,QAAA;AAAM,KAAA,CAAA,CAAET,QAAQ,EAAA;IACrFU,IAAMd,EAAAA,GAAAA,CAAIG,MAAM,EAAA,CAAGC,QAAQ,EAAA;IAC3BW,OAASf,EAAAA,GAAAA,CAAIC,IAAI,CAAC,CAACC,KAAAA,GAAAA;QACjB,IAAI,OAAOA,UAAU,QAAU,EAAA;YAC7B,OAAOF,GAAAA,CAAIG,MAAM,EAAA,CAAGC,QAAQ,EAAA;AAC9B;QAEA,IAAIY,KAAAA,CAAMC,OAAO,CAACf,KAAQ,CAAA,EAAA;YACxB,OAAOF,GAAAA,CAAIkB,KAAK,EAAA,CAAGd,QAAQ,EAAA;AAC7B;AAEA,QAAA,OAAOJ,GAAIK,CAAAA,KAAK,EAAGC,CAAAA,UAAU,GAAGF,QAAQ,EAAA;AAC1C,KAAA,CAAA;IACAe,OAASnB,EAAAA,GAAAA,CACNO,MAAM,CAAC;QACNa,MAAQpB,EAAAA,GAAAA,CAAIO,MAAM,EAAA,CAAGG,WAAW,EAAA;QAChCW,KAAOrB,EAAAA,GAAAA,CAAIO,MAAM,EAAA,CAAGG,WAAW,EAAA;QAC/BY,IAAMtB,EAAAA,GAAAA,CAAIO,MAAM,EAAA,CAAGG,WAAW;AAChC,KAAA,CAAA,CACCA,WAAW,EAAA;IACda,QAAUvB,EAAAA,GAAAA,CAAIO,MAAM,EAAA,CAAGG,WAAW,EAAA;IAClCc,MAAQxB,EAAAA,GAAAA,CACLO,MAAM,CAAC;QACNkB,IAAMzB,EAAAA,GAAAA,CAAIC,IAAI,CAAC,CAACC,KAAAA,GAAAA;AACd,YAAA,IAAIA,UAAU,KAAO,EAAA;gBACnB,OAAOF,GAAAA,CAAI0B,OAAO,EAAA,CAAGtB,QAAQ,EAAA;AAC/B;YAEA,OAAOJ,GAAAA,CAAIO,MAAM,CAAC;gBAChBoB,KAAO3B,EAAAA,GAAAA,CAAIkB,KAAK,EAAGU,CAAAA,EAAE,CAAC5B,GAAIG,CAAAA,MAAM,IAAIC,QAAQ;AAC9C,aAAA,CAAA;AACF,SAAA,CAAA;QACAyB,QAAU7B,EAAAA,GAAAA,CACPkB,KAAK,EACN;SACCU,EAAE,CAAC7B,0BACHW,WAAW,EAAA;QACdoB,WAAa9B,EAAAA,GAAAA,CACVkB,KAAK,EACN;SACCU,EAAE,CAAC7B,0BACHW,WAAW;AAChB,KAAA,CAAA,CACCA,WAAW;AAChB,CAAA,CAAA;AAEA,MAAMqB,sBAAsB,CAACC,WAAAA,GAAAA;IAC3B,IAAI;QACF,OAAOrB,WAAAA,CAAYsB,YAAY,CAACD,WAAa,EAAA;YAC3CE,MAAQ,EAAA,IAAA;YACRC,UAAY,EAAA,KAAA;YACZC,YAAc,EAAA;AAChB,SAAA,CAAA;AACF,KAAA,CAAE,OAAOC,KAAO,EAAA;QACd,IAAIA,KAAAA,YAAiBrC,GAAIsC,CAAAA,eAAe,EAAE;YACxC,MAAM,IAAIC,MAAM,CAAC,qBAAqB,EAAEF,KAAMG,CAAAA,OAAO,CAAC,CAAC,CAAA;AACzD;AACF;AACF;AAEA,MAAMC,kBAAqB,GAAA,CAACC,MAAqBC,EAAAA,IAAAA,GAA0B,EAAE,GAAA;IAC3E,MAAM,EAAEC,IAAI,EAAE,GAAGD,IAAAA;AAEjB,IAAA,MAAME,kBAAkBC,sBAAuBJ,CAAAA,MAAAA,CAAAA;IAE/C,MAAMK,WAAAA,GAAc,CAACC,KAAwBC,EAAAA,MAAAA,GAAAA;QAC3ClB,mBAAoBiB,CAAAA,KAAAA,CAAAA;;AAGpB,QAAA,MAAME,aAAgBC,GAAAA,MAAAA,CAAOC,MAAM,CAACJ,KAAO,EAAA;YACzCK,IAAM,EAAA;AACJ,gBAAA,GAAIL,KAAMK,CAAAA,IAAI,IAAI,EAAE;AACpBT,gBAAAA,IAAAA,EAAMA,IAAQ,IAAA;AAChB;AACF,SAAA,CAAA;AAEAC,QAAAA,eAAAA,CAAgBK,aAAe,EAAA;AAAED,YAAAA;AAAO,SAAA,CAAA;AAC1C,KAAA;IAEA,MAAMK,SAAAA,GAAY,CAACC,MAAyCN,EAAAA,MAAAA,GAAAA;QAC1D,IAAIjC,KAAAA,CAAMC,OAAO,CAACsC,MAAS,CAAA,EAAA;AACzBA,YAAAA,MAAAA,CAAOC,OAAO,CAAC,CAACR,KAAAA,GAAUD,YAAYC,KAAOC,EAAAA,MAAAA,CAAAA,CAAAA;SACxC,MAAA,IAAIM,MAAOA,CAAAA,MAAM,EAAE;YACxB,MAAME,SAAAA,GAAY,IAAIC,MAAO,CAAA;AAAEC,gBAAAA,MAAAA,EAAQJ,OAAOI;AAAO,aAAA,CAAA;AAErDJ,YAAAA,MAAAA,CAAOA,MAAM,CAACC,OAAO,CAAC,CAACR,KAAAA,GAAAA;AACrB,gBAAA,MAAMY,SAAYC,GAAAA,GAAAA,CAAI,QAAUb,EAAAA,KAAAA,CAAMxB,MAAM,CAAA;gBAC5CuB,WAAYC,CAAAA,KAAAA,EAAOY,YAAYX,MAASQ,GAAAA,SAAAA,CAAAA;AAC1C,aAAA,CAAA;AAEA,YAAA,OAAOR,OAAOa,GAAG,CAACL,UAAUF,MAAM,EAAA,EAAIE,UAAUM,cAAc,EAAA,CAAA;AAChE;AACF,KAAA;IAEA,OAAO;AACLT,QAAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { Database } from '@strapi/database';
|
|
2
|
+
export interface SessionProvider {
|
|
3
|
+
create(session: SessionData): Promise<SessionData>;
|
|
4
|
+
findBySessionId(sessionId: string): Promise<SessionData | null>;
|
|
5
|
+
updateBySessionId(sessionId: string, data: Partial<SessionData>): Promise<void>;
|
|
6
|
+
deleteBySessionId(sessionId: string): Promise<void>;
|
|
7
|
+
deleteExpired(): Promise<void>;
|
|
8
|
+
deleteBy(criteria: {
|
|
9
|
+
userId?: string;
|
|
10
|
+
origin?: string;
|
|
11
|
+
deviceId?: string;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface SessionData {
|
|
15
|
+
id?: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
deviceId?: string;
|
|
19
|
+
origin: string;
|
|
20
|
+
childId?: string | null;
|
|
21
|
+
type?: 'refresh' | 'session';
|
|
22
|
+
status?: 'active' | 'rotated' | 'revoked';
|
|
23
|
+
expiresAt: Date;
|
|
24
|
+
absoluteExpiresAt?: Date | null;
|
|
25
|
+
createdAt?: Date;
|
|
26
|
+
updatedAt?: Date;
|
|
27
|
+
}
|
|
28
|
+
export interface RefreshTokenPayload {
|
|
29
|
+
userId: string;
|
|
30
|
+
sessionId: string;
|
|
31
|
+
type: 'refresh';
|
|
32
|
+
exp: number;
|
|
33
|
+
iat: number;
|
|
34
|
+
}
|
|
35
|
+
export interface AccessTokenPayload {
|
|
36
|
+
userId: string;
|
|
37
|
+
sessionId: string;
|
|
38
|
+
type: 'access';
|
|
39
|
+
exp: number;
|
|
40
|
+
iat: number;
|
|
41
|
+
}
|
|
42
|
+
export type TokenPayload = RefreshTokenPayload | AccessTokenPayload;
|
|
43
|
+
export interface ValidateRefreshTokenResult {
|
|
44
|
+
isValid: boolean;
|
|
45
|
+
userId?: string;
|
|
46
|
+
sessionId?: string;
|
|
47
|
+
error?: 'invalid_token' | 'token_expired' | 'session_not_found' | 'session_expired' | 'wrong_token_type';
|
|
48
|
+
}
|
|
49
|
+
export interface SessionManagerConfig {
|
|
50
|
+
jwtSecret: string;
|
|
51
|
+
accessTokenLifespan: number;
|
|
52
|
+
maxRefreshTokenLifespan: number;
|
|
53
|
+
idleRefreshTokenLifespan: number;
|
|
54
|
+
maxSessionLifespan: number;
|
|
55
|
+
idleSessionLifespan: number;
|
|
56
|
+
}
|
|
57
|
+
declare class OriginSessionManager {
|
|
58
|
+
private sessionManager;
|
|
59
|
+
private origin;
|
|
60
|
+
constructor(sessionManager: SessionManager, origin: string);
|
|
61
|
+
generateRefreshToken(userId: string, deviceId: string | undefined, options?: {
|
|
62
|
+
type?: 'refresh' | 'session';
|
|
63
|
+
}): Promise<{
|
|
64
|
+
token: string;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
absoluteExpiresAt: string;
|
|
67
|
+
}>;
|
|
68
|
+
generateAccessToken(refreshToken: string): Promise<{
|
|
69
|
+
token: string;
|
|
70
|
+
} | {
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
73
|
+
rotateRefreshToken(refreshToken: string): Promise<{
|
|
74
|
+
token: string;
|
|
75
|
+
sessionId: string;
|
|
76
|
+
absoluteExpiresAt: string;
|
|
77
|
+
type: 'refresh' | 'session';
|
|
78
|
+
} | {
|
|
79
|
+
error: string;
|
|
80
|
+
}>;
|
|
81
|
+
validateAccessToken(token: string): {
|
|
82
|
+
isValid: true;
|
|
83
|
+
payload: AccessTokenPayload;
|
|
84
|
+
} | {
|
|
85
|
+
isValid: false;
|
|
86
|
+
payload: null;
|
|
87
|
+
};
|
|
88
|
+
validateRefreshToken(token: string): Promise<ValidateRefreshTokenResult>;
|
|
89
|
+
invalidateRefreshToken(userId: string, deviceId?: string): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Returns true when a session exists and is not expired for this origin.
|
|
92
|
+
* If the session exists but is expired, it will be deleted as part of this check.
|
|
93
|
+
*/
|
|
94
|
+
isSessionActive(sessionId: string): Promise<boolean>;
|
|
95
|
+
}
|
|
96
|
+
declare class SessionManager {
|
|
97
|
+
private provider;
|
|
98
|
+
private originConfigs;
|
|
99
|
+
private cleanupInvocationCounter;
|
|
100
|
+
private readonly cleanupEveryCalls;
|
|
101
|
+
constructor(provider: SessionProvider);
|
|
102
|
+
/**
|
|
103
|
+
* Define configuration for a specific origin
|
|
104
|
+
*/
|
|
105
|
+
defineOrigin(origin: string, config: SessionManagerConfig): void;
|
|
106
|
+
/**
|
|
107
|
+
* Check if an origin is defined
|
|
108
|
+
*/
|
|
109
|
+
hasOrigin(origin: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Get configuration for a specific origin, throw error if not defined
|
|
112
|
+
*/
|
|
113
|
+
private getConfigForOrigin;
|
|
114
|
+
generateSessionId(): string;
|
|
115
|
+
private maybeCleanupExpired;
|
|
116
|
+
/**
|
|
117
|
+
* Get the cleanup every calls threshold
|
|
118
|
+
*/
|
|
119
|
+
get cleanupThreshold(): number;
|
|
120
|
+
generateRefreshToken(userId: string, deviceId: string | undefined, origin: string, options?: {
|
|
121
|
+
type?: 'refresh' | 'session';
|
|
122
|
+
}): Promise<{
|
|
123
|
+
token: string;
|
|
124
|
+
sessionId: string;
|
|
125
|
+
absoluteExpiresAt: string;
|
|
126
|
+
}>;
|
|
127
|
+
validateAccessToken(token: string, origin: string): {
|
|
128
|
+
isValid: true;
|
|
129
|
+
payload: AccessTokenPayload;
|
|
130
|
+
} | {
|
|
131
|
+
isValid: false;
|
|
132
|
+
payload: null;
|
|
133
|
+
};
|
|
134
|
+
validateRefreshToken(token: string, origin: string): Promise<ValidateRefreshTokenResult>;
|
|
135
|
+
invalidateRefreshToken(origin: string, userId: string, deviceId?: string): Promise<void>;
|
|
136
|
+
generateAccessToken(refreshToken: string, origin: string): Promise<{
|
|
137
|
+
token: string;
|
|
138
|
+
} | {
|
|
139
|
+
error: string;
|
|
140
|
+
}>;
|
|
141
|
+
rotateRefreshToken(refreshToken: string, origin: string): Promise<{
|
|
142
|
+
token: string;
|
|
143
|
+
sessionId: string;
|
|
144
|
+
absoluteExpiresAt: string;
|
|
145
|
+
type: 'refresh' | 'session';
|
|
146
|
+
} | {
|
|
147
|
+
error: string;
|
|
148
|
+
}>;
|
|
149
|
+
/**
|
|
150
|
+
* Returns true when a session exists and is not expired.
|
|
151
|
+
* If the session exists but is expired, it will be deleted as part of this check.
|
|
152
|
+
*/
|
|
153
|
+
isSessionActive(sessionId: string, origin: string): Promise<boolean>;
|
|
154
|
+
}
|
|
155
|
+
declare const createDatabaseProvider: (db: Database, contentType: string) => SessionProvider;
|
|
156
|
+
declare const createSessionManager: ({ db, }: {
|
|
157
|
+
db: Database;
|
|
158
|
+
}) => SessionManager & ((origin: string) => OriginSessionManager);
|
|
159
|
+
export { createSessionManager, createDatabaseProvider };
|
|
160
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/services/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
var jwt = require('jsonwebtoken');
|
|
5
|
+
var constants = require('../constants.js');
|
|
6
|
+
|
|
7
|
+
class DatabaseSessionProvider {
|
|
8
|
+
async create(session) {
|
|
9
|
+
const result = await this.db.query(this.contentType).create({
|
|
10
|
+
data: session
|
|
11
|
+
});
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
async findBySessionId(sessionId) {
|
|
15
|
+
const result = await this.db.query(this.contentType).findOne({
|
|
16
|
+
where: {
|
|
17
|
+
sessionId
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
async updateBySessionId(sessionId, data) {
|
|
23
|
+
await this.db.query(this.contentType).update({
|
|
24
|
+
where: {
|
|
25
|
+
sessionId
|
|
26
|
+
},
|
|
27
|
+
data
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async deleteBySessionId(sessionId) {
|
|
31
|
+
await this.db.query(this.contentType).delete({
|
|
32
|
+
where: {
|
|
33
|
+
sessionId
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async deleteExpired() {
|
|
38
|
+
await this.db.query(this.contentType).deleteMany({
|
|
39
|
+
where: {
|
|
40
|
+
absoluteExpiresAt: {
|
|
41
|
+
$lt: new Date()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async deleteBy(criteria) {
|
|
47
|
+
await this.db.query(this.contentType).deleteMany({
|
|
48
|
+
where: {
|
|
49
|
+
...criteria.userId ? {
|
|
50
|
+
userId: criteria.userId
|
|
51
|
+
} : {},
|
|
52
|
+
...criteria.origin ? {
|
|
53
|
+
origin: criteria.origin
|
|
54
|
+
} : {},
|
|
55
|
+
...criteria.deviceId ? {
|
|
56
|
+
deviceId: criteria.deviceId
|
|
57
|
+
} : {}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
constructor(db, contentType){
|
|
62
|
+
this.db = db;
|
|
63
|
+
this.contentType = contentType;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
class OriginSessionManager {
|
|
67
|
+
async generateRefreshToken(userId, deviceId, options) {
|
|
68
|
+
return this.sessionManager.generateRefreshToken(userId, deviceId, this.origin, options);
|
|
69
|
+
}
|
|
70
|
+
async generateAccessToken(refreshToken) {
|
|
71
|
+
return this.sessionManager.generateAccessToken(refreshToken, this.origin);
|
|
72
|
+
}
|
|
73
|
+
async rotateRefreshToken(refreshToken) {
|
|
74
|
+
return this.sessionManager.rotateRefreshToken(refreshToken, this.origin);
|
|
75
|
+
}
|
|
76
|
+
validateAccessToken(token) {
|
|
77
|
+
return this.sessionManager.validateAccessToken(token, this.origin);
|
|
78
|
+
}
|
|
79
|
+
async validateRefreshToken(token) {
|
|
80
|
+
return this.sessionManager.validateRefreshToken(token, this.origin);
|
|
81
|
+
}
|
|
82
|
+
async invalidateRefreshToken(userId, deviceId) {
|
|
83
|
+
return this.sessionManager.invalidateRefreshToken(this.origin, userId, deviceId);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns true when a session exists and is not expired for this origin.
|
|
87
|
+
* If the session exists but is expired, it will be deleted as part of this check.
|
|
88
|
+
*/ async isSessionActive(sessionId) {
|
|
89
|
+
return this.sessionManager.isSessionActive(sessionId, this.origin);
|
|
90
|
+
}
|
|
91
|
+
constructor(sessionManager, origin){
|
|
92
|
+
this.sessionManager = sessionManager;
|
|
93
|
+
this.origin = origin;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
class SessionManager {
|
|
97
|
+
/**
|
|
98
|
+
* Define configuration for a specific origin
|
|
99
|
+
*/ defineOrigin(origin, config) {
|
|
100
|
+
this.originConfigs.set(origin, config);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if an origin is defined
|
|
104
|
+
*/ hasOrigin(origin) {
|
|
105
|
+
return this.originConfigs.has(origin);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get configuration for a specific origin, throw error if not defined
|
|
109
|
+
*/ getConfigForOrigin(origin) {
|
|
110
|
+
const originConfig = this.originConfigs.get(origin);
|
|
111
|
+
if (originConfig) {
|
|
112
|
+
return originConfig;
|
|
113
|
+
}
|
|
114
|
+
throw new Error(`SessionManager: Origin '${origin}' is not defined. Please define it using defineOrigin('${origin}', config).`);
|
|
115
|
+
}
|
|
116
|
+
generateSessionId() {
|
|
117
|
+
return crypto.randomBytes(16).toString('hex');
|
|
118
|
+
}
|
|
119
|
+
async maybeCleanupExpired() {
|
|
120
|
+
this.cleanupInvocationCounter += 1;
|
|
121
|
+
if (this.cleanupInvocationCounter >= this.cleanupEveryCalls) {
|
|
122
|
+
this.cleanupInvocationCounter = 0;
|
|
123
|
+
await this.provider.deleteExpired();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the cleanup every calls threshold
|
|
128
|
+
*/ get cleanupThreshold() {
|
|
129
|
+
return this.cleanupEveryCalls;
|
|
130
|
+
}
|
|
131
|
+
async generateRefreshToken(userId, deviceId, origin, options) {
|
|
132
|
+
if (!origin || typeof origin !== 'string') {
|
|
133
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
134
|
+
}
|
|
135
|
+
await this.maybeCleanupExpired();
|
|
136
|
+
const config = this.getConfigForOrigin(origin);
|
|
137
|
+
const sessionId = this.generateSessionId();
|
|
138
|
+
const tokenType = options?.type ?? 'refresh';
|
|
139
|
+
const isRefresh = tokenType === 'refresh';
|
|
140
|
+
const idleLifespan = isRefresh ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;
|
|
141
|
+
const maxLifespan = isRefresh ? config.maxRefreshTokenLifespan : config.maxSessionLifespan;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const expiresAt = new Date(now + idleLifespan * 1000);
|
|
144
|
+
const absoluteExpiresAt = new Date(now + maxLifespan * 1000);
|
|
145
|
+
// Create the root record first so createdAt can be used for signing.
|
|
146
|
+
const record = await this.provider.create({
|
|
147
|
+
userId,
|
|
148
|
+
sessionId,
|
|
149
|
+
...deviceId && {
|
|
150
|
+
deviceId
|
|
151
|
+
},
|
|
152
|
+
origin,
|
|
153
|
+
childId: null,
|
|
154
|
+
type: tokenType,
|
|
155
|
+
status: 'active',
|
|
156
|
+
expiresAt,
|
|
157
|
+
absoluteExpiresAt
|
|
158
|
+
});
|
|
159
|
+
const issuedAtSeconds = Math.floor(new Date(record.createdAt ?? new Date()).getTime() / 1000);
|
|
160
|
+
const expiresAtSeconds = Math.floor(new Date(record.expiresAt).getTime() / 1000);
|
|
161
|
+
const payload = {
|
|
162
|
+
userId,
|
|
163
|
+
sessionId,
|
|
164
|
+
type: 'refresh',
|
|
165
|
+
iat: issuedAtSeconds,
|
|
166
|
+
exp: expiresAtSeconds
|
|
167
|
+
};
|
|
168
|
+
const token = jwt.sign(payload, config.jwtSecret, {
|
|
169
|
+
algorithm: constants.DEFAULT_ALGORITHM,
|
|
170
|
+
noTimestamp: true
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
token,
|
|
174
|
+
sessionId,
|
|
175
|
+
absoluteExpiresAt: absoluteExpiresAt.toISOString()
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
validateAccessToken(token, origin) {
|
|
179
|
+
if (!origin || typeof origin !== 'string') {
|
|
180
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const config = this.getConfigForOrigin(origin);
|
|
184
|
+
const payload = jwt.verify(token, config.jwtSecret, {
|
|
185
|
+
algorithms: [
|
|
186
|
+
constants.DEFAULT_ALGORITHM
|
|
187
|
+
]
|
|
188
|
+
});
|
|
189
|
+
// Ensure this is an access token
|
|
190
|
+
if (!payload || payload.type !== 'access') {
|
|
191
|
+
return {
|
|
192
|
+
isValid: false,
|
|
193
|
+
payload: null
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
isValid: true,
|
|
198
|
+
payload
|
|
199
|
+
};
|
|
200
|
+
} catch (err) {
|
|
201
|
+
return {
|
|
202
|
+
isValid: false,
|
|
203
|
+
payload: null
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async validateRefreshToken(token, origin) {
|
|
208
|
+
if (!origin || typeof origin !== 'string') {
|
|
209
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
const config = this.getConfigForOrigin(origin);
|
|
213
|
+
const verifyOptions = {
|
|
214
|
+
algorithms: [
|
|
215
|
+
constants.DEFAULT_ALGORITHM
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
const payload = jwt.verify(token, config.jwtSecret, verifyOptions);
|
|
219
|
+
if (payload.type !== 'refresh') {
|
|
220
|
+
return {
|
|
221
|
+
isValid: false
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const session = await this.provider.findBySessionId(payload.sessionId);
|
|
225
|
+
if (!session) {
|
|
226
|
+
return {
|
|
227
|
+
isValid: false
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const now = new Date();
|
|
231
|
+
if (new Date(session.expiresAt) <= now) {
|
|
232
|
+
return {
|
|
233
|
+
isValid: false
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// Absolute family expiry check
|
|
237
|
+
if (session.absoluteExpiresAt && new Date(session.absoluteExpiresAt) <= now) {
|
|
238
|
+
return {
|
|
239
|
+
isValid: false
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// Only 'active' sessions are eligible to create access tokens.
|
|
243
|
+
if (session.status !== 'active') {
|
|
244
|
+
return {
|
|
245
|
+
isValid: false
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (session.userId !== payload.userId) {
|
|
249
|
+
return {
|
|
250
|
+
isValid: false
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
isValid: true,
|
|
255
|
+
userId: payload.userId,
|
|
256
|
+
sessionId: payload.sessionId
|
|
257
|
+
};
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error instanceof jwt.JsonWebTokenError) {
|
|
260
|
+
return {
|
|
261
|
+
isValid: false
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async invalidateRefreshToken(origin, userId, deviceId) {
|
|
268
|
+
await this.provider.deleteBy({
|
|
269
|
+
userId,
|
|
270
|
+
origin,
|
|
271
|
+
deviceId
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async generateAccessToken(refreshToken, origin) {
|
|
275
|
+
if (!origin || typeof origin !== 'string') {
|
|
276
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
277
|
+
}
|
|
278
|
+
const validation = await this.validateRefreshToken(refreshToken, origin);
|
|
279
|
+
if (!validation.isValid) {
|
|
280
|
+
return {
|
|
281
|
+
error: 'invalid_refresh_token'
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const payload = {
|
|
285
|
+
userId: String(validation.userId),
|
|
286
|
+
sessionId: validation.sessionId,
|
|
287
|
+
type: 'access'
|
|
288
|
+
};
|
|
289
|
+
const config = this.getConfigForOrigin(origin);
|
|
290
|
+
const token = jwt.sign(payload, config.jwtSecret, {
|
|
291
|
+
algorithm: constants.DEFAULT_ALGORITHM,
|
|
292
|
+
expiresIn: config.accessTokenLifespan
|
|
293
|
+
});
|
|
294
|
+
return {
|
|
295
|
+
token
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async rotateRefreshToken(refreshToken, origin) {
|
|
299
|
+
if (!origin || typeof origin !== 'string') {
|
|
300
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const config = this.getConfigForOrigin(origin);
|
|
304
|
+
const payload = jwt.verify(refreshToken, config.jwtSecret, {
|
|
305
|
+
algorithms: [
|
|
306
|
+
constants.DEFAULT_ALGORITHM
|
|
307
|
+
]
|
|
308
|
+
});
|
|
309
|
+
if (!payload || payload.type !== 'refresh') {
|
|
310
|
+
return {
|
|
311
|
+
error: 'invalid_refresh_token'
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const current = await this.provider.findBySessionId(payload.sessionId);
|
|
315
|
+
if (!current) {
|
|
316
|
+
return {
|
|
317
|
+
error: 'invalid_refresh_token'
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// If parent already has a child, return the same child token
|
|
321
|
+
if (current.childId) {
|
|
322
|
+
const child = await this.provider.findBySessionId(current.childId);
|
|
323
|
+
if (child) {
|
|
324
|
+
const childIat = Math.floor(new Date(child.createdAt ?? new Date()).getTime() / 1000);
|
|
325
|
+
const childExp = Math.floor(new Date(child.expiresAt).getTime() / 1000);
|
|
326
|
+
const childPayload = {
|
|
327
|
+
userId: child.userId,
|
|
328
|
+
sessionId: child.sessionId,
|
|
329
|
+
type: 'refresh',
|
|
330
|
+
iat: childIat,
|
|
331
|
+
exp: childExp
|
|
332
|
+
};
|
|
333
|
+
const childToken = jwt.sign(childPayload, config.jwtSecret, {
|
|
334
|
+
algorithm: constants.DEFAULT_ALGORITHM,
|
|
335
|
+
noTimestamp: true
|
|
336
|
+
});
|
|
337
|
+
let absoluteExpiresAt;
|
|
338
|
+
if (child.absoluteExpiresAt) {
|
|
339
|
+
absoluteExpiresAt = typeof child.absoluteExpiresAt === 'string' ? child.absoluteExpiresAt : child.absoluteExpiresAt.toISOString();
|
|
340
|
+
} else {
|
|
341
|
+
absoluteExpiresAt = new Date(0).toISOString();
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
token: childToken,
|
|
345
|
+
sessionId: child.sessionId,
|
|
346
|
+
absoluteExpiresAt,
|
|
347
|
+
type: child.type ?? 'refresh'
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const now = Date.now();
|
|
352
|
+
const tokenType = current.type ?? 'refresh';
|
|
353
|
+
const idleLifespan = tokenType === 'refresh' ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;
|
|
354
|
+
// Enforce idle window since creation of the current token
|
|
355
|
+
if (current.createdAt && now - new Date(current.createdAt).getTime() > idleLifespan * 1000) {
|
|
356
|
+
return {
|
|
357
|
+
error: 'idle_window_elapsed'
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
// Enforce max family window using absoluteExpiresAt
|
|
361
|
+
const absolute = current.absoluteExpiresAt ? new Date(current.absoluteExpiresAt).getTime() : now;
|
|
362
|
+
if (absolute <= now) {
|
|
363
|
+
return {
|
|
364
|
+
error: 'max_window_elapsed'
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
// Create child token
|
|
368
|
+
const childSessionId = this.generateSessionId();
|
|
369
|
+
const childExpiresAt = new Date(now + idleLifespan * 1000);
|
|
370
|
+
const childRecord = await this.provider.create({
|
|
371
|
+
userId: current.userId,
|
|
372
|
+
sessionId: childSessionId,
|
|
373
|
+
...current.deviceId && {
|
|
374
|
+
deviceId: current.deviceId
|
|
375
|
+
},
|
|
376
|
+
origin: current.origin,
|
|
377
|
+
childId: null,
|
|
378
|
+
type: tokenType,
|
|
379
|
+
status: 'active',
|
|
380
|
+
expiresAt: childExpiresAt,
|
|
381
|
+
absoluteExpiresAt: current.absoluteExpiresAt ?? new Date(absolute)
|
|
382
|
+
});
|
|
383
|
+
const childIat = Math.floor(new Date(childRecord.createdAt ?? new Date()).getTime() / 1000);
|
|
384
|
+
const childExp = Math.floor(new Date(childRecord.expiresAt).getTime() / 1000);
|
|
385
|
+
const payloadOut = {
|
|
386
|
+
userId: current.userId,
|
|
387
|
+
sessionId: childSessionId,
|
|
388
|
+
type: 'refresh',
|
|
389
|
+
iat: childIat,
|
|
390
|
+
exp: childExp
|
|
391
|
+
};
|
|
392
|
+
const childToken = jwt.sign(payloadOut, config.jwtSecret, {
|
|
393
|
+
algorithm: constants.DEFAULT_ALGORITHM,
|
|
394
|
+
noTimestamp: true
|
|
395
|
+
});
|
|
396
|
+
await this.provider.updateBySessionId(current.sessionId, {
|
|
397
|
+
status: 'rotated',
|
|
398
|
+
childId: childSessionId
|
|
399
|
+
});
|
|
400
|
+
let absoluteExpiresAt;
|
|
401
|
+
if (childRecord.absoluteExpiresAt) {
|
|
402
|
+
absoluteExpiresAt = typeof childRecord.absoluteExpiresAt === 'string' ? childRecord.absoluteExpiresAt : childRecord.absoluteExpiresAt.toISOString();
|
|
403
|
+
} else {
|
|
404
|
+
absoluteExpiresAt = new Date(absolute).toISOString();
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
token: childToken,
|
|
408
|
+
sessionId: childSessionId,
|
|
409
|
+
absoluteExpiresAt,
|
|
410
|
+
type: tokenType
|
|
411
|
+
};
|
|
412
|
+
} catch {
|
|
413
|
+
return {
|
|
414
|
+
error: 'invalid_refresh_token'
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Returns true when a session exists and is not expired.
|
|
420
|
+
* If the session exists but is expired, it will be deleted as part of this check.
|
|
421
|
+
*/ async isSessionActive(sessionId, origin) {
|
|
422
|
+
const session = await this.provider.findBySessionId(sessionId);
|
|
423
|
+
if (!session) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
if (session.origin !== origin) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (new Date(session.expiresAt) <= new Date()) {
|
|
430
|
+
// Clean up expired session eagerly
|
|
431
|
+
await this.provider.deleteBySessionId(sessionId);
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
constructor(provider){
|
|
437
|
+
// Store origin-specific configurations
|
|
438
|
+
this.originConfigs = new Map();
|
|
439
|
+
// Run expired cleanup only every N calls to avoid extra queries
|
|
440
|
+
this.cleanupInvocationCounter = 0;
|
|
441
|
+
this.cleanupEveryCalls = 50;
|
|
442
|
+
this.provider = provider;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const createDatabaseProvider = (db, contentType)=>{
|
|
446
|
+
return new DatabaseSessionProvider(db, contentType);
|
|
447
|
+
};
|
|
448
|
+
const createSessionManager = ({ db })=>{
|
|
449
|
+
const provider = createDatabaseProvider(db, 'admin::session');
|
|
450
|
+
const sessionManager = new SessionManager(provider);
|
|
451
|
+
// Add callable functionality
|
|
452
|
+
const fluentApi = (origin)=>{
|
|
453
|
+
if (!origin || typeof origin !== 'string') {
|
|
454
|
+
throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
|
|
455
|
+
}
|
|
456
|
+
return new OriginSessionManager(sessionManager, origin);
|
|
457
|
+
};
|
|
458
|
+
// Attach only the public SessionManagerService API to the callable
|
|
459
|
+
const api = fluentApi;
|
|
460
|
+
api.generateSessionId = sessionManager.generateSessionId.bind(sessionManager);
|
|
461
|
+
api.defineOrigin = sessionManager.defineOrigin.bind(sessionManager);
|
|
462
|
+
api.hasOrigin = sessionManager.hasOrigin.bind(sessionManager);
|
|
463
|
+
// Note: isSessionActive is origin-scoped and exposed on OriginSessionManager only
|
|
464
|
+
// Forward the cleanupThreshold getter (used in tests)
|
|
465
|
+
Object.defineProperty(api, 'cleanupThreshold', {
|
|
466
|
+
get () {
|
|
467
|
+
return sessionManager.cleanupThreshold;
|
|
468
|
+
},
|
|
469
|
+
enumerable: true
|
|
470
|
+
});
|
|
471
|
+
return api;
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
exports.createDatabaseProvider = createDatabaseProvider;
|
|
475
|
+
exports.createSessionManager = createSessionManager;
|
|
476
|
+
//# sourceMappingURL=session-manager.js.map
|