@baseplate-dev/plugin-auth 0.1.1
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/LICENSE +390 -0
- package/dist/auth/core/common.d.ts +7 -0
- package/dist/auth/core/common.d.ts.map +1 -0
- package/dist/auth/core/common.js +26 -0
- package/dist/auth/core/common.js.map +1 -0
- package/dist/auth/core/components/auth-definition-editor.d.ts +5 -0
- package/dist/auth/core/components/auth-definition-editor.d.ts.map +1 -0
- package/dist/auth/core/components/auth-definition-editor.js +57 -0
- package/dist/auth/core/components/auth-definition-editor.js.map +1 -0
- package/dist/auth/core/components/role-editor-form.d.ts +10 -0
- package/dist/auth/core/components/role-editor-form.d.ts.map +1 -0
- package/dist/auth/core/components/role-editor-form.js +27 -0
- package/dist/auth/core/components/role-editor-form.js.map +1 -0
- package/dist/auth/core/node.d.ts +5 -0
- package/dist/auth/core/node.d.ts.map +1 -0
- package/dist/auth/core/node.js +58 -0
- package/dist/auth/core/node.js.map +1 -0
- package/dist/auth/core/schema/models.d.ts +9 -0
- package/dist/auth/core/schema/models.d.ts.map +1 -0
- package/dist/auth/core/schema/models.js +187 -0
- package/dist/auth/core/schema/models.js.map +1 -0
- package/dist/auth/core/schema/plugin-definition.d.ts +88 -0
- package/dist/auth/core/schema/plugin-definition.d.ts.map +1 -0
- package/dist/auth/core/schema/plugin-definition.js +29 -0
- package/dist/auth/core/schema/plugin-definition.js.map +1 -0
- package/dist/auth/core/web.d.ts +6 -0
- package/dist/auth/core/web.d.ts.map +1 -0
- package/dist/auth/core/web.js +14 -0
- package/dist/auth/core/web.js.map +1 -0
- package/dist/auth/generators/fastify/auth-module/auth-module.generator.d.ts +65 -0
- package/dist/auth/generators/fastify/auth-module/auth-module.generator.d.ts.map +1 -0
- package/dist/auth/generators/fastify/auth-module/auth-module.generator.js +62 -0
- package/dist/auth/generators/fastify/auth-module/auth-module.generator.js.map +1 -0
- package/dist/auth/generators/fastify/auth-module/generated/ts-templates.d.ts +96 -0
- package/dist/auth/generators/fastify/auth-module/generated/ts-templates.d.ts.map +1 -0
- package/dist/auth/generators/fastify/auth-module/generated/ts-templates.js +94 -0
- package/dist/auth/generators/fastify/auth-module/generated/ts-templates.js.map +1 -0
- package/dist/auth/generators/fastify/auth-module/templates/management.ts +22 -0
- package/dist/auth/generators/fastify/auth-module/templates/schema/user-session-payload.object-type.ts +23 -0
- package/dist/auth/generators/fastify/auth-module/templates/schema/user-session.mutations.ts +22 -0
- package/dist/auth/generators/fastify/auth-module/templates/schema/user-session.queries.ts +20 -0
- package/dist/auth/generators/fastify/auth-module/templates/services/user-session.service.ts +251 -0
- package/dist/auth/generators/fastify/auth-module/templates/user-session.constants.ts +19 -0
- package/dist/auth/generators/fastify/auth-module/templates/user-session.service.ts +101 -0
- package/dist/auth/generators/fastify/auth-module/templates/utils/cookie-signer.ts +71 -0
- package/dist/auth/generators/fastify/auth-module/templates/utils/session-cookie.ts +42 -0
- package/dist/auth/generators/fastify/auth-module/templates/utils/verify-request-origin.ts +45 -0
- package/dist/auth/generators/fastify/index.d.ts +2 -0
- package/dist/auth/generators/fastify/index.d.ts.map +1 -0
- package/dist/auth/generators/fastify/index.js +2 -0
- package/dist/auth/generators/fastify/index.js.map +1 -0
- package/dist/auth/generators/index.d.ts +2 -0
- package/dist/auth/generators/index.d.ts.map +1 -0
- package/dist/auth/generators/index.js +2 -0
- package/dist/auth/generators/index.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/metadata.json +8 -0
- package/dist/auth/static/icon.svg +1 -0
- package/dist/auth0/constants/packages.d.ts +6 -0
- package/dist/auth0/constants/packages.d.ts.map +1 -0
- package/dist/auth0/constants/packages.js +6 -0
- package/dist/auth0/constants/packages.js.map +1 -0
- package/dist/auth0/core/common.d.ts +7 -0
- package/dist/auth0/core/common.d.ts.map +1 -0
- package/dist/auth0/core/common.js +28 -0
- package/dist/auth0/core/common.js.map +1 -0
- package/dist/auth0/core/components/auth-definition-editor.d.ts +5 -0
- package/dist/auth0/core/components/auth-definition-editor.d.ts.map +1 -0
- package/dist/auth0/core/components/auth-definition-editor.js +57 -0
- package/dist/auth0/core/components/auth-definition-editor.js.map +1 -0
- package/dist/auth0/core/components/role-dialog.d.ts +14 -0
- package/dist/auth0/core/components/role-dialog.d.ts.map +1 -0
- package/dist/auth0/core/components/role-dialog.js +27 -0
- package/dist/auth0/core/components/role-dialog.js.map +1 -0
- package/dist/auth0/core/components/role-editor-form.d.ts +10 -0
- package/dist/auth0/core/components/role-editor-form.d.ts.map +1 -0
- package/dist/auth0/core/components/role-editor-form.js +52 -0
- package/dist/auth0/core/components/role-editor-form.js.map +1 -0
- package/dist/auth0/core/node.d.ts +5 -0
- package/dist/auth0/core/node.d.ts.map +1 -0
- package/dist/auth0/core/node.js +86 -0
- package/dist/auth0/core/node.js.map +1 -0
- package/dist/auth0/core/schema/migrations.d.ts +3 -0
- package/dist/auth0/core/schema/migrations.d.ts.map +1 -0
- package/dist/auth0/core/schema/migrations.js +17 -0
- package/dist/auth0/core/schema/migrations.js.map +1 -0
- package/dist/auth0/core/schema/models.d.ts +6 -0
- package/dist/auth0/core/schema/models.d.ts.map +1 -0
- package/dist/auth0/core/schema/models.js +33 -0
- package/dist/auth0/core/schema/models.js.map +1 -0
- package/dist/auth0/core/schema/plugin-definition.d.ts +73 -0
- package/dist/auth0/core/schema/plugin-definition.d.ts.map +1 -0
- package/dist/auth0/core/schema/plugin-definition.js +17 -0
- package/dist/auth0/core/schema/plugin-definition.js.map +1 -0
- package/dist/auth0/core/web.d.ts +6 -0
- package/dist/auth0/core/web.d.ts.map +1 -0
- package/dist/auth0/core/web.js +14 -0
- package/dist/auth0/core/web.js.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/auth0-module.generator.d.ts +67 -0
- package/dist/auth0/generators/fastify/auth0-module/auth0-module.generator.d.ts.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/auth0-module.generator.js +125 -0
- package/dist/auth0/generators/fastify/auth0-module/auth0-module.generator.js.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-import-maps.d.ts +3 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-import-maps.d.ts.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-import-maps.js +12 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-import-maps.js.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-templates.d.ts +43 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-templates.d.ts.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-templates.js +25 -0
- package/dist/auth0/generators/fastify/auth0-module/generated/ts-templates.js.map +1 -0
- package/dist/auth0/generators/fastify/auth0-module/templates/management.ts +22 -0
- package/dist/auth0/generators/fastify/auth0-module/templates/user-session.service.ts +101 -0
- package/dist/auth0/generators/fastify/index.d.ts +2 -0
- package/dist/auth0/generators/fastify/index.d.ts.map +1 -0
- package/dist/auth0/generators/fastify/index.js +2 -0
- package/dist/auth0/generators/fastify/index.js.map +1 -0
- package/dist/auth0/generators/index.d.ts +3 -0
- package/dist/auth0/generators/index.d.ts.map +1 -0
- package/dist/auth0/generators/index.js +3 -0
- package/dist/auth0/generators/index.js.map +1 -0
- package/dist/auth0/generators/react/auth0-apollo/auth0-apollo.generator.d.ts +10 -0
- package/dist/auth0/generators/react/auth0-apollo/auth0-apollo.generator.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-apollo/auth0-apollo.generator.js +41 -0
- package/dist/auth0/generators/react/auth0-apollo/auth0-apollo.generator.js.map +1 -0
- package/dist/auth0/generators/react/auth0-apollo/templates/auth-link.ts +11 -0
- package/dist/auth0/generators/react/auth0-callback/auth0-callback.generator.d.ts +61 -0
- package/dist/auth0/generators/react/auth0-callback/auth0-callback.generator.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-callback/auth0-callback.generator.js +59 -0
- package/dist/auth0/generators/react/auth0-callback/auth0-callback.generator.js.map +1 -0
- package/dist/auth0/generators/react/auth0-callback/generated/ts-templates.d.ts +106 -0
- package/dist/auth0/generators/react/auth0-callback/generated/ts-templates.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-callback/generated/ts-templates.js +28 -0
- package/dist/auth0/generators/react/auth0-callback/generated/ts-templates.js.map +1 -0
- package/dist/auth0/generators/react/auth0-callback/templates/auth0-callback.page.tsx +61 -0
- package/dist/auth0/generators/react/auth0-callback/templates/signup.page.tsx +48 -0
- package/dist/auth0/generators/react/auth0-components/auth0-components.generator.d.ts +53 -0
- package/dist/auth0/generators/react/auth0-components/auth0-components.generator.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-components/auth0-components.generator.js +44 -0
- package/dist/auth0/generators/react/auth0-components/auth0-components.generator.js.map +1 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-import-maps.d.ts +3 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-import-maps.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-import-maps.js +12 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-import-maps.js.map +1 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-templates.d.ts +45 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-templates.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-templates.js +13 -0
- package/dist/auth0/generators/react/auth0-components/generated/ts-templates.js.map +1 -0
- package/dist/auth0/generators/react/auth0-components/templates/RequireAuth.tsx +22 -0
- package/dist/auth0/generators/react/auth0-hooks/auth0-hooks.generator.d.ts +28 -0
- package/dist/auth0/generators/react/auth0-hooks/auth0-hooks.generator.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/auth0-hooks.generator.js +65 -0
- package/dist/auth0/generators/react/auth0-hooks/auth0-hooks.generator.js.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/text-templates.d.ts +8 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/text-templates.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/text-templates.js +12 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/text-templates.js.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-import-maps.d.ts +3 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-import-maps.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-import-maps.js +16 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-import-maps.js.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-templates.d.ts +33 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-templates.d.ts.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-templates.js +50 -0
- package/dist/auth0/generators/react/auth0-hooks/generated/ts-templates.js.map +1 -0
- package/dist/auth0/generators/react/auth0-hooks/templates/useCurrentUser.gql +10 -0
- package/dist/auth0/generators/react/auth0-hooks/templates/useCurrentUser.ts +29 -0
- package/dist/auth0/generators/react/auth0-hooks/templates/useLogOut.ts +14 -0
- package/dist/auth0/generators/react/auth0-hooks/templates/useRequiredUserId.ts +11 -0
- package/dist/auth0/generators/react/auth0-hooks/templates/useSession.ts +23 -0
- package/dist/auth0/generators/react/index.d.ts +6 -0
- package/dist/auth0/generators/react/index.d.ts.map +1 -0
- package/dist/auth0/generators/react/index.js +6 -0
- package/dist/auth0/generators/react/index.js.map +1 -0
- package/dist/auth0/generators/react/react-auth0/react-auth0.generator.d.ts +23 -0
- package/dist/auth0/generators/react/react-auth0/react-auth0.generator.d.ts.map +1 -0
- package/dist/auth0/generators/react/react-auth0/react-auth0.generator.js +64 -0
- package/dist/auth0/generators/react/react-auth0/react-auth0.generator.js.map +1 -0
- package/dist/auth0/metadata.json +8 -0
- package/dist/auth0/static/icon.svg +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/roles/constants.d.ts +4 -0
- package/dist/roles/constants.d.ts.map +1 -0
- package/dist/roles/constants.js +25 -0
- package/dist/roles/constants.js.map +1 -0
- package/dist/roles/index.d.ts +3 -0
- package/dist/roles/index.d.ts.map +1 -0
- package/dist/roles/index.js +3 -0
- package/dist/roles/index.js.map +1 -0
- package/dist/roles/schema.d.ts +65 -0
- package/dist/roles/schema.d.ts.map +1 -0
- package/dist/roles/schema.js +43 -0
- package/dist/roles/schema.js.map +1 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +2 -0
- package/dist/utils/cn.js.map +1 -0
- package/dist/web/assets/__federation_expose_auth0CoreCommon--n6esVYP.js +54 -0
- package/dist/web/assets/__federation_expose_auth0CoreCommon--n6esVYP.js.map +1 -0
- package/dist/web/assets/__federation_expose_auth0CoreWeb-Ql6gpX5q.js +377 -0
- package/dist/web/assets/__federation_expose_auth0CoreWeb-Ql6gpX5q.js.map +1 -0
- package/dist/web/assets/__federation_fn_import-BiwmVFwy.js +421 -0
- package/dist/web/assets/__federation_fn_import-BiwmVFwy.js.map +1 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/project-builder-lib/web-CY8oz43s.js +330 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/project-builder-lib/web-CY8oz43s.js.map +1 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/project-builder-lib-B62IPizQ.js +10559 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/project-builder-lib-B62IPizQ.js.map +1 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/ui-components-BU2QTWNs.js +12718 -0
- package/dist/web/assets/__federation_shared_@baseplate-dev/ui-components-BU2QTWNs.js.map +1 -0
- package/dist/web/assets/__federation_shared_react-dom-Bwq4pLax.js +250 -0
- package/dist/web/assets/__federation_shared_react-dom-Bwq4pLax.js.map +1 -0
- package/dist/web/assets/__federation_shared_react-uocnGSER.js +7 -0
- package/dist/web/assets/__federation_shared_react-uocnGSER.js.map +1 -0
- package/dist/web/assets/__federation_shared_zod-BK7IELHt.js +4380 -0
- package/dist/web/assets/__federation_shared_zod-BK7IELHt.js.map +1 -0
- package/dist/web/assets/index-B1hOKVPw.js +574 -0
- package/dist/web/assets/index-B1hOKVPw.js.map +1 -0
- package/dist/web/assets/index-CgJx-krK.js +95 -0
- package/dist/web/assets/index-CgJx-krK.js.map +1 -0
- package/dist/web/assets/index-l0sNRNKZ.js.map +1 -0
- package/dist/web/assets/index.esm-BcxPd7Ha.js +2584 -0
- package/dist/web/assets/index.esm-BcxPd7Ha.js.map +1 -0
- package/dist/web/assets/model-utils-CbQJy1wE.js +1263 -0
- package/dist/web/assets/model-utils-CbQJy1wE.js.map +1 -0
- package/dist/web/assets/plugin-definition-BQRupXoQ.js +78 -0
- package/dist/web/assets/plugin-definition-BQRupXoQ.js.map +1 -0
- package/dist/web/assets/react-2jQE8aG0.js +47 -0
- package/dist/web/assets/react-2jQE8aG0.js.map +1 -0
- package/dist/web/assets/remoteEntry.js +54 -0
- package/dist/web/assets/remoteEntry.js.map +1 -0
- package/dist/web/assets/style-BdgwWjAf.css +95 -0
- package/dist/web/index.html +24 -0
- package/dist/web-export.d.ts +2 -0
- package/dist/web-export.js +2 -0
- package/dist/web-export.js.map +1 -0
- package/manifest.json +4 -0
- package/package.json +80 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createTsTemplateFile, createTsTemplateGroup, } from '@baseplate-dev/core-generators';
|
|
2
|
+
import { authContextImportsProvider, authRolesImportsProvider, configServiceImportsProvider, errorHandlerServiceImportsProvider, pothosImportsProvider, requestServiceContextImportsProvider, userSessionTypesImportsProvider, } from '@baseplate-dev/fastify-generators';
|
|
3
|
+
const schemaUserSessionMutations = createTsTemplateFile({
|
|
4
|
+
group: 'schema',
|
|
5
|
+
importMapProviders: { pothosImports: pothosImportsProvider },
|
|
6
|
+
name: 'schema-user-session-mutations',
|
|
7
|
+
projectExports: {},
|
|
8
|
+
source: { path: 'schema/user-session.mutations.ts' },
|
|
9
|
+
variables: {},
|
|
10
|
+
});
|
|
11
|
+
const schemaUserSessionQueries = createTsTemplateFile({
|
|
12
|
+
group: 'schema',
|
|
13
|
+
importMapProviders: { pothosImports: pothosImportsProvider },
|
|
14
|
+
name: 'schema-user-session-queries',
|
|
15
|
+
projectExports: {},
|
|
16
|
+
source: { path: 'schema/user-session.queries.ts' },
|
|
17
|
+
variables: { TPL_PRISMA_USER: {} },
|
|
18
|
+
});
|
|
19
|
+
const userSessionPayloadObjectType = createTsTemplateFile({
|
|
20
|
+
group: 'schema',
|
|
21
|
+
importMapProviders: { pothosImports: pothosImportsProvider },
|
|
22
|
+
name: 'user-session-payload-object-type',
|
|
23
|
+
projectExports: { userSessionPayload: {} },
|
|
24
|
+
source: { path: 'schema/user-session-payload.object-type.ts' },
|
|
25
|
+
variables: { TPL_PRISMA_USER: {}, TPL_USER_OBJECT_TYPE: {} },
|
|
26
|
+
});
|
|
27
|
+
const schemaGroup = createTsTemplateGroup({
|
|
28
|
+
templates: {
|
|
29
|
+
schemaUserSessionMutations: {
|
|
30
|
+
destination: 'user-session.mutations.ts',
|
|
31
|
+
template: schemaUserSessionMutations,
|
|
32
|
+
},
|
|
33
|
+
schemaUserSessionQueries: {
|
|
34
|
+
destination: 'user-session.queries.ts',
|
|
35
|
+
template: schemaUserSessionQueries,
|
|
36
|
+
},
|
|
37
|
+
userSessionPayloadObjectType: {
|
|
38
|
+
destination: 'user-session-payload.object-type.ts',
|
|
39
|
+
template: userSessionPayloadObjectType,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const servicesUserSessionService = createTsTemplateFile({
|
|
44
|
+
importMapProviders: {
|
|
45
|
+
authContextImports: authContextImportsProvider,
|
|
46
|
+
authRolesImports: authRolesImportsProvider,
|
|
47
|
+
configServiceImports: configServiceImportsProvider,
|
|
48
|
+
errorHandlerServiceImports: errorHandlerServiceImportsProvider,
|
|
49
|
+
requestServiceContextImports: requestServiceContextImportsProvider,
|
|
50
|
+
userSessionTypesImports: userSessionTypesImportsProvider,
|
|
51
|
+
},
|
|
52
|
+
name: 'services-user-session-service',
|
|
53
|
+
projectExports: { userSessionService: {} },
|
|
54
|
+
source: { path: 'services/user-session.service.ts' },
|
|
55
|
+
variables: { TPL_PRISMA_USER_SESSION: {} },
|
|
56
|
+
});
|
|
57
|
+
const userSessionConstants = createTsTemplateFile({
|
|
58
|
+
name: 'user-session-constants',
|
|
59
|
+
projectExports: {
|
|
60
|
+
USER_SESSION_DURATION_SEC: {},
|
|
61
|
+
USER_SESSION_MAX_LIFETIME_SEC: {},
|
|
62
|
+
USER_SESSION_RENEWAL_THRESHOLD_SEC: {},
|
|
63
|
+
},
|
|
64
|
+
source: { path: 'user-session.constants.ts' },
|
|
65
|
+
variables: {},
|
|
66
|
+
});
|
|
67
|
+
const utilsCookieSigner = createTsTemplateFile({
|
|
68
|
+
name: 'utils-cookie-signer',
|
|
69
|
+
projectExports: { sign: {}, signObject: {}, unsign: {}, unsignObject: {} },
|
|
70
|
+
source: { path: 'utils/cookie-signer.ts' },
|
|
71
|
+
variables: {},
|
|
72
|
+
});
|
|
73
|
+
const utilsSessionCookie = createTsTemplateFile({
|
|
74
|
+
importMapProviders: { configServiceImports: configServiceImportsProvider },
|
|
75
|
+
name: 'utils-session-cookie',
|
|
76
|
+
projectExports: { getUserSessionCookieName: {} },
|
|
77
|
+
source: { path: 'utils/session-cookie.ts' },
|
|
78
|
+
variables: {},
|
|
79
|
+
});
|
|
80
|
+
const utilsVerifyRequestOrigin = createTsTemplateFile({
|
|
81
|
+
name: 'utils-verify-request-origin',
|
|
82
|
+
projectExports: { verifyRequestOrigin: {} },
|
|
83
|
+
source: { path: 'utils/verify-request-origin.ts' },
|
|
84
|
+
variables: {},
|
|
85
|
+
});
|
|
86
|
+
export const FASTIFY_AUTH_MODULE_TS_TEMPLATES = {
|
|
87
|
+
schemaGroup,
|
|
88
|
+
servicesUserSessionService,
|
|
89
|
+
userSessionConstants,
|
|
90
|
+
utilsCookieSigner,
|
|
91
|
+
utilsSessionCookie,
|
|
92
|
+
utilsVerifyRequestOrigin,
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=ts-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-templates.js","sourceRoot":"","sources":["../../../../../../src/auth/generators/fastify/auth-module/generated/ts-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,4BAA4B,EAC5B,kCAAkC,EAClC,qBAAqB,EACrB,oCAAoC,EACpC,+BAA+B,GAChC,MAAM,mCAAmC,CAAC;AAE3C,MAAM,0BAA0B,GAAG,oBAAoB,CAAC;IACtD,KAAK,EAAE,QAAQ;IACf,kBAAkB,EAAE,EAAE,aAAa,EAAE,qBAAqB,EAAE;IAC5D,IAAI,EAAE,+BAA+B;IACrC,cAAc,EAAE,EAAE;IAClB,MAAM,EAAE,EAAE,IAAI,EAAE,kCAAkC,EAAE;IACpD,SAAS,EAAE,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;IACpD,KAAK,EAAE,QAAQ;IACf,kBAAkB,EAAE,EAAE,aAAa,EAAE,qBAAqB,EAAE;IAC5D,IAAI,EAAE,6BAA6B;IACnC,cAAc,EAAE,EAAE;IAClB,MAAM,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE;IAClD,SAAS,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE;CACnC,CAAC,CAAC;AAEH,MAAM,4BAA4B,GAAG,oBAAoB,CAAC;IACxD,KAAK,EAAE,QAAQ;IACf,kBAAkB,EAAE,EAAE,aAAa,EAAE,qBAAqB,EAAE;IAC5D,IAAI,EAAE,kCAAkC;IACxC,cAAc,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE;IAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,4CAA4C,EAAE;IAC9D,SAAS,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE;CAC7D,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,qBAAqB,CAAC;IACxC,SAAS,EAAE;QACT,0BAA0B,EAAE;YAC1B,WAAW,EAAE,2BAA2B;YACxC,QAAQ,EAAE,0BAA0B;SACrC;QACD,wBAAwB,EAAE;YACxB,WAAW,EAAE,yBAAyB;YACtC,QAAQ,EAAE,wBAAwB;SACnC;QACD,4BAA4B,EAAE;YAC5B,WAAW,EAAE,qCAAqC;YAClD,QAAQ,EAAE,4BAA4B;SACvC;KACF;CACF,CAAC,CAAC;AAEH,MAAM,0BAA0B,GAAG,oBAAoB,CAAC;IACtD,kBAAkB,EAAE;QAClB,kBAAkB,EAAE,0BAA0B;QAC9C,gBAAgB,EAAE,wBAAwB;QAC1C,oBAAoB,EAAE,4BAA4B;QAClD,0BAA0B,EAAE,kCAAkC;QAC9D,4BAA4B,EAAE,oCAAoC;QAClE,uBAAuB,EAAE,+BAA+B;KACzD;IACD,IAAI,EAAE,+BAA+B;IACrC,cAAc,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE;IAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,kCAAkC,EAAE;IACpD,SAAS,EAAE,EAAE,uBAAuB,EAAE,EAAE,EAAE;CAC3C,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;IAChD,IAAI,EAAE,wBAAwB;IAC9B,cAAc,EAAE;QACd,yBAAyB,EAAE,EAAE;QAC7B,6BAA6B,EAAE,EAAE;QACjC,kCAAkC,EAAE,EAAE;KACvC;IACD,MAAM,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAC7C,SAAS,EAAE,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;IAC7C,IAAI,EAAE,qBAAqB;IAC3B,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;IAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE;IAC1C,SAAS,EAAE,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;IAC9C,kBAAkB,EAAE,EAAE,oBAAoB,EAAE,4BAA4B,EAAE;IAC1E,IAAI,EAAE,sBAAsB;IAC5B,cAAc,EAAE,EAAE,wBAAwB,EAAE,EAAE,EAAE;IAChD,MAAM,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE;IAC3C,SAAS,EAAE,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;IACpD,IAAI,EAAE,6BAA6B;IACnC,cAAc,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE;IAClD,SAAS,EAAE,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,WAAW;IACX,0BAA0B;IAC1B,oBAAoB;IACpB,iBAAiB;IACjB,kBAAkB;IAClB,wBAAwB;CACzB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { config } from '%configServiceImports';
|
|
4
|
+
import { ManagementClient } from 'auth';
|
|
5
|
+
|
|
6
|
+
let cachedClient: ManagementClient | null = null;
|
|
7
|
+
|
|
8
|
+
export function getAuthManagementClient(): ManagementClient {
|
|
9
|
+
if (cachedClient) {
|
|
10
|
+
return cachedClient;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const client = new ManagementClient({
|
|
14
|
+
domain: config.AUTH_TENANT_DOMAIN,
|
|
15
|
+
clientId: config.AUTH_CLIENT_ID,
|
|
16
|
+
clientSecret: config.AUTH_CLIENT_SECRET,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
cachedClient = client;
|
|
20
|
+
|
|
21
|
+
return client;
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { builder } from '%pothosImports';
|
|
4
|
+
|
|
5
|
+
export const userSessionPayload = builder.simpleObject(
|
|
6
|
+
'UserSessionPayload',
|
|
7
|
+
{
|
|
8
|
+
fields: (t) => ({
|
|
9
|
+
expiresAt: t.field({ type: 'DateTime' }),
|
|
10
|
+
userId: t.field({ type: 'Uuid' }),
|
|
11
|
+
}),
|
|
12
|
+
},
|
|
13
|
+
(t) => ({
|
|
14
|
+
user: t.prismaField({
|
|
15
|
+
type: TPL_USER_OBJECT_TYPE,
|
|
16
|
+
resolve: async (query, root) =>
|
|
17
|
+
TPL_PRISMA_USER.findUniqueOrThrow({
|
|
18
|
+
where: { id: root.userId },
|
|
19
|
+
...query,
|
|
20
|
+
}),
|
|
21
|
+
}),
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { builder } from '%pothosImports';
|
|
4
|
+
|
|
5
|
+
import { userSessionService } from '../services/user-session.service.js';
|
|
6
|
+
|
|
7
|
+
builder.mutationField('logOut', (t) =>
|
|
8
|
+
t.fieldWithInputPayload({
|
|
9
|
+
payload: {
|
|
10
|
+
success: t.payload.boolean({
|
|
11
|
+
description: 'Whether the logout was successful.',
|
|
12
|
+
}),
|
|
13
|
+
},
|
|
14
|
+
resolve: async (parent, args, context) => {
|
|
15
|
+
if (context.auth.session && context.auth.session.type === 'user') {
|
|
16
|
+
await userSessionService.clearSession(context.auth.session, context);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { success: true };
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { builder } from '%pothosImports';
|
|
4
|
+
|
|
5
|
+
builder.queryField('currentUser', (t) =>
|
|
6
|
+
t.prismaField({
|
|
7
|
+
type: 'User',
|
|
8
|
+
nullable: true,
|
|
9
|
+
resolve: async (query, root, args, { auth }) => {
|
|
10
|
+
if (!auth.userId) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return TPL_PRISMA_USER.findUniqueOrThrow({
|
|
15
|
+
...query,
|
|
16
|
+
where: { id: auth.userId },
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type { AuthUserSessionInfo } from '%authContextImports';
|
|
4
|
+
import type { AuthRole } from '%authRolesImports';
|
|
5
|
+
import type { RequestServiceContext } from '%requestServiceContextImports';
|
|
6
|
+
import type {
|
|
7
|
+
UserSessionPayload,
|
|
8
|
+
UserSessionService,
|
|
9
|
+
} from '%userSessionTypesImports';
|
|
10
|
+
import type { CookieSerializeOptions } from '@fastify/cookie';
|
|
11
|
+
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
12
|
+
|
|
13
|
+
import { InvalidSessionError } from '%authContextImports';
|
|
14
|
+
import { DEFAULT_USER_ROLES } from '%authRolesImports';
|
|
15
|
+
import { config } from '%configServiceImports';
|
|
16
|
+
import { ForbiddenError } from '%errorHandlerServiceImports';
|
|
17
|
+
import { randomBytes } from 'node:crypto';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
USER_SESSION_DURATION_SEC,
|
|
21
|
+
USER_SESSION_MAX_LIFETIME_SEC,
|
|
22
|
+
USER_SESSION_RENEWAL_THRESHOLD_SEC,
|
|
23
|
+
} from '../constants/user-session.constants.js';
|
|
24
|
+
import { signObject, unsignObject } from '../utils/cookie-signer.js';
|
|
25
|
+
import { getUserSessionCookieName } from '../utils/session-cookie.js';
|
|
26
|
+
import { verifyRequestOrigin } from '../utils/verify-request-origin.js';
|
|
27
|
+
|
|
28
|
+
interface SessionCookieValue {
|
|
29
|
+
// Session token
|
|
30
|
+
token: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const COOKIE_OPTIONS: CookieSerializeOptions = {
|
|
34
|
+
httpOnly: true,
|
|
35
|
+
sameSite: 'lax',
|
|
36
|
+
secure: config.APP_ENVIRONMENT !== 'development',
|
|
37
|
+
maxAge: USER_SESSION_DURATION_SEC,
|
|
38
|
+
path: '/',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates the expiry of a user session and determines if it needs to be renewed.
|
|
43
|
+
*
|
|
44
|
+
* @param currentDate The current date and time.
|
|
45
|
+
* @param userSession The session details.
|
|
46
|
+
* @param userSession.expiresAt The date and time when the session expires.
|
|
47
|
+
* @param userSession.createdAt The date and time when the session was created.
|
|
48
|
+
* @param userSession.renewedAt The date and time when the session was last renewed.
|
|
49
|
+
* @returns The validity of the session and the new expiry date if applicable.
|
|
50
|
+
*/
|
|
51
|
+
function validateSessionExpiry(
|
|
52
|
+
currentDate: Date,
|
|
53
|
+
userSession: {
|
|
54
|
+
expiresAt: Date;
|
|
55
|
+
createdAt: Date;
|
|
56
|
+
renewedAt: Date;
|
|
57
|
+
},
|
|
58
|
+
): { valid: boolean; newExpiry: Date | null } {
|
|
59
|
+
// check if session is expired
|
|
60
|
+
if (userSession.expiresAt < currentDate) {
|
|
61
|
+
return { valid: false, newExpiry: null };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// check if session needs renewal
|
|
65
|
+
const shouldRenewBy =
|
|
66
|
+
userSession.renewedAt.getTime() + USER_SESSION_RENEWAL_THRESHOLD_SEC * 1000;
|
|
67
|
+
|
|
68
|
+
if (shouldRenewBy < currentDate.getTime()) {
|
|
69
|
+
const newExpiry = currentDate.getTime() + USER_SESSION_DURATION_SEC * 1000;
|
|
70
|
+
const maxExpiry =
|
|
71
|
+
USER_SESSION_MAX_LIFETIME_SEC === 0
|
|
72
|
+
? undefined
|
|
73
|
+
: userSession.createdAt.getTime() +
|
|
74
|
+
USER_SESSION_MAX_LIFETIME_SEC * 1000;
|
|
75
|
+
return {
|
|
76
|
+
valid: true,
|
|
77
|
+
newExpiry: new Date(Math.min(newExpiry, maxExpiry ?? newExpiry)),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { valid: true, newExpiry: null };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generates a session token
|
|
86
|
+
*
|
|
87
|
+
* @returns The session token
|
|
88
|
+
*/
|
|
89
|
+
function generateSessionToken(): string {
|
|
90
|
+
return randomBytes(16).toString('base64url');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class CookieUserSessionService implements UserSessionService {
|
|
94
|
+
/**
|
|
95
|
+
* Creates a user session cookie and sets it in the response.
|
|
96
|
+
*
|
|
97
|
+
* @param userId The ID of the user for whom the session is being created.
|
|
98
|
+
* @param context The request service context
|
|
99
|
+
* @param currentDate The current date and time. Defaults to the current date and time.
|
|
100
|
+
* @returns The session payload
|
|
101
|
+
*/
|
|
102
|
+
async createSession(
|
|
103
|
+
userId: string,
|
|
104
|
+
context: RequestServiceContext,
|
|
105
|
+
currentDate: Date = new Date(),
|
|
106
|
+
): Promise<UserSessionPayload> {
|
|
107
|
+
const token = generateSessionToken();
|
|
108
|
+
const expiresAt = new Date(
|
|
109
|
+
currentDate.getTime() + USER_SESSION_DURATION_SEC * 1000,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
await TPL_PRISMA_USER_SESSION.create({
|
|
113
|
+
data: {
|
|
114
|
+
user: { connect: { id: userId } },
|
|
115
|
+
token,
|
|
116
|
+
expiresAt,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const cookieName = getUserSessionCookieName(context.reqInfo.headers);
|
|
121
|
+
const cookieValue = signObject({ token }, config.AUTH_SECRET);
|
|
122
|
+
|
|
123
|
+
context.cookieStore.set(cookieName, cookieValue, COOKIE_OPTIONS);
|
|
124
|
+
|
|
125
|
+
return { userId, expiresAt };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Ends the user session by clearing the session cookie and deleting the session from the database.
|
|
130
|
+
*
|
|
131
|
+
* @param sessionInfo The session info
|
|
132
|
+
* @param context The request service context
|
|
133
|
+
*/
|
|
134
|
+
async clearSession(
|
|
135
|
+
sessionInfo: AuthUserSessionInfo,
|
|
136
|
+
context: RequestServiceContext,
|
|
137
|
+
): Promise<void> {
|
|
138
|
+
await TPL_PRISMA_USER_SESSION.delete({
|
|
139
|
+
where: { id: sessionInfo.id },
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const cookieName = getUserSessionCookieName(context.reqInfo.headers);
|
|
143
|
+
context.cookieStore.clear(cookieName);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Retrieves the user session information from the request.
|
|
148
|
+
*
|
|
149
|
+
* @param req - The Fastify request object containing the cookies.
|
|
150
|
+
* @param reply - The Fastify reply object used to set or clear cookies.
|
|
151
|
+
* @param currentDate - The current date used for session validation. Defaults to the current date and time.
|
|
152
|
+
* @returns A promise that resolves to the authenticated user session information or null if the session is invalid.
|
|
153
|
+
* @throws {InvalidSessionError} If the session is invalid or expired.
|
|
154
|
+
*/
|
|
155
|
+
async getSessionInfoFromRequest(
|
|
156
|
+
req: FastifyRequest,
|
|
157
|
+
reply?: FastifyReply,
|
|
158
|
+
): Promise<AuthUserSessionInfo | undefined> {
|
|
159
|
+
const currentDate = new Date();
|
|
160
|
+
|
|
161
|
+
// Check Origin header for non-GET/HEAD requests to prevent CSRF attacks
|
|
162
|
+
if (
|
|
163
|
+
(req.method !== 'GET' ||
|
|
164
|
+
req.headers.upgrade?.toLowerCase() === 'websocket') &&
|
|
165
|
+
req.method !== 'HEAD' &&
|
|
166
|
+
!verifyRequestOrigin(req, [req.host])
|
|
167
|
+
) {
|
|
168
|
+
throw new ForbiddenError('Invalid Origin header');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const cookieName = getUserSessionCookieName(req.headers);
|
|
172
|
+
try {
|
|
173
|
+
// Check if the session cookie is present
|
|
174
|
+
const sessionCookieValue = req.cookies[cookieName];
|
|
175
|
+
if (!sessionCookieValue) return undefined;
|
|
176
|
+
|
|
177
|
+
// Unsign the session cookie
|
|
178
|
+
const sessionCookieResult = unsignObject(
|
|
179
|
+
sessionCookieValue,
|
|
180
|
+
config.AUTH_SECRET,
|
|
181
|
+
) as SessionCookieValue | undefined;
|
|
182
|
+
if (!sessionCookieResult) throw new InvalidSessionError();
|
|
183
|
+
|
|
184
|
+
const { token } = sessionCookieResult;
|
|
185
|
+
if (typeof token !== 'string') throw new InvalidSessionError();
|
|
186
|
+
|
|
187
|
+
// Fetch and validate user session
|
|
188
|
+
const userSession = await TPL_PRISMA_USER_SESSION.findUnique({
|
|
189
|
+
where: { token },
|
|
190
|
+
include: { user: { select: { id: true, roles: true } } },
|
|
191
|
+
});
|
|
192
|
+
if (!userSession) throw new InvalidSessionError();
|
|
193
|
+
|
|
194
|
+
const sessionExpiryResult = validateSessionExpiry(
|
|
195
|
+
currentDate,
|
|
196
|
+
userSession,
|
|
197
|
+
);
|
|
198
|
+
if (!sessionExpiryResult.valid)
|
|
199
|
+
throw new InvalidSessionError('Session expired');
|
|
200
|
+
|
|
201
|
+
// renew the session if needed
|
|
202
|
+
if (sessionExpiryResult.newExpiry && reply) {
|
|
203
|
+
await TPL_PRISMA_USER_SESSION.update({
|
|
204
|
+
where: { id: userSession.id },
|
|
205
|
+
data: {
|
|
206
|
+
renewedAt: currentDate,
|
|
207
|
+
expiresAt: sessionExpiryResult.newExpiry,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
const newSignedCookie = signObject({ token }, config.AUTH_SECRET);
|
|
211
|
+
reply.setCookie(cookieName, newSignedCookie, COOKIE_OPTIONS);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const { user } = userSession;
|
|
215
|
+
const expiresAt = sessionExpiryResult.newExpiry ?? userSession.expiresAt;
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
id: userSession.id,
|
|
219
|
+
type: 'user',
|
|
220
|
+
roles: [
|
|
221
|
+
...DEFAULT_USER_ROLES,
|
|
222
|
+
...user.roles.map((role) => role.role as AuthRole),
|
|
223
|
+
],
|
|
224
|
+
userId: user.id,
|
|
225
|
+
expiresAt,
|
|
226
|
+
};
|
|
227
|
+
} catch (err) {
|
|
228
|
+
// clear the cookie if it's invalid
|
|
229
|
+
if (err instanceof InvalidSessionError && reply) {
|
|
230
|
+
reply.clearCookie(cookieName);
|
|
231
|
+
}
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Retrieves the user session information from the authentication token
|
|
238
|
+
*
|
|
239
|
+
* Note: Since we use cookies, we ignore the authToken parameter
|
|
240
|
+
*
|
|
241
|
+
* @param req The request object
|
|
242
|
+
* @returns The session info or undefined if no session is found
|
|
243
|
+
*/
|
|
244
|
+
async getSessionInfoFromToken(
|
|
245
|
+
req: FastifyRequest,
|
|
246
|
+
): Promise<AuthUserSessionInfo | undefined> {
|
|
247
|
+
return this.getSessionInfoFromRequest(req, undefined);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const userSessionService = new CookieUserSessionService();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defines the duration of a user session before it expires, in seconds.
|
|
5
|
+
*/
|
|
6
|
+
export const USER_SESSION_DURATION_SEC = 14 * 24 * 60 * 60; // 14 days
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Defines the duration after which a user session should be renewed, in seconds.
|
|
10
|
+
*/
|
|
11
|
+
export const USER_SESSION_RENEWAL_THRESHOLD_SEC = 1 * 24 * 60 * 60; // 1 day
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Sets the maximum possible lifespan of a user session, in seconds, including renewals.
|
|
15
|
+
*
|
|
16
|
+
* A value of 0 allows the session to persist indefinitely, provided it is renewed
|
|
17
|
+
* before the session expiration.
|
|
18
|
+
*/
|
|
19
|
+
export const USER_SESSION_MAX_LIFETIME_SEC = 0 as number;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type { AuthUserSessionInfo } from '%authContextImports';
|
|
4
|
+
import type { AuthRole } from '%authRolesImports';
|
|
5
|
+
import type { UserSessionService } from '%userSessionTypesImports';
|
|
6
|
+
import type { FastifyRequest } from 'fastify';
|
|
7
|
+
|
|
8
|
+
import { DEFAULT_USER_ROLES } from '%authRolesImports';
|
|
9
|
+
|
|
10
|
+
const USER_ID_CLAIM = 'https://app.com/user_id';
|
|
11
|
+
const EMAIL_CLAIM = 'https://app.com/email';
|
|
12
|
+
const EMAIL_VERIFIED_CLAIM = 'https://app.com/email_verified';
|
|
13
|
+
const ROLES_CLAIM = 'https://app.com/roles';
|
|
14
|
+
|
|
15
|
+
interface AuthJwt {
|
|
16
|
+
[USER_ID_CLAIM]?: string;
|
|
17
|
+
[EMAIL_CLAIM]?: string;
|
|
18
|
+
[EMAIL_VERIFIED_CLAIM]?: boolean;
|
|
19
|
+
[ROLES_CLAIM]?: string[];
|
|
20
|
+
sub: string;
|
|
21
|
+
email: string;
|
|
22
|
+
exp: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class AuthUserSessionService implements UserSessionService {
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves the user session information from the request.
|
|
28
|
+
*
|
|
29
|
+
* @param req - The Fastify request object containing the cookies.
|
|
30
|
+
* @param reply - The Fastify reply object used to set or clear cookies.
|
|
31
|
+
* @param currentDate - The current date used for session validation. Defaults to the current date and time.
|
|
32
|
+
* @returns A promise that resolves to the authenticated user session information or null if the session is invalid.
|
|
33
|
+
* @throws {InvalidSessionError} If the session is invalid or expired.
|
|
34
|
+
*/
|
|
35
|
+
async getSessionInfoFromRequest(
|
|
36
|
+
req: FastifyRequest,
|
|
37
|
+
): Promise<AuthUserSessionInfo | undefined> {
|
|
38
|
+
if (!req.headers.authorization) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const verifiedJwt = await req.jwtVerify<AuthJwt>();
|
|
43
|
+
const userId = verifiedJwt[USER_ID_CLAIM];
|
|
44
|
+
const roles = verifiedJwt[ROLES_CLAIM] ?? [];
|
|
45
|
+
const email = verifiedJwt[EMAIL_CLAIM];
|
|
46
|
+
|
|
47
|
+
if (!userId) {
|
|
48
|
+
throw new Error(`Missing user id in JWT`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const user = await TPL_USER_MODEL.findUnique({ where: { id: userId } });
|
|
52
|
+
|
|
53
|
+
// create user if one does not exist already
|
|
54
|
+
if (!email) {
|
|
55
|
+
throw new Error(`Missing email claim in JWT`);
|
|
56
|
+
}
|
|
57
|
+
if (!user) {
|
|
58
|
+
// Use createMany to avoid race-conditions with creating the user
|
|
59
|
+
await TPL_USER_MODEL.createMany({
|
|
60
|
+
data: [
|
|
61
|
+
{
|
|
62
|
+
id: userId,
|
|
63
|
+
authId: verifiedJwt.sub,
|
|
64
|
+
email,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
skipDuplicates: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
id: verifiedJwt.sub,
|
|
73
|
+
type: 'user',
|
|
74
|
+
userId,
|
|
75
|
+
roles: [...DEFAULT_USER_ROLES, ...roles] as AuthRole[],
|
|
76
|
+
expiresAt:
|
|
77
|
+
typeof verifiedJwt.exp === 'number'
|
|
78
|
+
? new Date(verifiedJwt.exp * 1000)
|
|
79
|
+
: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves the user session information from the authentication token
|
|
85
|
+
*
|
|
86
|
+
* @param req The request object
|
|
87
|
+
* @returns The session info or undefined if no session is found
|
|
88
|
+
*/
|
|
89
|
+
async getSessionInfoFromToken(
|
|
90
|
+
req: FastifyRequest,
|
|
91
|
+
token?: string | null,
|
|
92
|
+
): Promise<AuthUserSessionInfo | undefined> {
|
|
93
|
+
// We have to manually add the header to the request since we can't
|
|
94
|
+
// use server.jwt.verify due to an error
|
|
95
|
+
req.headers.authorization = token ?? undefined;
|
|
96
|
+
|
|
97
|
+
return this.getSessionInfoFromRequest(req);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const userSessionService = new AuthUserSessionService();
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import * as crypto from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sign the given `val` with `secret` using HMAC-SHA256.
|
|
7
|
+
*
|
|
8
|
+
* @param val - The value to sign.
|
|
9
|
+
* @param secret - The secret key to use for signing.
|
|
10
|
+
* @returns The signed value.
|
|
11
|
+
*/
|
|
12
|
+
export function sign(val: string, secret: string): string {
|
|
13
|
+
return `${val}.${crypto
|
|
14
|
+
.createHmac('sha256', secret)
|
|
15
|
+
.update(val)
|
|
16
|
+
.digest('base64')
|
|
17
|
+
// remove all equal signs since they are not valid in cookie values
|
|
18
|
+
.replaceAll('=', '')}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Unsign and decode the given `input` with `secret`,
|
|
23
|
+
*
|
|
24
|
+
* @param input - The signed cookie string to unsign.
|
|
25
|
+
* @param secret - The secret key used for signing.
|
|
26
|
+
* @returns The unsigned value or undefined if the signature is invalid.
|
|
27
|
+
*/
|
|
28
|
+
export function unsign(input: string, secret: string): string | undefined {
|
|
29
|
+
const tentativeValue = input.slice(0, input.lastIndexOf('.'));
|
|
30
|
+
const expectedInput = sign(tentativeValue, secret);
|
|
31
|
+
const expectedBuffer = Buffer.from(expectedInput);
|
|
32
|
+
const inputBuffer = Buffer.from(input);
|
|
33
|
+
if (
|
|
34
|
+
expectedBuffer.length !== inputBuffer.length ||
|
|
35
|
+
!crypto.timingSafeEqual(expectedBuffer, inputBuffer)
|
|
36
|
+
) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return tentativeValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sign an object by JSON stringifying and base64 encoding it, then signing with secret.
|
|
44
|
+
*
|
|
45
|
+
* @param obj - The object to sign.
|
|
46
|
+
* @param secret - The secret key to use for signing.
|
|
47
|
+
* @returns The signed encoded object.
|
|
48
|
+
*/
|
|
49
|
+
export function signObject(obj: unknown, secret: string): string {
|
|
50
|
+
const encoded = Buffer.from(JSON.stringify(obj)).toString('base64url');
|
|
51
|
+
return sign(encoded, secret);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Unsign and decode an object that was signed with signObject.
|
|
56
|
+
*
|
|
57
|
+
* @param input - The signed encoded object string to unsign.
|
|
58
|
+
* @param secret - The secret key used for signing.
|
|
59
|
+
* @returns The decoded object or undefined if the signature is invalid.
|
|
60
|
+
*/
|
|
61
|
+
export function unsignObject(input: string, secret: string): unknown {
|
|
62
|
+
const unsigned = unsign(input, secret);
|
|
63
|
+
if (!unsigned) return undefined;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const decoded = Buffer.from(unsigned, 'base64url').toString('utf8');
|
|
67
|
+
return JSON.parse(decoded);
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type { FastifyRequest } from 'fastify';
|
|
4
|
+
|
|
5
|
+
import { config } from '%configServiceImports';
|
|
6
|
+
|
|
7
|
+
const COOKIE_NAME = 'user-session';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Retrieves the name of the session cookie based on the provided request.
|
|
11
|
+
* If the application environment is not 'development', the cookie name will be prefixed with '__Host-'.
|
|
12
|
+
* In development, the cookie name will be suffixed with the port number.
|
|
13
|
+
*
|
|
14
|
+
* @param req - The FastifyRequest object representing the incoming request.
|
|
15
|
+
* @returns The name of the Iron session cookie.
|
|
16
|
+
*/
|
|
17
|
+
export function getUserSessionCookieName(
|
|
18
|
+
headers: FastifyRequest['headers'],
|
|
19
|
+
): string {
|
|
20
|
+
if (config.APP_ENVIRONMENT !== 'development') {
|
|
21
|
+
return `__Host-${COOKIE_NAME}`;
|
|
22
|
+
}
|
|
23
|
+
// in development, localhost does not support the __Host prefix and should be scoped to port
|
|
24
|
+
// use origin/referer header to determine hostname because dev reverse proxies use origin/referer to signal the original host
|
|
25
|
+
const { origin, referer } = headers;
|
|
26
|
+
const originalHost = origin ?? referer;
|
|
27
|
+
let port;
|
|
28
|
+
try {
|
|
29
|
+
if (!originalHost) {
|
|
30
|
+
port = config.SERVER_PORT;
|
|
31
|
+
} else {
|
|
32
|
+
const url = new URL(originalHost);
|
|
33
|
+
const parsedPort = Number.parseInt(url.port, 10);
|
|
34
|
+
// Validate port is in valid range (1-65535)
|
|
35
|
+
port =
|
|
36
|
+
parsedPort > 0 && parsedPort < 65_536 ? parsedPort : config.SERVER_PORT;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
port = config.SERVER_PORT;
|
|
40
|
+
}
|
|
41
|
+
return `${COOKIE_NAME}-${port}`;
|
|
42
|
+
}
|