@shipfox/api-auth 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +116 -0
- package/LICENSE +21 -0
- package/README.md +336 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +95 -0
- package/dist/config.js.map +1 -0
- package/dist/core/auth.d.ts +108 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +476 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/entities/email-verification.d.ts +9 -0
- package/dist/core/entities/email-verification.d.ts.map +1 -0
- package/dist/core/entities/email-verification.js +3 -0
- package/dist/core/entities/email-verification.js.map +1 -0
- package/dist/core/entities/password-reset.d.ts +9 -0
- package/dist/core/entities/password-reset.d.ts.map +1 -0
- package/dist/core/entities/password-reset.js +3 -0
- package/dist/core/entities/password-reset.js.map +1 -0
- package/dist/core/entities/refresh-token.d.ts +12 -0
- package/dist/core/entities/refresh-token.d.ts.map +1 -0
- package/dist/core/entities/refresh-token.js +3 -0
- package/dist/core/entities/refresh-token.js.map +1 -0
- package/dist/core/entities/user.d.ts +12 -0
- package/dist/core/entities/user.d.ts.map +1 -0
- package/dist/core/entities/user.js +3 -0
- package/dist/core/entities/user.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +106 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/job-lease-token.d.ts +12 -0
- package/dist/core/job-lease-token.d.ts.map +1 -0
- package/dist/core/job-lease-token.js +58 -0
- package/dist/core/job-lease-token.js.map +1 -0
- package/dist/core/jwt.d.ts +37 -0
- package/dist/core/jwt.d.ts.map +1 -0
- package/dist/core/jwt.js +46 -0
- package/dist/core/jwt.js.map +1 -0
- package/dist/core/password.d.ts +10 -0
- package/dist/core/password.d.ts.map +1 -0
- package/dist/core/password.js +13 -0
- package/dist/core/password.js.map +1 -0
- package/dist/core/rate-limit.d.ts +34 -0
- package/dist/core/rate-limit.d.ts.map +1 -0
- package/dist/core/rate-limit.js +76 -0
- package/dist/core/rate-limit.js.map +1 -0
- package/dist/core/runner-session-token.d.ts +6 -0
- package/dist/core/runner-session-token.d.ts.map +1 -0
- package/dist/core/runner-session-token.js +37 -0
- package/dist/core/runner-session-token.js.map +1 -0
- package/dist/db/db.d.ts +1723 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +28 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/email-verifications.d.ts +39 -0
- package/dist/db/email-verifications.d.ts.map +1 -0
- package/dist/db/email-verifications.js +95 -0
- package/dist/db/email-verifications.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +5 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/password-resets.d.ts +23 -0
- package/dist/db/password-resets.d.ts.map +1 -0
- package/dist/db/password-resets.js +37 -0
- package/dist/db/password-resets.js.map +1 -0
- package/dist/db/rate-limits.d.ts +18 -0
- package/dist/db/rate-limits.d.ts.map +1 -0
- package/dist/db/rate-limits.js +44 -0
- package/dist/db/rate-limits.js.map +1 -0
- package/dist/db/refresh-tokens.d.ts +44 -0
- package/dist/db/refresh-tokens.d.ts.map +1 -0
- package/dist/db/refresh-tokens.js +71 -0
- package/dist/db/refresh-tokens.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/email-verifications.d.ts +114 -0
- package/dist/db/schema/email-verifications.d.ts.map +1 -0
- package/dist/db/schema/email-verifications.js +35 -0
- package/dist/db/schema/email-verifications.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/password-resets.d.ts +114 -0
- package/dist/db/schema/password-resets.d.ts.map +1 -0
- package/dist/db/schema/password-resets.js +35 -0
- package/dist/db/schema/password-resets.js.map +1 -0
- package/dist/db/schema/rate-limits.d.ts +145 -0
- package/dist/db/schema/rate-limits.d.ts.map +1 -0
- package/dist/db/schema/rate-limits.js +25 -0
- package/dist/db/schema/rate-limits.js.map +1 -0
- package/dist/db/schema/refresh-tokens.d.ts +165 -0
- package/dist/db/schema/refresh-tokens.d.ts.map +1 -0
- package/dist/db/schema/refresh-tokens.js +47 -0
- package/dist/db/schema/refresh-tokens.js.map +1 -0
- package/dist/db/schema/users.d.ts +149 -0
- package/dist/db/schema/users.d.ts.map +1 -0
- package/dist/db/schema/users.js +40 -0
- package/dist/db/schema/users.js.map +1 -0
- package/dist/db/users.d.ts +28 -0
- package/dist/db/users.d.ts.map +1 -0
- package/dist/db/users.js +83 -0
- package/dist/db/users.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +16 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +52 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/presentation/auth/bearer-token-auth.d.ts +15 -0
- package/dist/presentation/auth/bearer-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/bearer-token-auth.js +31 -0
- package/dist/presentation/auth/bearer-token-auth.js.map +1 -0
- package/dist/presentation/auth/jwt-auth.d.ts +10 -0
- package/dist/presentation/auth/jwt-auth.d.ts.map +1 -0
- package/dist/presentation/auth/jwt-auth.js +31 -0
- package/dist/presentation/auth/jwt-auth.js.map +1 -0
- package/dist/presentation/auth/lease-token-auth.d.ts +15 -0
- package/dist/presentation/auth/lease-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/lease-token-auth.js +27 -0
- package/dist/presentation/auth/lease-token-auth.js.map +1 -0
- package/dist/presentation/auth/refresh-cookie.d.ts +7 -0
- package/dist/presentation/auth/refresh-cookie.d.ts.map +1 -0
- package/dist/presentation/auth/refresh-cookie.js +29 -0
- package/dist/presentation/auth/refresh-cookie.js.map +1 -0
- package/dist/presentation/auth/runner-session-auth.d.ts +3 -0
- package/dist/presentation/auth/runner-session-auth.d.ts.map +1 -0
- package/dist/presentation/auth/runner-session-auth.js +16 -0
- package/dist/presentation/auth/runner-session-auth.js.map +1 -0
- package/dist/presentation/dto/user.d.ts +8 -0
- package/dist/presentation/dto/user.d.ts.map +1 -0
- package/dist/presentation/dto/user.js +19 -0
- package/dist/presentation/dto/user.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-session.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-session.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-session.js +49 -0
- package/dist/presentation/e2eRoutes/create-session.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-user.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-user.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-user.js +42 -0
- package/dist/presentation/e2eRoutes/create-user.js.map +1 -0
- package/dist/presentation/e2eRoutes/index.d.ts +3 -0
- package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/index.js +15 -0
- package/dist/presentation/e2eRoutes/index.js.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts +2 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.js +35 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.js.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.d.ts +2 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.d.ts.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.js +27 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +3 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +31 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/password/change-password.d.ts +2 -0
- package/dist/presentation/routes/password/change-password.d.ts.map +1 -0
- package/dist/presentation/routes/password/change-password.js +50 -0
- package/dist/presentation/routes/password/change-password.js.map +1 -0
- package/dist/presentation/routes/password/password-reset-confirm.d.ts +2 -0
- package/dist/presentation/routes/password/password-reset-confirm.d.ts.map +1 -0
- package/dist/presentation/routes/password/password-reset-confirm.js +36 -0
- package/dist/presentation/routes/password/password-reset-confirm.js.map +1 -0
- package/dist/presentation/routes/password/password-reset-request.d.ts +2 -0
- package/dist/presentation/routes/password/password-reset-request.d.ts.map +1 -0
- package/dist/presentation/routes/password/password-reset-request.js +26 -0
- package/dist/presentation/routes/password/password-reset-request.js.map +1 -0
- package/dist/presentation/routes/rate-limit.d.ts +10 -0
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
- package/dist/presentation/routes/rate-limit.js +101 -0
- package/dist/presentation/routes/rate-limit.js.map +1 -0
- package/dist/presentation/routes/registration/signup.d.ts +2 -0
- package/dist/presentation/routes/registration/signup.d.ts.map +1 -0
- package/dist/presentation/routes/registration/signup.js +82 -0
- package/dist/presentation/routes/registration/signup.js.map +1 -0
- package/dist/presentation/routes/session/login.d.ts +2 -0
- package/dist/presentation/routes/session/login.d.ts.map +1 -0
- package/dist/presentation/routes/session/login.js +48 -0
- package/dist/presentation/routes/session/login.js.map +1 -0
- package/dist/presentation/routes/session/logout.d.ts +2 -0
- package/dist/presentation/routes/session/logout.d.ts.map +1 -0
- package/dist/presentation/routes/session/logout.js +25 -0
- package/dist/presentation/routes/session/logout.js.map +1 -0
- package/dist/presentation/routes/session/me.d.ts +2 -0
- package/dist/presentation/routes/session/me.d.ts.map +1 -0
- package/dist/presentation/routes/session/me.js +42 -0
- package/dist/presentation/routes/session/me.js.map +1 -0
- package/dist/presentation/routes/session/refresh.d.ts +2 -0
- package/dist/presentation/routes/session/refresh.d.ts.map +1 -0
- package/dist/presentation/routes/session/refresh.js +49 -0
- package/dist/presentation/routes/session/refresh.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +3 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -0
- package/dist/presentation/subscribers/index.js +4 -0
- package/dist/presentation/subscribers/index.js.map +1 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts +3 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.js +13 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.js.map +1 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts +3 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.js +14 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +81 -0
- package/drizzle/meta/0000_snapshot.json +643 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +88 -0
- package/src/config.ts +88 -0
- package/src/core/auth.test.ts +675 -0
- package/src/core/auth.ts +560 -0
- package/src/core/entities/email-verification.ts +8 -0
- package/src/core/entities/password-reset.ts +8 -0
- package/src/core/entities/refresh-token.ts +11 -0
- package/src/core/entities/user.ts +12 -0
- package/src/core/errors.ts +123 -0
- package/src/core/job-lease-token.test.ts +201 -0
- package/src/core/job-lease-token.ts +83 -0
- package/src/core/jwt.test.ts +180 -0
- package/src/core/jwt.ts +66 -0
- package/src/core/password.test.ts +28 -0
- package/src/core/password.ts +21 -0
- package/src/core/rate-limit.test.ts +462 -0
- package/src/core/rate-limit.ts +130 -0
- package/src/core/runner-session-token.test.ts +126 -0
- package/src/core/runner-session-token.ts +49 -0
- package/src/db/db.ts +28 -0
- package/src/db/email-verifications.test.ts +61 -0
- package/src/db/email-verifications.ts +160 -0
- package/src/db/migrations.ts +4 -0
- package/src/db/password-resets.test.ts +61 -0
- package/src/db/password-resets.ts +69 -0
- package/src/db/rate-limits.ts +69 -0
- package/src/db/refresh-tokens.test.ts +164 -0
- package/src/db/refresh-tokens.ts +149 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/email-verifications.ts +37 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/password-resets.ts +37 -0
- package/src/db/schema/rate-limits.ts +27 -0
- package/src/db/schema/refresh-tokens.ts +43 -0
- package/src/db/schema/users.ts +37 -0
- package/src/db/users.test.ts +75 -0
- package/src/db/users.ts +121 -0
- package/src/index.test.ts +39 -0
- package/src/index.ts +69 -0
- package/src/metrics/index.ts +13 -0
- package/src/metrics/instance.test.ts +69 -0
- package/src/metrics/instance.ts +77 -0
- package/src/presentation/auth/bearer-token-auth.test.ts +99 -0
- package/src/presentation/auth/bearer-token-auth.ts +51 -0
- package/src/presentation/auth/jwt-auth.test.ts +136 -0
- package/src/presentation/auth/jwt-auth.ts +39 -0
- package/src/presentation/auth/lease-token-auth.test.ts +56 -0
- package/src/presentation/auth/lease-token-auth.ts +25 -0
- package/src/presentation/auth/refresh-cookie.test.ts +39 -0
- package/src/presentation/auth/refresh-cookie.ts +34 -0
- package/src/presentation/auth/runner-session-auth.test.ts +54 -0
- package/src/presentation/auth/runner-session-auth.ts +13 -0
- package/src/presentation/dto/user.ts +21 -0
- package/src/presentation/e2eRoutes/create-session.ts +39 -0
- package/src/presentation/e2eRoutes/create-user.ts +38 -0
- package/src/presentation/e2eRoutes/index.test.ts +102 -0
- package/src/presentation/e2eRoutes/index.ts +10 -0
- package/src/presentation/routes/email-verification/verify-email-confirm.test.ts +66 -0
- package/src/presentation/routes/email-verification/verify-email-confirm.ts +37 -0
- package/src/presentation/routes/email-verification/verify-email-resend.test.ts +100 -0
- package/src/presentation/routes/email-verification/verify-email-resend.ts +26 -0
- package/src/presentation/routes/index.ts +29 -0
- package/src/presentation/routes/password/change-password.test.ts +106 -0
- package/src/presentation/routes/password/change-password.ts +45 -0
- package/src/presentation/routes/password/password-reset-confirm.test.ts +63 -0
- package/src/presentation/routes/password/password-reset-confirm.ts +35 -0
- package/src/presentation/routes/password/password-reset-request.test.ts +56 -0
- package/src/presentation/routes/password/password-reset-request.ts +25 -0
- package/src/presentation/routes/rate-limit.test.ts +341 -0
- package/src/presentation/routes/rate-limit.ts +119 -0
- package/src/presentation/routes/registration/signup.test.ts +284 -0
- package/src/presentation/routes/registration/signup.ts +81 -0
- package/src/presentation/routes/session/login.test.ts +77 -0
- package/src/presentation/routes/session/login.ts +50 -0
- package/src/presentation/routes/session/logout.test.ts +55 -0
- package/src/presentation/routes/session/logout.ts +23 -0
- package/src/presentation/routes/session/me.test.ts +56 -0
- package/src/presentation/routes/session/me.ts +35 -0
- package/src/presentation/routes/session/refresh.test.ts +89 -0
- package/src/presentation/routes/session/refresh.ts +54 -0
- package/src/presentation/subscribers/email-send-requested.test.ts +77 -0
- package/src/presentation/subscribers/index.ts +2 -0
- package/src/presentation/subscribers/on-email-verification-send-requested.ts +10 -0
- package/src/presentation/subscribers/on-password-reset-send-requested.ts +13 -0
- package/test/env.ts +10 -0
- package/test/factories/index.ts +1 -0
- package/test/factories/user.ts +36 -0
- package/test/globalSetup.ts +18 -0
- package/test/index.ts +1 -0
- package/test/routes.ts +275 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +4 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import cookie from '@fastify/cookie';
|
|
2
|
+
import { createE2eSessionRoute } from './create-session.js';
|
|
3
|
+
import { createE2eUserRoute } from './create-user.js';
|
|
4
|
+
export const authE2eRoutes = {
|
|
5
|
+
prefix: '/auth',
|
|
6
|
+
plugins: [
|
|
7
|
+
cookie
|
|
8
|
+
],
|
|
9
|
+
routes: [
|
|
10
|
+
createE2eUserRoute,
|
|
11
|
+
createE2eSessionRoute
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/e2eRoutes/index.ts"],"sourcesContent":["import cookie from '@fastify/cookie';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createE2eSessionRoute} from './create-session.js';\nimport {createE2eUserRoute} from './create-user.js';\n\nexport const authE2eRoutes: RouteGroup = {\n prefix: '/auth',\n plugins: [cookie],\n routes: [createE2eUserRoute, createE2eSessionRoute],\n};\n"],"names":["cookie","createE2eSessionRoute","createE2eUserRoute","authE2eRoutes","prefix","plugins","routes"],"mappings":"AAAA,OAAOA,YAAY,kBAAkB;AAErC,SAAQC,qBAAqB,QAAO,sBAAsB;AAC1D,SAAQC,kBAAkB,QAAO,mBAAmB;AAEpD,OAAO,MAAMC,gBAA4B;IACvCC,QAAQ;IACRC,SAAS;QAACL;KAAO;IACjBM,QAAQ;QAACJ;QAAoBD;KAAsB;AACrD,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-email-confirm.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/email-verification/verify-email-confirm.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,uBAAuB,iDA0BlC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { verifyEmailConfirmBodySchema, verifyEmailConfirmResponseSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { confirmEmailVerification } from '#core/auth.js';
|
|
4
|
+
import { TokenInvalidError } from '#core/errors.js';
|
|
5
|
+
import { setRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
6
|
+
import { toAuthSessionDto } from '#presentation/dto/user.js';
|
|
7
|
+
export const verifyEmailConfirmRoute = defineRoute({
|
|
8
|
+
method: 'POST',
|
|
9
|
+
path: '/verify-email/confirm',
|
|
10
|
+
description: 'Verify a user email address with the link sent by email.',
|
|
11
|
+
schema: {
|
|
12
|
+
body: verifyEmailConfirmBodySchema,
|
|
13
|
+
response: {
|
|
14
|
+
200: verifyEmailConfirmResponseSchema
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
errorHandler: (error)=>{
|
|
18
|
+
if (error instanceof TokenInvalidError) {
|
|
19
|
+
throw new ClientError('Verification token is invalid or expired', 'token-invalid', {
|
|
20
|
+
status: 410
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
},
|
|
25
|
+
handler: async (request, reply)=>{
|
|
26
|
+
const { token } = request.body;
|
|
27
|
+
const result = await confirmEmailVerification({
|
|
28
|
+
token
|
|
29
|
+
});
|
|
30
|
+
setRefreshTokenCookie(reply, result.refreshToken);
|
|
31
|
+
return toAuthSessionDto(result);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=verify-email-confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/email-verification/verify-email-confirm.ts"],"sourcesContent":["import {\n verifyEmailConfirmBodySchema,\n verifyEmailConfirmResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {confirmEmailVerification} from '#core/auth.js';\nimport {TokenInvalidError} from '#core/errors.js';\nimport {setRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\nimport {toAuthSessionDto} from '#presentation/dto/user.js';\n\nexport const verifyEmailConfirmRoute = defineRoute({\n method: 'POST',\n path: '/verify-email/confirm',\n description: 'Verify a user email address with the link sent by email.',\n schema: {\n body: verifyEmailConfirmBodySchema,\n response: {\n 200: verifyEmailConfirmResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof TokenInvalidError) {\n throw new ClientError('Verification token is invalid or expired', 'token-invalid', {\n status: 410,\n });\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {token} = request.body;\n\n const result = await confirmEmailVerification({token});\n setRefreshTokenCookie(reply, result.refreshToken);\n\n return toAuthSessionDto(result);\n },\n});\n"],"names":["verifyEmailConfirmBodySchema","verifyEmailConfirmResponseSchema","ClientError","defineRoute","confirmEmailVerification","TokenInvalidError","setRefreshTokenCookie","toAuthSessionDto","verifyEmailConfirmRoute","method","path","description","schema","body","response","errorHandler","error","status","handler","request","reply","token","result","refreshToken"],"mappings":"AAAA,SACEA,4BAA4B,EAC5BC,gCAAgC,QAC3B,wBAAwB;AAC/B,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,wBAAwB,QAAO,gBAAgB;AACvD,SAAQC,iBAAiB,QAAO,kBAAkB;AAClD,SAAQC,qBAAqB,QAAO,uCAAuC;AAC3E,SAAQC,gBAAgB,QAAO,4BAA4B;AAE3D,OAAO,MAAMC,0BAA0BL,YAAY;IACjDM,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMb;QACNc,UAAU;YACR,KAAKb;QACP;IACF;IACAc,cAAc,CAACC;QACb,IAAIA,iBAAiBX,mBAAmB;YACtC,MAAM,IAAIH,YAAY,4CAA4C,iBAAiB;gBACjFe,QAAQ;YACV;QACF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAM,EAACC,KAAK,EAAC,GAAGF,QAAQN,IAAI;QAE5B,MAAMS,SAAS,MAAMlB,yBAAyB;YAACiB;QAAK;QACpDf,sBAAsBc,OAAOE,OAAOC,YAAY;QAEhD,OAAOhB,iBAAiBe;IAC1B;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-email-resend.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/email-verification/verify-email-resend.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,sBAAsB,iDAoBjC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { verifyEmailResendBodySchema, verifyEmailResendResponseSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { resendEmailVerification } from '#core/auth.js';
|
|
4
|
+
import { createAuthRateLimitPreHandler } from '#presentation/routes/rate-limit.js';
|
|
5
|
+
export const verifyEmailResendRoute = defineRoute({
|
|
6
|
+
method: 'POST',
|
|
7
|
+
path: '/verify-email/resend',
|
|
8
|
+
description: 'Send a new email verification link if the account can be verified.',
|
|
9
|
+
schema: {
|
|
10
|
+
body: verifyEmailResendBodySchema,
|
|
11
|
+
response: {
|
|
12
|
+
200: verifyEmailResendResponseSchema
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
preHandler: createAuthRateLimitPreHandler('email-send'),
|
|
16
|
+
handler: async (request, reply)=>{
|
|
17
|
+
const { email } = request.body;
|
|
18
|
+
const result = await resendEmailVerification({
|
|
19
|
+
email
|
|
20
|
+
});
|
|
21
|
+
reply.code(200).send({
|
|
22
|
+
next_resend_available_at: result.nextResendAvailableAt.toISOString()
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=verify-email-resend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/email-verification/verify-email-resend.ts"],"sourcesContent":["import {verifyEmailResendBodySchema, verifyEmailResendResponseSchema} from '@shipfox/api-auth-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {resendEmailVerification} from '#core/auth.js';\nimport {createAuthRateLimitPreHandler} from '#presentation/routes/rate-limit.js';\n\nexport const verifyEmailResendRoute = defineRoute({\n method: 'POST',\n path: '/verify-email/resend',\n description: 'Send a new email verification link if the account can be verified.',\n schema: {\n body: verifyEmailResendBodySchema,\n response: {\n 200: verifyEmailResendResponseSchema,\n },\n },\n preHandler: createAuthRateLimitPreHandler('email-send'),\n handler: async (request, reply) => {\n const {email} = request.body;\n\n const result = await resendEmailVerification({email});\n\n reply.code(200).send({\n next_resend_available_at: result.nextResendAvailableAt.toISOString(),\n });\n },\n});\n"],"names":["verifyEmailResendBodySchema","verifyEmailResendResponseSchema","defineRoute","resendEmailVerification","createAuthRateLimitPreHandler","verifyEmailResendRoute","method","path","description","schema","body","response","preHandler","handler","request","reply","email","result","code","send","next_resend_available_at","nextResendAvailableAt","toISOString"],"mappings":"AAAA,SAAQA,2BAA2B,EAAEC,+BAA+B,QAAO,wBAAwB;AACnG,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,uBAAuB,QAAO,gBAAgB;AACtD,SAAQC,6BAA6B,QAAO,qCAAqC;AAEjF,OAAO,MAAMC,yBAAyBH,YAAY;IAChDI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMV;QACNW,UAAU;YACR,KAAKV;QACP;IACF;IACAW,YAAYR,8BAA8B;IAC1CS,SAAS,OAAOC,SAASC;QACvB,MAAM,EAACC,KAAK,EAAC,GAAGF,QAAQJ,IAAI;QAE5B,MAAMO,SAAS,MAAMd,wBAAwB;YAACa;QAAK;QAEnDD,MAAMG,IAAI,CAAC,KAAKC,IAAI,CAAC;YACnBC,0BAA0BH,OAAOI,qBAAqB,CAACC,WAAW;QACpE;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAatD,eAAO,MAAM,UAAU,EAAE,UAexB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { authCookiePlugin } from '#presentation/auth/refresh-cookie.js';
|
|
2
|
+
import { verifyEmailConfirmRoute } from './email-verification/verify-email-confirm.js';
|
|
3
|
+
import { verifyEmailResendRoute } from './email-verification/verify-email-resend.js';
|
|
4
|
+
import { changePasswordRoute } from './password/change-password.js';
|
|
5
|
+
import { passwordResetConfirmRoute } from './password/password-reset-confirm.js';
|
|
6
|
+
import { passwordResetRequestRoute } from './password/password-reset-request.js';
|
|
7
|
+
import { signupRoute } from './registration/signup.js';
|
|
8
|
+
import { loginRoute } from './session/login.js';
|
|
9
|
+
import { logoutRoute } from './session/logout.js';
|
|
10
|
+
import { meRoute } from './session/me.js';
|
|
11
|
+
import { refreshRoute } from './session/refresh.js';
|
|
12
|
+
export const authRoutes = {
|
|
13
|
+
prefix: '/auth',
|
|
14
|
+
plugins: [
|
|
15
|
+
authCookiePlugin
|
|
16
|
+
],
|
|
17
|
+
routes: [
|
|
18
|
+
signupRoute,
|
|
19
|
+
verifyEmailConfirmRoute,
|
|
20
|
+
verifyEmailResendRoute,
|
|
21
|
+
loginRoute,
|
|
22
|
+
refreshRoute,
|
|
23
|
+
logoutRoute,
|
|
24
|
+
meRoute,
|
|
25
|
+
changePasswordRoute,
|
|
26
|
+
passwordResetRequestRoute,
|
|
27
|
+
passwordResetConfirmRoute
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {RouteGroup} from '@shipfox/node-fastify';\nimport {authCookiePlugin} from '#presentation/auth/refresh-cookie.js';\nimport {verifyEmailConfirmRoute} from './email-verification/verify-email-confirm.js';\nimport {verifyEmailResendRoute} from './email-verification/verify-email-resend.js';\nimport {changePasswordRoute} from './password/change-password.js';\nimport {passwordResetConfirmRoute} from './password/password-reset-confirm.js';\nimport {passwordResetRequestRoute} from './password/password-reset-request.js';\nimport {signupRoute} from './registration/signup.js';\nimport {loginRoute} from './session/login.js';\nimport {logoutRoute} from './session/logout.js';\nimport {meRoute} from './session/me.js';\nimport {refreshRoute} from './session/refresh.js';\n\nexport const authRoutes: RouteGroup = {\n prefix: '/auth',\n plugins: [authCookiePlugin],\n routes: [\n signupRoute,\n verifyEmailConfirmRoute,\n verifyEmailResendRoute,\n loginRoute,\n refreshRoute,\n logoutRoute,\n meRoute,\n changePasswordRoute,\n passwordResetRequestRoute,\n passwordResetConfirmRoute,\n ],\n};\n"],"names":["authCookiePlugin","verifyEmailConfirmRoute","verifyEmailResendRoute","changePasswordRoute","passwordResetConfirmRoute","passwordResetRequestRoute","signupRoute","loginRoute","logoutRoute","meRoute","refreshRoute","authRoutes","prefix","plugins","routes"],"mappings":"AACA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,uBAAuB,QAAO,+CAA+C;AACrF,SAAQC,sBAAsB,QAAO,8CAA8C;AACnF,SAAQC,mBAAmB,QAAO,gCAAgC;AAClE,SAAQC,yBAAyB,QAAO,uCAAuC;AAC/E,SAAQC,yBAAyB,QAAO,uCAAuC;AAC/E,SAAQC,WAAW,QAAO,2BAA2B;AACrD,SAAQC,UAAU,QAAO,qBAAqB;AAC9C,SAAQC,WAAW,QAAO,sBAAsB;AAChD,SAAQC,OAAO,QAAO,kBAAkB;AACxC,SAAQC,YAAY,QAAO,uBAAuB;AAElD,OAAO,MAAMC,aAAyB;IACpCC,QAAQ;IACRC,SAAS;QAACb;KAAiB;IAC3Bc,QAAQ;QACNR;QACAL;QACAC;QACAK;QACAG;QACAF;QACAC;QACAN;QACAE;QACAD;KACD;AACH,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-password.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/password/change-password.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,mBAAmB,iDAmC9B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AUTH_USER } from '@shipfox/api-auth-context';
|
|
2
|
+
import { changePasswordBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { changePassword } from '#core/auth.js';
|
|
6
|
+
import { InvalidCredentialsError, UserNotFoundError } from '#core/errors.js';
|
|
7
|
+
import { getClientContext } from '#presentation/auth/jwt-auth.js';
|
|
8
|
+
import { getRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
9
|
+
export const changePasswordRoute = defineRoute({
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: '/change-password',
|
|
12
|
+
description: 'Change the signed-in user password.',
|
|
13
|
+
auth: AUTH_USER,
|
|
14
|
+
schema: {
|
|
15
|
+
body: changePasswordBodySchema,
|
|
16
|
+
response: {
|
|
17
|
+
204: z.void()
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
errorHandler: (error)=>{
|
|
21
|
+
if (error instanceof UserNotFoundError) {
|
|
22
|
+
throw new ClientError('User not found', 'not-found', {
|
|
23
|
+
status: 404
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (error instanceof InvalidCredentialsError) {
|
|
27
|
+
throw new ClientError('Invalid credentials', 'invalid-credentials', {
|
|
28
|
+
status: 401
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
},
|
|
33
|
+
handler: async (request, reply)=>{
|
|
34
|
+
const client = getClientContext(request);
|
|
35
|
+
if (!client) {
|
|
36
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
37
|
+
status: 401
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
await changePassword({
|
|
41
|
+
userId: client.userId,
|
|
42
|
+
currentPassword: request.body.current_password,
|
|
43
|
+
newPassword: request.body.new_password,
|
|
44
|
+
refreshToken: getRefreshTokenCookie(request)
|
|
45
|
+
});
|
|
46
|
+
reply.code(204);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=change-password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/password/change-password.ts"],"sourcesContent":["import {AUTH_USER} from '@shipfox/api-auth-context';\nimport {changePasswordBodySchema} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {changePassword} from '#core/auth.js';\nimport {InvalidCredentialsError, UserNotFoundError} from '#core/errors.js';\nimport {getClientContext} from '#presentation/auth/jwt-auth.js';\nimport {getRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\n\nexport const changePasswordRoute = defineRoute({\n method: 'POST',\n path: '/change-password',\n description: 'Change the signed-in user password.',\n auth: AUTH_USER,\n schema: {\n body: changePasswordBodySchema,\n response: {\n 204: z.void(),\n },\n },\n errorHandler: (error) => {\n if (error instanceof UserNotFoundError) {\n throw new ClientError('User not found', 'not-found', {status: 404});\n }\n if (error instanceof InvalidCredentialsError) {\n throw new ClientError('Invalid credentials', 'invalid-credentials', {status: 401});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getClientContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n await changePassword({\n userId: client.userId,\n currentPassword: request.body.current_password,\n newPassword: request.body.new_password,\n refreshToken: getRefreshTokenCookie(request),\n });\n\n reply.code(204);\n },\n});\n"],"names":["AUTH_USER","changePasswordBodySchema","ClientError","defineRoute","z","changePassword","InvalidCredentialsError","UserNotFoundError","getClientContext","getRefreshTokenCookie","changePasswordRoute","method","path","description","auth","schema","body","response","void","errorHandler","error","status","handler","request","reply","client","userId","currentPassword","current_password","newPassword","new_password","refreshToken","code"],"mappings":"AAAA,SAAQA,SAAS,QAAO,4BAA4B;AACpD,SAAQC,wBAAwB,QAAO,wBAAwB;AAC/D,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,cAAc,QAAO,gBAAgB;AAC7C,SAAQC,uBAAuB,EAAEC,iBAAiB,QAAO,kBAAkB;AAC3E,SAAQC,gBAAgB,QAAO,iCAAiC;AAChE,SAAQC,qBAAqB,QAAO,uCAAuC;AAE3E,OAAO,MAAMC,sBAAsBP,YAAY;IAC7CQ,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMd;IACNe,QAAQ;QACNC,MAAMf;QACNgB,UAAU;YACR,KAAKb,EAAEc,IAAI;QACb;IACF;IACAC,cAAc,CAACC;QACb,IAAIA,iBAAiBb,mBAAmB;YACtC,MAAM,IAAIL,YAAY,kBAAkB,aAAa;gBAACmB,QAAQ;YAAG;QACnE;QACA,IAAID,iBAAiBd,yBAAyB;YAC5C,MAAM,IAAIJ,YAAY,uBAAuB,uBAAuB;gBAACmB,QAAQ;YAAG;QAClF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAASjB,iBAAiBe;QAChC,IAAI,CAACE,QAAQ;YACX,MAAM,IAAIvB,YAAY,2BAA2B,gBAAgB;gBAACmB,QAAQ;YAAG;QAC/E;QAEA,MAAMhB,eAAe;YACnBqB,QAAQD,OAAOC,MAAM;YACrBC,iBAAiBJ,QAAQP,IAAI,CAACY,gBAAgB;YAC9CC,aAAaN,QAAQP,IAAI,CAACc,YAAY;YACtCC,cAActB,sBAAsBc;QACtC;QAEAC,MAAMQ,IAAI,CAAC;IACb;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-confirm.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/password/password-reset-confirm.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,yBAAyB,iDAwBpC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { passwordResetConfirmBodySchema, passwordResetConfirmResponseSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { confirmPasswordReset } from '#core/auth.js';
|
|
4
|
+
import { TokenInvalidError } from '#core/errors.js';
|
|
5
|
+
import { setRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
6
|
+
import { toAuthSessionDto } from '#presentation/dto/user.js';
|
|
7
|
+
export const passwordResetConfirmRoute = defineRoute({
|
|
8
|
+
method: 'POST',
|
|
9
|
+
path: '/password-reset/confirm',
|
|
10
|
+
description: 'Set a new password using the link sent by email.',
|
|
11
|
+
schema: {
|
|
12
|
+
body: passwordResetConfirmBodySchema,
|
|
13
|
+
response: {
|
|
14
|
+
200: passwordResetConfirmResponseSchema
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
errorHandler: (error)=>{
|
|
18
|
+
if (error instanceof TokenInvalidError) {
|
|
19
|
+
throw new ClientError('Reset token is invalid or expired', 'token-invalid', {
|
|
20
|
+
status: 410
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
},
|
|
25
|
+
handler: async (request, reply)=>{
|
|
26
|
+
const { token, new_password } = request.body;
|
|
27
|
+
const result = await confirmPasswordReset({
|
|
28
|
+
token,
|
|
29
|
+
newPassword: new_password
|
|
30
|
+
});
|
|
31
|
+
setRefreshTokenCookie(reply, result.refreshToken);
|
|
32
|
+
return toAuthSessionDto(result);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=password-reset-confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/password/password-reset-confirm.ts"],"sourcesContent":["import {\n passwordResetConfirmBodySchema,\n passwordResetConfirmResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {confirmPasswordReset} from '#core/auth.js';\nimport {TokenInvalidError} from '#core/errors.js';\nimport {setRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\nimport {toAuthSessionDto} from '#presentation/dto/user.js';\n\nexport const passwordResetConfirmRoute = defineRoute({\n method: 'POST',\n path: '/password-reset/confirm',\n description: 'Set a new password using the link sent by email.',\n schema: {\n body: passwordResetConfirmBodySchema,\n response: {\n 200: passwordResetConfirmResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof TokenInvalidError) {\n throw new ClientError('Reset token is invalid or expired', 'token-invalid', {status: 410});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {token, new_password} = request.body;\n\n const result = await confirmPasswordReset({token, newPassword: new_password});\n setRefreshTokenCookie(reply, result.refreshToken);\n\n return toAuthSessionDto(result);\n },\n});\n"],"names":["passwordResetConfirmBodySchema","passwordResetConfirmResponseSchema","ClientError","defineRoute","confirmPasswordReset","TokenInvalidError","setRefreshTokenCookie","toAuthSessionDto","passwordResetConfirmRoute","method","path","description","schema","body","response","errorHandler","error","status","handler","request","reply","token","new_password","result","newPassword","refreshToken"],"mappings":"AAAA,SACEA,8BAA8B,EAC9BC,kCAAkC,QAC7B,wBAAwB;AAC/B,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,oBAAoB,QAAO,gBAAgB;AACnD,SAAQC,iBAAiB,QAAO,kBAAkB;AAClD,SAAQC,qBAAqB,QAAO,uCAAuC;AAC3E,SAAQC,gBAAgB,QAAO,4BAA4B;AAE3D,OAAO,MAAMC,4BAA4BL,YAAY;IACnDM,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMb;QACNc,UAAU;YACR,KAAKb;QACP;IACF;IACAc,cAAc,CAACC;QACb,IAAIA,iBAAiBX,mBAAmB;YACtC,MAAM,IAAIH,YAAY,qCAAqC,iBAAiB;gBAACe,QAAQ;YAAG;QAC1F;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAM,EAACC,KAAK,EAAEC,YAAY,EAAC,GAAGH,QAAQN,IAAI;QAE1C,MAAMU,SAAS,MAAMnB,qBAAqB;YAACiB;YAAOG,aAAaF;QAAY;QAC3EhB,sBAAsBc,OAAOG,OAAOE,YAAY;QAEhD,OAAOlB,iBAAiBgB;IAC1B;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-request.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/password/password-reset-request.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,yBAAyB,iDAkBpC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { passwordResetRequestBodySchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { requestPasswordReset } from '#core/auth.js';
|
|
5
|
+
import { createAuthRateLimitPreHandler } from '#presentation/routes/rate-limit.js';
|
|
6
|
+
export const passwordResetRequestRoute = defineRoute({
|
|
7
|
+
method: 'POST',
|
|
8
|
+
path: '/password-reset',
|
|
9
|
+
description: 'Send a password reset email when an account is eligible.',
|
|
10
|
+
schema: {
|
|
11
|
+
body: passwordResetRequestBodySchema,
|
|
12
|
+
response: {
|
|
13
|
+
204: z.void()
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
preHandler: createAuthRateLimitPreHandler('email-send'),
|
|
17
|
+
handler: async (request, reply)=>{
|
|
18
|
+
const { email } = request.body;
|
|
19
|
+
await requestPasswordReset({
|
|
20
|
+
email
|
|
21
|
+
});
|
|
22
|
+
reply.code(204);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=password-reset-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/password/password-reset-request.ts"],"sourcesContent":["import {passwordResetRequestBodySchema} from '@shipfox/api-auth-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {requestPasswordReset} from '#core/auth.js';\nimport {createAuthRateLimitPreHandler} from '#presentation/routes/rate-limit.js';\n\nexport const passwordResetRequestRoute = defineRoute({\n method: 'POST',\n path: '/password-reset',\n description: 'Send a password reset email when an account is eligible.',\n schema: {\n body: passwordResetRequestBodySchema,\n response: {\n 204: z.void(),\n },\n },\n preHandler: createAuthRateLimitPreHandler('email-send'),\n handler: async (request, reply) => {\n const {email} = request.body;\n\n await requestPasswordReset({email});\n\n reply.code(204);\n },\n});\n"],"names":["passwordResetRequestBodySchema","defineRoute","z","requestPasswordReset","createAuthRateLimitPreHandler","passwordResetRequestRoute","method","path","description","schema","body","response","void","preHandler","handler","request","reply","email","code"],"mappings":"AAAA,SAAQA,8BAA8B,QAAO,wBAAwB;AACrE,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,oBAAoB,QAAO,gBAAgB;AACnD,SAAQC,6BAA6B,QAAO,qCAAqC;AAEjF,OAAO,MAAMC,4BAA4BJ,YAAY;IACnDK,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMV;QACNW,UAAU;YACR,KAAKT,EAAEU,IAAI;QACb;IACF;IACAC,YAAYT,8BAA8B;IAC1CU,SAAS,OAAOC,SAASC;QACvB,MAAM,EAACC,KAAK,EAAC,GAAGF,QAAQL,IAAI;QAE5B,MAAMP,qBAAqB;YAACc;QAAK;QAEjCD,MAAME,IAAI,CAAC;IACb;AACF,GAAG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type FastifyReply, type FastifyRequest } from '@shipfox/node-fastify';
|
|
2
|
+
import { type AuthRateLimitAction } from '#core/rate-limit.js';
|
|
3
|
+
interface EmailBody {
|
|
4
|
+
email: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function createAuthRateLimitPreHandler(action: AuthRateLimitAction): (request: FastifyRequest<{
|
|
7
|
+
Body: EmailBody;
|
|
8
|
+
}>, reply: FastifyReply) => Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EACL,KAAK,mBAAmB,EAMzB,MAAM,qBAAqB,CAAC;AAa7B,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AA6ED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,mBAAmB,IACzD,SAAS,cAAc,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,CAAC,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ClientError } from '@shipfox/node-fastify';
|
|
2
|
+
import { AuthRateLimitExceededError, AuthRateLimitUnavailableError, checkAuthRateLimit } from '#core/rate-limit.js';
|
|
3
|
+
const policies = {
|
|
4
|
+
login: {
|
|
5
|
+
ip: {
|
|
6
|
+
limit: 60,
|
|
7
|
+
windowSeconds: 5 * 60
|
|
8
|
+
},
|
|
9
|
+
email: {
|
|
10
|
+
limit: 10,
|
|
11
|
+
windowSeconds: 15 * 60
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
'email-send': {
|
|
15
|
+
ip: {
|
|
16
|
+
limit: 30,
|
|
17
|
+
windowSeconds: 60 * 60
|
|
18
|
+
},
|
|
19
|
+
email: {
|
|
20
|
+
limit: 3,
|
|
21
|
+
windowSeconds: 60 * 60
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function routeName(request) {
|
|
26
|
+
return request.routeOptions.url ?? request.url.split('?')[0] ?? 'unknown';
|
|
27
|
+
}
|
|
28
|
+
async function enforceRateLimit(params) {
|
|
29
|
+
try {
|
|
30
|
+
await checkAuthRateLimit({
|
|
31
|
+
action: params.action,
|
|
32
|
+
scope: params.scope,
|
|
33
|
+
identifier: params.identifier,
|
|
34
|
+
...policies[params.action][params.scope]
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof AuthRateLimitExceededError) {
|
|
38
|
+
params.request.log.warn({
|
|
39
|
+
action: error.action,
|
|
40
|
+
scope: error.scope,
|
|
41
|
+
route: routeName(params.request),
|
|
42
|
+
retryAfterSeconds: error.retryAfterSeconds,
|
|
43
|
+
identifierHmacPrefix: error.identifierHmacPrefix
|
|
44
|
+
}, 'Auth rate limit blocked request');
|
|
45
|
+
params.reply.header('Retry-After', String(error.retryAfterSeconds));
|
|
46
|
+
throw new ClientError('Rate limit exceeded', 'rate-limited', {
|
|
47
|
+
status: 429,
|
|
48
|
+
details: {
|
|
49
|
+
retry_after_seconds: error.retryAfterSeconds
|
|
50
|
+
},
|
|
51
|
+
data: {
|
|
52
|
+
action: error.action,
|
|
53
|
+
scope: error.scope,
|
|
54
|
+
route: routeName(params.request),
|
|
55
|
+
identifierHmacPrefix: error.identifierHmacPrefix
|
|
56
|
+
},
|
|
57
|
+
cause: error
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof AuthRateLimitUnavailableError) {
|
|
61
|
+
params.request.log.error({
|
|
62
|
+
action: error.action,
|
|
63
|
+
scope: error.scope,
|
|
64
|
+
route: routeName(params.request),
|
|
65
|
+
identifierHmacPrefix: error.identifierHmacPrefix,
|
|
66
|
+
err: error
|
|
67
|
+
}, 'Auth rate limiter unavailable');
|
|
68
|
+
throw new ClientError('Authentication rate limiter unavailable', 'auth-rate-limit-unavailable', {
|
|
69
|
+
status: 503,
|
|
70
|
+
data: {
|
|
71
|
+
action: error.action,
|
|
72
|
+
scope: error.scope,
|
|
73
|
+
route: routeName(params.request),
|
|
74
|
+
identifierHmacPrefix: error.identifierHmacPrefix
|
|
75
|
+
},
|
|
76
|
+
cause: error
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function createAuthRateLimitPreHandler(action) {
|
|
83
|
+
return async (request, reply)=>{
|
|
84
|
+
await enforceRateLimit({
|
|
85
|
+
request,
|
|
86
|
+
reply,
|
|
87
|
+
action,
|
|
88
|
+
scope: 'ip',
|
|
89
|
+
identifier: request.ip
|
|
90
|
+
});
|
|
91
|
+
await enforceRateLimit({
|
|
92
|
+
request,
|
|
93
|
+
reply,
|
|
94
|
+
action,
|
|
95
|
+
scope: 'email',
|
|
96
|
+
identifier: request.body.email
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/rate-limit.ts"],"sourcesContent":["import {ClientError, type FastifyReply, type FastifyRequest} from '@shipfox/node-fastify';\nimport {\n type AuthRateLimitAction,\n AuthRateLimitExceededError,\n type AuthRateLimitPolicy,\n type AuthRateLimitScope,\n AuthRateLimitUnavailableError,\n checkAuthRateLimit,\n} from '#core/rate-limit.js';\n\nconst policies: Record<AuthRateLimitAction, Record<AuthRateLimitScope, AuthRateLimitPolicy>> = {\n login: {\n ip: {limit: 60, windowSeconds: 5 * 60},\n email: {limit: 10, windowSeconds: 15 * 60},\n },\n 'email-send': {\n ip: {limit: 30, windowSeconds: 60 * 60},\n email: {limit: 3, windowSeconds: 60 * 60},\n },\n};\n\ninterface EmailBody {\n email: string;\n}\n\nfunction routeName(request: FastifyRequest): string {\n return request.routeOptions.url ?? request.url.split('?')[0] ?? 'unknown';\n}\n\nasync function enforceRateLimit(params: {\n request: FastifyRequest;\n reply: FastifyReply;\n action: AuthRateLimitAction;\n scope: AuthRateLimitScope;\n identifier: string;\n}): Promise<void> {\n try {\n await checkAuthRateLimit({\n action: params.action,\n scope: params.scope,\n identifier: params.identifier,\n ...policies[params.action][params.scope],\n });\n } catch (error) {\n if (error instanceof AuthRateLimitExceededError) {\n params.request.log.warn(\n {\n action: error.action,\n scope: error.scope,\n route: routeName(params.request),\n retryAfterSeconds: error.retryAfterSeconds,\n identifierHmacPrefix: error.identifierHmacPrefix,\n },\n 'Auth rate limit blocked request',\n );\n params.reply.header('Retry-After', String(error.retryAfterSeconds));\n throw new ClientError('Rate limit exceeded', 'rate-limited', {\n status: 429,\n details: {retry_after_seconds: error.retryAfterSeconds},\n data: {\n action: error.action,\n scope: error.scope,\n route: routeName(params.request),\n identifierHmacPrefix: error.identifierHmacPrefix,\n },\n cause: error,\n });\n }\n\n if (error instanceof AuthRateLimitUnavailableError) {\n params.request.log.error(\n {\n action: error.action,\n scope: error.scope,\n route: routeName(params.request),\n identifierHmacPrefix: error.identifierHmacPrefix,\n err: error,\n },\n 'Auth rate limiter unavailable',\n );\n throw new ClientError(\n 'Authentication rate limiter unavailable',\n 'auth-rate-limit-unavailable',\n {\n status: 503,\n data: {\n action: error.action,\n scope: error.scope,\n route: routeName(params.request),\n identifierHmacPrefix: error.identifierHmacPrefix,\n },\n cause: error,\n },\n );\n }\n\n throw error;\n }\n}\n\nexport function createAuthRateLimitPreHandler(action: AuthRateLimitAction) {\n return async (request: FastifyRequest<{Body: EmailBody}>, reply: FastifyReply): Promise<void> => {\n await enforceRateLimit({\n request,\n reply,\n action,\n scope: 'ip',\n identifier: request.ip,\n });\n\n await enforceRateLimit({\n request,\n reply,\n action,\n scope: 'email',\n identifier: request.body.email,\n });\n };\n}\n"],"names":["ClientError","AuthRateLimitExceededError","AuthRateLimitUnavailableError","checkAuthRateLimit","policies","login","ip","limit","windowSeconds","email","routeName","request","routeOptions","url","split","enforceRateLimit","params","action","scope","identifier","error","log","warn","route","retryAfterSeconds","identifierHmacPrefix","reply","header","String","status","details","retry_after_seconds","data","cause","err","createAuthRateLimitPreHandler","body"],"mappings":"AAAA,SAAQA,WAAW,QAA+C,wBAAwB;AAC1F,SAEEC,0BAA0B,EAG1BC,6BAA6B,EAC7BC,kBAAkB,QACb,sBAAsB;AAE7B,MAAMC,WAAyF;IAC7FC,OAAO;QACLC,IAAI;YAACC,OAAO;YAAIC,eAAe,IAAI;QAAE;QACrCC,OAAO;YAACF,OAAO;YAAIC,eAAe,KAAK;QAAE;IAC3C;IACA,cAAc;QACZF,IAAI;YAACC,OAAO;YAAIC,eAAe,KAAK;QAAE;QACtCC,OAAO;YAACF,OAAO;YAAGC,eAAe,KAAK;QAAE;IAC1C;AACF;AAMA,SAASE,UAAUC,OAAuB;IACxC,OAAOA,QAAQC,YAAY,CAACC,GAAG,IAAIF,QAAQE,GAAG,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI;AAClE;AAEA,eAAeC,iBAAiBC,MAM/B;IACC,IAAI;QACF,MAAMb,mBAAmB;YACvBc,QAAQD,OAAOC,MAAM;YACrBC,OAAOF,OAAOE,KAAK;YACnBC,YAAYH,OAAOG,UAAU;YAC7B,GAAGf,QAAQ,CAACY,OAAOC,MAAM,CAAC,CAACD,OAAOE,KAAK,CAAC;QAC1C;IACF,EAAE,OAAOE,OAAO;QACd,IAAIA,iBAAiBnB,4BAA4B;YAC/Ce,OAAOL,OAAO,CAACU,GAAG,CAACC,IAAI,CACrB;gBACEL,QAAQG,MAAMH,MAAM;gBACpBC,OAAOE,MAAMF,KAAK;gBAClBK,OAAOb,UAAUM,OAAOL,OAAO;gBAC/Ba,mBAAmBJ,MAAMI,iBAAiB;gBAC1CC,sBAAsBL,MAAMK,oBAAoB;YAClD,GACA;YAEFT,OAAOU,KAAK,CAACC,MAAM,CAAC,eAAeC,OAAOR,MAAMI,iBAAiB;YACjE,MAAM,IAAIxB,YAAY,uBAAuB,gBAAgB;gBAC3D6B,QAAQ;gBACRC,SAAS;oBAACC,qBAAqBX,MAAMI,iBAAiB;gBAAA;gBACtDQ,MAAM;oBACJf,QAAQG,MAAMH,MAAM;oBACpBC,OAAOE,MAAMF,KAAK;oBAClBK,OAAOb,UAAUM,OAAOL,OAAO;oBAC/Bc,sBAAsBL,MAAMK,oBAAoB;gBAClD;gBACAQ,OAAOb;YACT;QACF;QAEA,IAAIA,iBAAiBlB,+BAA+B;YAClDc,OAAOL,OAAO,CAACU,GAAG,CAACD,KAAK,CACtB;gBACEH,QAAQG,MAAMH,MAAM;gBACpBC,OAAOE,MAAMF,KAAK;gBAClBK,OAAOb,UAAUM,OAAOL,OAAO;gBAC/Bc,sBAAsBL,MAAMK,oBAAoB;gBAChDS,KAAKd;YACP,GACA;YAEF,MAAM,IAAIpB,YACR,2CACA,+BACA;gBACE6B,QAAQ;gBACRG,MAAM;oBACJf,QAAQG,MAAMH,MAAM;oBACpBC,OAAOE,MAAMF,KAAK;oBAClBK,OAAOb,UAAUM,OAAOL,OAAO;oBAC/Bc,sBAAsBL,MAAMK,oBAAoB;gBAClD;gBACAQ,OAAOb;YACT;QAEJ;QAEA,MAAMA;IACR;AACF;AAEA,OAAO,SAASe,8BAA8BlB,MAA2B;IACvE,OAAO,OAAON,SAA4Ce;QACxD,MAAMX,iBAAiB;YACrBJ;YACAe;YACAT;YACAC,OAAO;YACPC,YAAYR,QAAQL,EAAE;QACxB;QAEA,MAAMS,iBAAiB;YACrBJ;YACAe;YACAT;YACAC,OAAO;YACPC,YAAYR,QAAQyB,IAAI,CAAC3B,KAAK;QAChC;IACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/registration/signup.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,WAAW,iDAmEtB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { signupBodySchema, signupResponseSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { InvitationEmailMismatchError, TokenAlreadyUsedError, TokenExpiredError, TokenInvalidError as WorkspacesTokenInvalidError } from '@shipfox/api-workspaces';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { signup, signupWithInvitation } from '#core/auth.js';
|
|
5
|
+
import { EmailTakenError } from '#core/errors.js';
|
|
6
|
+
import { setRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
7
|
+
import { toUserDto } from '#presentation/dto/user.js';
|
|
8
|
+
export const signupRoute = defineRoute({
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: '/signup',
|
|
11
|
+
description: 'Create a user account. Sends a verification email unless an invitation token is supplied.',
|
|
12
|
+
schema: {
|
|
13
|
+
body: signupBodySchema,
|
|
14
|
+
response: {
|
|
15
|
+
201: signupResponseSchema
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
errorHandler: (error)=>{
|
|
19
|
+
if (error instanceof EmailTakenError) {
|
|
20
|
+
throw new ClientError('Email already registered', 'email-taken', {
|
|
21
|
+
status: 409
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (error instanceof WorkspacesTokenInvalidError) {
|
|
25
|
+
throw new ClientError('Invitation token is invalid', 'invitation-token-invalid', {
|
|
26
|
+
status: 410
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (error instanceof TokenAlreadyUsedError) {
|
|
30
|
+
throw new ClientError('Invitation has already been accepted', 'invitation-token-used', {
|
|
31
|
+
status: 410
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (error instanceof TokenExpiredError) {
|
|
35
|
+
throw new ClientError('Invitation has expired', 'invitation-token-expired', {
|
|
36
|
+
status: 410
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof InvitationEmailMismatchError) {
|
|
40
|
+
throw new ClientError('Signup email does not match the invitation', 'invitation-email-mismatch', {
|
|
41
|
+
status: 403
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
},
|
|
46
|
+
handler: async (request, reply)=>{
|
|
47
|
+
const { email, password, name, invitation_token } = request.body;
|
|
48
|
+
if (invitation_token === undefined) {
|
|
49
|
+
const user = await signup({
|
|
50
|
+
email,
|
|
51
|
+
password,
|
|
52
|
+
name
|
|
53
|
+
});
|
|
54
|
+
reply.code(201);
|
|
55
|
+
return {
|
|
56
|
+
user: toUserDto(user)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const result = await signupWithInvitation({
|
|
60
|
+
email,
|
|
61
|
+
password,
|
|
62
|
+
name,
|
|
63
|
+
invitationToken: invitation_token
|
|
64
|
+
});
|
|
65
|
+
setRefreshTokenCookie(reply, result.refreshToken);
|
|
66
|
+
reply.code(201);
|
|
67
|
+
return {
|
|
68
|
+
user: toUserDto(result.user),
|
|
69
|
+
token: result.token,
|
|
70
|
+
membership: result.membership ? {
|
|
71
|
+
id: result.membership.id,
|
|
72
|
+
user_id: result.membership.userId,
|
|
73
|
+
workspace_id: result.membership.workspaceId
|
|
74
|
+
} : null,
|
|
75
|
+
...result.acceptError ? {
|
|
76
|
+
accept_error: result.acceptError
|
|
77
|
+
} : {}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=signup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/registration/signup.ts"],"sourcesContent":["import {signupBodySchema, signupResponseSchema} from '@shipfox/api-auth-dto';\nimport {\n InvitationEmailMismatchError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError as WorkspacesTokenInvalidError,\n} from '@shipfox/api-workspaces';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {signup, signupWithInvitation} from '#core/auth.js';\nimport {EmailTakenError} from '#core/errors.js';\nimport {setRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\nimport {toUserDto} from '#presentation/dto/user.js';\n\nexport const signupRoute = defineRoute({\n method: 'POST',\n path: '/signup',\n description:\n 'Create a user account. Sends a verification email unless an invitation token is supplied.',\n schema: {\n body: signupBodySchema,\n response: {\n 201: signupResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof EmailTakenError) {\n throw new ClientError('Email already registered', 'email-taken', {status: 409});\n }\n if (error instanceof WorkspacesTokenInvalidError) {\n throw new ClientError('Invitation token is invalid', 'invitation-token-invalid', {\n status: 410,\n });\n }\n if (error instanceof TokenAlreadyUsedError) {\n throw new ClientError('Invitation has already been accepted', 'invitation-token-used', {\n status: 410,\n });\n }\n if (error instanceof TokenExpiredError) {\n throw new ClientError('Invitation has expired', 'invitation-token-expired', {status: 410});\n }\n if (error instanceof InvitationEmailMismatchError) {\n throw new ClientError(\n 'Signup email does not match the invitation',\n 'invitation-email-mismatch',\n {status: 403},\n );\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {email, password, name, invitation_token} = request.body;\n\n if (invitation_token === undefined) {\n const user = await signup({email, password, name});\n reply.code(201);\n return {user: toUserDto(user)};\n }\n\n const result = await signupWithInvitation({\n email,\n password,\n name,\n invitationToken: invitation_token,\n });\n setRefreshTokenCookie(reply, result.refreshToken);\n reply.code(201);\n return {\n user: toUserDto(result.user),\n token: result.token,\n membership: result.membership\n ? {\n id: result.membership.id,\n user_id: result.membership.userId,\n workspace_id: result.membership.workspaceId,\n }\n : null,\n ...(result.acceptError ? {accept_error: result.acceptError} : {}),\n };\n },\n});\n"],"names":["signupBodySchema","signupResponseSchema","InvitationEmailMismatchError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","WorkspacesTokenInvalidError","ClientError","defineRoute","signup","signupWithInvitation","EmailTakenError","setRefreshTokenCookie","toUserDto","signupRoute","method","path","description","schema","body","response","errorHandler","error","status","handler","request","reply","email","password","name","invitation_token","undefined","user","code","result","invitationToken","refreshToken","token","membership","id","user_id","userId","workspace_id","workspaceId","acceptError","accept_error"],"mappings":"AAAA,SAAQA,gBAAgB,EAAEC,oBAAoB,QAAO,wBAAwB;AAC7E,SACEC,4BAA4B,EAC5BC,qBAAqB,EACrBC,iBAAiB,EACjBC,qBAAqBC,2BAA2B,QAC3C,0BAA0B;AACjC,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,MAAM,EAAEC,oBAAoB,QAAO,gBAAgB;AAC3D,SAAQC,eAAe,QAAO,kBAAkB;AAChD,SAAQC,qBAAqB,QAAO,uCAAuC;AAC3E,SAAQC,SAAS,QAAO,4BAA4B;AAEpD,OAAO,MAAMC,cAAcN,YAAY;IACrCO,QAAQ;IACRC,MAAM;IACNC,aACE;IACFC,QAAQ;QACNC,MAAMnB;QACNoB,UAAU;YACR,KAAKnB;QACP;IACF;IACAoB,cAAc,CAACC;QACb,IAAIA,iBAAiBX,iBAAiB;YACpC,MAAM,IAAIJ,YAAY,4BAA4B,eAAe;gBAACgB,QAAQ;YAAG;QAC/E;QACA,IAAID,iBAAiBhB,6BAA6B;YAChD,MAAM,IAAIC,YAAY,+BAA+B,4BAA4B;gBAC/EgB,QAAQ;YACV;QACF;QACA,IAAID,iBAAiBnB,uBAAuB;YAC1C,MAAM,IAAII,YAAY,wCAAwC,yBAAyB;gBACrFgB,QAAQ;YACV;QACF;QACA,IAAID,iBAAiBlB,mBAAmB;YACtC,MAAM,IAAIG,YAAY,0BAA0B,4BAA4B;gBAACgB,QAAQ;YAAG;QAC1F;QACA,IAAID,iBAAiBpB,8BAA8B;YACjD,MAAM,IAAIK,YACR,8CACA,6BACA;gBAACgB,QAAQ;YAAG;QAEhB;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAM,EAACC,KAAK,EAAEC,QAAQ,EAAEC,IAAI,EAAEC,gBAAgB,EAAC,GAAGL,QAAQN,IAAI;QAE9D,IAAIW,qBAAqBC,WAAW;YAClC,MAAMC,OAAO,MAAMvB,OAAO;gBAACkB;gBAAOC;gBAAUC;YAAI;YAChDH,MAAMO,IAAI,CAAC;YACX,OAAO;gBAACD,MAAMnB,UAAUmB;YAAK;QAC/B;QAEA,MAAME,SAAS,MAAMxB,qBAAqB;YACxCiB;YACAC;YACAC;YACAM,iBAAiBL;QACnB;QACAlB,sBAAsBc,OAAOQ,OAAOE,YAAY;QAChDV,MAAMO,IAAI,CAAC;QACX,OAAO;YACLD,MAAMnB,UAAUqB,OAAOF,IAAI;YAC3BK,OAAOH,OAAOG,KAAK;YACnBC,YAAYJ,OAAOI,UAAU,GACzB;gBACEC,IAAIL,OAAOI,UAAU,CAACC,EAAE;gBACxBC,SAASN,OAAOI,UAAU,CAACG,MAAM;gBACjCC,cAAcR,OAAOI,UAAU,CAACK,WAAW;YAC7C,IACA;YACJ,GAAIT,OAAOU,WAAW,GAAG;gBAACC,cAAcX,OAAOU,WAAW;YAAA,IAAI,CAAC,CAAC;QAClE;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/session/login.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,iDAqCrB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loginBodySchema, loginResponseSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { login } from '#core/auth.js';
|
|
4
|
+
import { AuthDependencyUnavailableError, EmailNotVerifiedError, InvalidCredentialsError } from '#core/errors.js';
|
|
5
|
+
import { setRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
6
|
+
import { toAuthSessionDto } from '#presentation/dto/user.js';
|
|
7
|
+
import { createAuthRateLimitPreHandler } from '#presentation/routes/rate-limit.js';
|
|
8
|
+
export const loginRoute = defineRoute({
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: '/login',
|
|
11
|
+
description: 'Sign in with an email address and password.',
|
|
12
|
+
schema: {
|
|
13
|
+
body: loginBodySchema,
|
|
14
|
+
response: {
|
|
15
|
+
200: loginResponseSchema
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
preHandler: createAuthRateLimitPreHandler('login'),
|
|
19
|
+
errorHandler: (error)=>{
|
|
20
|
+
if (error instanceof InvalidCredentialsError) {
|
|
21
|
+
throw new ClientError('Invalid credentials', 'invalid-credentials', {
|
|
22
|
+
status: 401
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (error instanceof EmailNotVerifiedError) {
|
|
26
|
+
throw new ClientError('Email not verified', 'email-not-verified', {
|
|
27
|
+
status: 403
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (error instanceof AuthDependencyUnavailableError) {
|
|
31
|
+
throw new ClientError('Authentication dependency unavailable', 'auth-dependency-unavailable', {
|
|
32
|
+
status: 503
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
},
|
|
37
|
+
handler: async (request, reply)=>{
|
|
38
|
+
const { email, password } = request.body;
|
|
39
|
+
const result = await login({
|
|
40
|
+
email,
|
|
41
|
+
password
|
|
42
|
+
});
|
|
43
|
+
setRefreshTokenCookie(reply, result.refreshToken);
|
|
44
|
+
return toAuthSessionDto(result);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=login.js.map
|