@shipfox/api-auth 9.1.0 → 9.3.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 +1 -1
- package/CHANGELOG.md +41 -0
- package/README.md +44 -2
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -1
- package/dist/core/admin-role-model.d.ts +5 -0
- package/dist/core/admin-role-model.d.ts.map +1 -0
- package/dist/core/admin-role-model.js +18 -0
- package/dist/core/admin-role-model.js.map +1 -0
- package/dist/core/admin-role.d.ts +13 -0
- package/dist/core/admin-role.d.ts.map +1 -0
- package/dist/core/admin-role.js +21 -0
- package/dist/core/admin-role.js.map +1 -0
- package/dist/core/administration.d.ts +27 -0
- package/dist/core/administration.d.ts.map +1 -0
- package/dist/core/administration.js +122 -0
- package/dist/core/administration.js.map +1 -0
- package/dist/core/auth.d.ts +9 -0
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +68 -20
- package/dist/core/auth.js.map +1 -1
- package/dist/core/entities/admin-grant.d.ts +10 -0
- package/dist/core/entities/admin-grant.d.ts.map +1 -0
- package/dist/core/entities/admin-grant.js +3 -0
- package/dist/core/entities/admin-grant.js.map +1 -0
- package/dist/core/errors.d.ts +25 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +49 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/ports.d.ts +13 -0
- package/dist/core/ports.d.ts.map +1 -0
- package/dist/core/ports.js +3 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/signup-policy.d.ts +10 -0
- package/dist/core/signup-policy.d.ts.map +1 -0
- package/dist/core/signup-policy.js +39 -0
- package/dist/core/signup-policy.js.map +1 -0
- package/dist/db/admin-grants.d.ts +33 -0
- package/dist/db/admin-grants.d.ts.map +1 -0
- package/dist/db/admin-grants.js +193 -0
- package/dist/db/admin-grants.js.map +1 -0
- package/dist/db/db.d.ts +474 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +4 -0
- package/dist/db/db.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +142 -0
- package/dist/db/schema/admin-command-results.d.ts.map +1 -0
- package/dist/db/schema/admin-command-results.js +21 -0
- package/dist/db/schema/admin-command-results.js.map +1 -0
- package/dist/db/schema/admin-grants.d.ts +115 -0
- package/dist/db/schema/admin-grants.d.ts.map +1 -0
- package/dist/db/schema/admin-grants.js +42 -0
- package/dist/db/schema/admin-grants.js.map +1 -0
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -4
- package/dist/index.js.map +1 -1
- package/dist/metrics/instance.d.ts +1 -1
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/user.d.ts +2 -1
- package/dist/presentation/dto/user.d.ts.map +1 -1
- package/dist/presentation/dto/user.js +2 -1
- package/dist/presentation/dto/user.js.map +1 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +26 -2
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/administration.d.ts +3 -0
- package/dist/presentation/routes/administration.d.ts.map +1 -0
- package/dist/presentation/routes/administration.js +186 -0
- package/dist/presentation/routes/administration.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +2 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +2 -2
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/rate-limit.d.ts +1 -0
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
- package/dist/presentation/routes/rate-limit.js +20 -1
- package/dist/presentation/routes/rate-limit.js.map +1 -1
- package/dist/presentation/routes/registration/signup.d.ts +2 -1
- package/dist/presentation/routes/registration/signup.d.ts.map +1 -1
- package/dist/presentation/routes/registration/signup.js +14 -3
- package/dist/presentation/routes/registration/signup.js.map +1 -1
- package/dist/presentation/routes/session/me.d.ts.map +1 -1
- package/dist/presentation/routes/session/me.js +5 -1
- package/dist/presentation/routes/session/me.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0001_brainy_shriek.sql +14 -0
- package/drizzle/0002_burly_whizzer.sql +12 -0
- package/drizzle/meta/0001_snapshot.json +702 -0
- package/drizzle/meta/0002_snapshot.json +791 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +10 -9
- package/src/config.test.ts +39 -0
- package/src/config.ts +34 -0
- package/src/core/admin-role-model.ts +25 -0
- package/src/core/admin-role.test.ts +39 -0
- package/src/core/admin-role.ts +25 -0
- package/src/core/administration.ts +172 -0
- package/src/core/auth-default-signup-policy.test.ts +65 -0
- package/src/core/auth.test.ts +115 -0
- package/src/core/auth.ts +77 -21
- package/src/core/entities/admin-grant.ts +10 -0
- package/src/core/errors.ts +59 -0
- package/src/core/jwt.test.ts +1 -0
- package/src/core/ports.ts +7 -0
- package/src/core/signup-policy.test.ts +78 -0
- package/src/core/signup-policy.ts +42 -0
- package/src/db/admin-grants.test.ts +57 -0
- package/src/db/admin-grants.ts +340 -0
- package/src/db/db.ts +4 -0
- package/src/db/schema/admin-command-results.ts +41 -0
- package/src/db/schema/admin-grants.ts +49 -0
- package/src/index.test.ts +58 -1
- package/src/index.ts +47 -6
- package/src/metrics/instance.ts +1 -1
- package/src/presentation/auth/refresh-cookie.test.ts +4 -0
- package/src/presentation/dto/user.ts +7 -2
- package/src/presentation/e2eRoutes/index.test.ts +1 -0
- package/src/presentation/inter-module.test.ts +47 -0
- package/src/presentation/inter-module.ts +22 -1
- package/src/presentation/routes/administration.test.ts +253 -0
- package/src/presentation/routes/administration.ts +195 -0
- package/src/presentation/routes/index.test.ts +1 -0
- package/src/presentation/routes/index.ts +3 -1
- package/src/presentation/routes/rate-limit.ts +23 -2
- package/src/presentation/routes/registration/signup.test.ts +26 -0
- package/src/presentation/routes/registration/signup.ts +22 -2
- package/src/presentation/routes/session/login.test.ts +1 -0
- package/src/presentation/routes/session/me.test.ts +18 -2
- package/src/presentation/routes/session/me.ts +5 -1
- package/test/globalSetup.ts +6 -2
- package/test/routes.ts +7 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { AUTH_USER } from '@shipfox/api-auth-context';
|
|
2
|
+
import { bootstrapAdminOwnerBodySchema, bootstrapAdminOwnerResponseSchema, grantAdminRoleBodySchema, grantAdminRoleResponseSchema, listAdminGrantsResponseSchema, revokeAdminGrantBodySchema, revokeAdminGrantResponseSchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { bootstrapFirstAdminOwner, grantAdministratorRole, listAdministratorGrants, revokeAdministratorGrant } from '#core/administration.js';
|
|
6
|
+
import { AdminBootstrapClosedError, AdminGrantAlreadyExistsError, AdminGrantNotFoundError, AdminIdempotencyKeyReuseError, AdminRoleRequiredError, InvalidAdminBootstrapTokenError, LastAdminOwnerError, UserNotFoundError } from '#core/errors.js';
|
|
7
|
+
import { getClientContext } from '#presentation/auth/jwt-auth.js';
|
|
8
|
+
import { createAuthIpRateLimitPreHandler } from './rate-limit.js';
|
|
9
|
+
const idempotencyKeyMaxLength = 256;
|
|
10
|
+
function requireActorId(request) {
|
|
11
|
+
const client = getClientContext(request);
|
|
12
|
+
if (!client) {
|
|
13
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
14
|
+
status: 401
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return client.userId;
|
|
18
|
+
}
|
|
19
|
+
function requireIdempotencyKey(request) {
|
|
20
|
+
const value = request.headers['idempotency-key'];
|
|
21
|
+
const key = Array.isArray(value) ? value[0] : value;
|
|
22
|
+
if (!key || key.trim().length === 0 || key.length > idempotencyKeyMaxLength) {
|
|
23
|
+
throw new ClientError('Idempotency-Key header is required', 'idempotency-key-required', {
|
|
24
|
+
status: 400
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
function toAdminGrantDto(grant) {
|
|
30
|
+
return {
|
|
31
|
+
id: grant.id,
|
|
32
|
+
user_id: grant.userId,
|
|
33
|
+
role: grant.role,
|
|
34
|
+
revoked_at: grant.revokedAt?.toISOString() ?? null,
|
|
35
|
+
created_at: grant.createdAt.toISOString(),
|
|
36
|
+
updated_at: grant.updatedAt.toISOString()
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function translateAdministrationError(error) {
|
|
40
|
+
if (error instanceof AdminRoleRequiredError) {
|
|
41
|
+
throw new ClientError('Administrator owner role required', 'forbidden', {
|
|
42
|
+
status: 403,
|
|
43
|
+
details: {
|
|
44
|
+
required_role: error.minimumRole
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (error instanceof InvalidAdminBootstrapTokenError) {
|
|
49
|
+
throw new ClientError('Bootstrap token is invalid', 'bootstrap-token-invalid', {
|
|
50
|
+
status: 403
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (error instanceof AdminBootstrapClosedError) {
|
|
54
|
+
throw new ClientError('First administrator owner already exists', 'bootstrap-closed', {
|
|
55
|
+
status: 409
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (error instanceof AdminGrantAlreadyExistsError) {
|
|
59
|
+
throw new ClientError('Administrator grant already exists', 'grant-already-exists', {
|
|
60
|
+
status: 409
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (error instanceof AdminGrantNotFoundError) {
|
|
64
|
+
throw new ClientError('Administrator grant not found', 'not-found', {
|
|
65
|
+
status: 404
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (error instanceof UserNotFoundError) {
|
|
69
|
+
throw new ClientError('User not found', 'not-found', {
|
|
70
|
+
status: 404
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (error instanceof LastAdminOwnerError) {
|
|
74
|
+
throw new ClientError('Cannot remove the final active administrator owner', 'last-owner', {
|
|
75
|
+
status: 409
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (error instanceof AdminIdempotencyKeyReuseError) {
|
|
79
|
+
throw new ClientError('Idempotency-Key was already used for a different command', 'idempotency-key-reused', {
|
|
80
|
+
status: 409
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
const bootstrapRoute = defineRoute({
|
|
86
|
+
method: 'POST',
|
|
87
|
+
path: '/bootstrap',
|
|
88
|
+
description: 'Claim the first administrator owner role with the deployment bootstrap token.',
|
|
89
|
+
schema: {
|
|
90
|
+
body: bootstrapAdminOwnerBodySchema,
|
|
91
|
+
response: {
|
|
92
|
+
201: bootstrapAdminOwnerResponseSchema
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
preHandler: createAuthIpRateLimitPreHandler('bootstrap'),
|
|
96
|
+
errorHandler: translateAdministrationError,
|
|
97
|
+
handler: async (request, reply)=>{
|
|
98
|
+
const actorId = requireActorId(request);
|
|
99
|
+
const grant = await bootstrapFirstAdminOwner({
|
|
100
|
+
actorId,
|
|
101
|
+
bootstrapToken: request.body.bootstrap_token,
|
|
102
|
+
idempotencyKey: requireIdempotencyKey(request),
|
|
103
|
+
correlationId: request.id
|
|
104
|
+
});
|
|
105
|
+
reply.code(201);
|
|
106
|
+
return toAdminGrantDto(grant);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const listRoute = defineRoute({
|
|
110
|
+
method: 'GET',
|
|
111
|
+
path: '/',
|
|
112
|
+
description: 'List local administrator grants.',
|
|
113
|
+
schema: {
|
|
114
|
+
response: {
|
|
115
|
+
200: listAdminGrantsResponseSchema
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
errorHandler: translateAdministrationError,
|
|
119
|
+
handler: async (request)=>({
|
|
120
|
+
grants: (await listAdministratorGrants({
|
|
121
|
+
actorId: requireActorId(request)
|
|
122
|
+
})).map(toAdminGrantDto)
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
const grantRoute = defineRoute({
|
|
126
|
+
method: 'POST',
|
|
127
|
+
path: '/',
|
|
128
|
+
description: 'Grant a local administrator role to an active user.',
|
|
129
|
+
schema: {
|
|
130
|
+
body: grantAdminRoleBodySchema,
|
|
131
|
+
response: {
|
|
132
|
+
201: grantAdminRoleResponseSchema
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
errorHandler: translateAdministrationError,
|
|
136
|
+
handler: async (request, reply)=>{
|
|
137
|
+
const actorId = requireActorId(request);
|
|
138
|
+
const grant = await grantAdministratorRole({
|
|
139
|
+
actorId,
|
|
140
|
+
userId: request.body.user_id,
|
|
141
|
+
role: request.body.role,
|
|
142
|
+
reason: request.body.reason,
|
|
143
|
+
idempotencyKey: requireIdempotencyKey(request),
|
|
144
|
+
correlationId: request.id
|
|
145
|
+
});
|
|
146
|
+
reply.code(201);
|
|
147
|
+
return toAdminGrantDto(grant);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
const revokeRoute = defineRoute({
|
|
151
|
+
method: 'DELETE',
|
|
152
|
+
path: '/:grantId',
|
|
153
|
+
description: 'Revoke a local administrator grant.',
|
|
154
|
+
schema: {
|
|
155
|
+
params: z.object({
|
|
156
|
+
grantId: z.string().uuid()
|
|
157
|
+
}),
|
|
158
|
+
body: revokeAdminGrantBodySchema,
|
|
159
|
+
response: {
|
|
160
|
+
200: revokeAdminGrantResponseSchema
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
errorHandler: translateAdministrationError,
|
|
164
|
+
handler: async (request)=>{
|
|
165
|
+
const grant = await revokeAdministratorGrant({
|
|
166
|
+
actorId: requireActorId(request),
|
|
167
|
+
grantId: request.params.grantId,
|
|
168
|
+
reason: request.body.reason,
|
|
169
|
+
idempotencyKey: requireIdempotencyKey(request),
|
|
170
|
+
correlationId: request.id
|
|
171
|
+
});
|
|
172
|
+
return toAdminGrantDto(grant);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
export const administrationRoutes = {
|
|
176
|
+
prefix: '/admin/v1/auth/admin-grants',
|
|
177
|
+
auth: AUTH_USER,
|
|
178
|
+
routes: [
|
|
179
|
+
bootstrapRoute,
|
|
180
|
+
listRoute,
|
|
181
|
+
grantRoute,
|
|
182
|
+
revokeRoute
|
|
183
|
+
]
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
//# sourceMappingURL=administration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/administration.ts"],"sourcesContent":["import {AUTH_USER} from '@shipfox/api-auth-context';\nimport {\n bootstrapAdminOwnerBodySchema,\n bootstrapAdminOwnerResponseSchema,\n grantAdminRoleBodySchema,\n grantAdminRoleResponseSchema,\n listAdminGrantsResponseSchema,\n revokeAdminGrantBodySchema,\n revokeAdminGrantResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyRequest} from 'fastify';\nimport {z} from 'zod';\nimport {\n bootstrapFirstAdminOwner,\n grantAdministratorRole,\n listAdministratorGrants,\n revokeAdministratorGrant,\n} from '#core/administration.js';\nimport type {AdminGrant} from '#core/entities/admin-grant.js';\nimport {\n AdminBootstrapClosedError,\n AdminGrantAlreadyExistsError,\n AdminGrantNotFoundError,\n AdminIdempotencyKeyReuseError,\n AdminRoleRequiredError,\n InvalidAdminBootstrapTokenError,\n LastAdminOwnerError,\n UserNotFoundError,\n} from '#core/errors.js';\nimport {getClientContext} from '#presentation/auth/jwt-auth.js';\nimport {createAuthIpRateLimitPreHandler} from './rate-limit.js';\n\nconst idempotencyKeyMaxLength = 256;\n\nfunction requireActorId(request: FastifyRequest): string {\n const client = getClientContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n return client.userId;\n}\n\nfunction requireIdempotencyKey(request: FastifyRequest): string {\n const value = request.headers['idempotency-key'];\n const key = Array.isArray(value) ? value[0] : value;\n if (!key || key.trim().length === 0 || key.length > idempotencyKeyMaxLength) {\n throw new ClientError('Idempotency-Key header is required', 'idempotency-key-required', {\n status: 400,\n });\n }\n return key;\n}\n\nfunction toAdminGrantDto(grant: AdminGrant) {\n return {\n id: grant.id,\n user_id: grant.userId,\n role: grant.role,\n revoked_at: grant.revokedAt?.toISOString() ?? null,\n created_at: grant.createdAt.toISOString(),\n updated_at: grant.updatedAt.toISOString(),\n };\n}\n\nfunction translateAdministrationError(error: unknown): never {\n if (error instanceof AdminRoleRequiredError) {\n throw new ClientError('Administrator owner role required', 'forbidden', {\n status: 403,\n details: {required_role: error.minimumRole},\n });\n }\n if (error instanceof InvalidAdminBootstrapTokenError) {\n throw new ClientError('Bootstrap token is invalid', 'bootstrap-token-invalid', {\n status: 403,\n });\n }\n if (error instanceof AdminBootstrapClosedError) {\n throw new ClientError('First administrator owner already exists', 'bootstrap-closed', {\n status: 409,\n });\n }\n if (error instanceof AdminGrantAlreadyExistsError) {\n throw new ClientError('Administrator grant already exists', 'grant-already-exists', {\n status: 409,\n });\n }\n if (error instanceof AdminGrantNotFoundError) {\n throw new ClientError('Administrator grant not found', 'not-found', {status: 404});\n }\n if (error instanceof UserNotFoundError) {\n throw new ClientError('User not found', 'not-found', {status: 404});\n }\n if (error instanceof LastAdminOwnerError) {\n throw new ClientError('Cannot remove the final active administrator owner', 'last-owner', {\n status: 409,\n });\n }\n if (error instanceof AdminIdempotencyKeyReuseError) {\n throw new ClientError(\n 'Idempotency-Key was already used for a different command',\n 'idempotency-key-reused',\n {status: 409},\n );\n }\n throw error;\n}\n\nconst bootstrapRoute = defineRoute({\n method: 'POST',\n path: '/bootstrap',\n description: 'Claim the first administrator owner role with the deployment bootstrap token.',\n schema: {\n body: bootstrapAdminOwnerBodySchema,\n response: {201: bootstrapAdminOwnerResponseSchema},\n },\n preHandler: createAuthIpRateLimitPreHandler('bootstrap'),\n errorHandler: translateAdministrationError,\n handler: async (request, reply) => {\n const actorId = requireActorId(request);\n const grant = await bootstrapFirstAdminOwner({\n actorId,\n bootstrapToken: request.body.bootstrap_token,\n idempotencyKey: requireIdempotencyKey(request),\n correlationId: request.id,\n });\n reply.code(201);\n return toAdminGrantDto(grant);\n },\n});\n\nconst listRoute = defineRoute({\n method: 'GET',\n path: '/',\n description: 'List local administrator grants.',\n schema: {response: {200: listAdminGrantsResponseSchema}},\n errorHandler: translateAdministrationError,\n handler: async (request) => ({\n grants: (await listAdministratorGrants({actorId: requireActorId(request)})).map(\n toAdminGrantDto,\n ),\n }),\n});\n\nconst grantRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Grant a local administrator role to an active user.',\n schema: {\n body: grantAdminRoleBodySchema,\n response: {201: grantAdminRoleResponseSchema},\n },\n errorHandler: translateAdministrationError,\n handler: async (request, reply) => {\n const actorId = requireActorId(request);\n const grant = await grantAdministratorRole({\n actorId,\n userId: request.body.user_id,\n role: request.body.role,\n reason: request.body.reason,\n idempotencyKey: requireIdempotencyKey(request),\n correlationId: request.id,\n });\n reply.code(201);\n return toAdminGrantDto(grant);\n },\n});\n\nconst revokeRoute = defineRoute({\n method: 'DELETE',\n path: '/:grantId',\n description: 'Revoke a local administrator grant.',\n schema: {\n params: z.object({grantId: z.string().uuid()}),\n body: revokeAdminGrantBodySchema,\n response: {200: revokeAdminGrantResponseSchema},\n },\n errorHandler: translateAdministrationError,\n handler: async (request) => {\n const grant = await revokeAdministratorGrant({\n actorId: requireActorId(request),\n grantId: request.params.grantId,\n reason: request.body.reason,\n idempotencyKey: requireIdempotencyKey(request),\n correlationId: request.id,\n });\n return toAdminGrantDto(grant);\n },\n});\n\nexport const administrationRoutes: RouteGroup = {\n prefix: '/admin/v1/auth/admin-grants',\n auth: AUTH_USER,\n routes: [bootstrapRoute, listRoute, grantRoute, revokeRoute],\n};\n"],"names":["AUTH_USER","bootstrapAdminOwnerBodySchema","bootstrapAdminOwnerResponseSchema","grantAdminRoleBodySchema","grantAdminRoleResponseSchema","listAdminGrantsResponseSchema","revokeAdminGrantBodySchema","revokeAdminGrantResponseSchema","ClientError","defineRoute","z","bootstrapFirstAdminOwner","grantAdministratorRole","listAdministratorGrants","revokeAdministratorGrant","AdminBootstrapClosedError","AdminGrantAlreadyExistsError","AdminGrantNotFoundError","AdminIdempotencyKeyReuseError","AdminRoleRequiredError","InvalidAdminBootstrapTokenError","LastAdminOwnerError","UserNotFoundError","getClientContext","createAuthIpRateLimitPreHandler","idempotencyKeyMaxLength","requireActorId","request","client","status","userId","requireIdempotencyKey","value","headers","key","Array","isArray","trim","length","toAdminGrantDto","grant","id","user_id","role","revoked_at","revokedAt","toISOString","created_at","createdAt","updated_at","updatedAt","translateAdministrationError","error","details","required_role","minimumRole","bootstrapRoute","method","path","description","schema","body","response","preHandler","errorHandler","handler","reply","actorId","bootstrapToken","bootstrap_token","idempotencyKey","correlationId","code","listRoute","grants","map","grantRoute","reason","revokeRoute","params","object","grantId","string","uuid","administrationRoutes","prefix","auth","routes"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WorkspacesInterModuleClient } from '@shipfox/api-workspaces-dto/inter-module';
|
|
2
2
|
import type { RouteGroup } from '@shipfox/node-fastify';
|
|
3
|
-
|
|
3
|
+
import type { SignupPolicy } from '#core/ports.js';
|
|
4
|
+
export declare function buildAuthRoutes(passwordEnabled: boolean, workspaces: WorkspacesInterModuleClient, signupPolicy?: SignupPolicy): RouteGroup;
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,0CAA0C,CAAC;AAC1F,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,0CAA0C,CAAC;AAC1F,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAajD,wBAAgB,eAAe,CAC7B,eAAe,EAAE,OAAO,EACxB,UAAU,EAAE,2BAA2B,EACvC,YAAY,CAAC,EAAE,YAAY,GAC1B,UAAU,CA2BZ"}
|
|
@@ -9,9 +9,9 @@ import { createLoginRoute } from './session/login.js';
|
|
|
9
9
|
import { logoutRoute } from './session/logout.js';
|
|
10
10
|
import { meRoute } from './session/me.js';
|
|
11
11
|
import { createRefreshRoute } from './session/refresh.js';
|
|
12
|
-
export function buildAuthRoutes(passwordEnabled, workspaces) {
|
|
12
|
+
export function buildAuthRoutes(passwordEnabled, workspaces, signupPolicy) {
|
|
13
13
|
const passwordRoutes = passwordEnabled ? [
|
|
14
|
-
createSignupRoute(workspaces),
|
|
14
|
+
createSignupRoute(workspaces, signupPolicy),
|
|
15
15
|
createVerifyEmailConfirmRoute(workspaces),
|
|
16
16
|
verifyEmailResendRoute,
|
|
17
17
|
createLoginRoute(workspaces)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {authCookiePlugin} from '#presentation/auth/refresh-cookie.js';\nimport {createVerifyEmailConfirmRoute} from './email-verification/verify-email-confirm.js';\nimport {verifyEmailResendRoute} from './email-verification/verify-email-resend.js';\nimport {changePasswordRoute} from './password/change-password.js';\nimport {createPasswordResetConfirmRoute} from './password/password-reset-confirm.js';\nimport {passwordResetRequestRoute} from './password/password-reset-request.js';\nimport {createSignupRoute} from './registration/signup.js';\nimport {createLoginRoute} from './session/login.js';\nimport {logoutRoute} from './session/logout.js';\nimport {meRoute} from './session/me.js';\nimport {createRefreshRoute} from './session/refresh.js';\n\nexport function buildAuthRoutes(\n passwordEnabled: boolean,\n workspaces: WorkspacesInterModuleClient,\n): RouteGroup {\n const passwordRoutes = passwordEnabled\n ? [\n createSignupRoute(workspaces),\n createVerifyEmailConfirmRoute(workspaces),\n verifyEmailResendRoute,\n createLoginRoute(workspaces),\n ]\n : [];\n\n return {\n prefix: '/auth',\n plugins: [authCookiePlugin],\n routes: [\n ...passwordRoutes,\n createRefreshRoute(workspaces),\n logoutRoute,\n meRoute,\n ...(passwordEnabled\n ? [\n changePasswordRoute,\n passwordResetRequestRoute,\n createPasswordResetConfirmRoute(workspaces),\n ]\n : []),\n ],\n };\n}\n"],"names":["authCookiePlugin","createVerifyEmailConfirmRoute","verifyEmailResendRoute","changePasswordRoute","createPasswordResetConfirmRoute","passwordResetRequestRoute","createSignupRoute","createLoginRoute","logoutRoute","meRoute","createRefreshRoute","buildAuthRoutes","passwordEnabled","workspaces","passwordRoutes","prefix","plugins","routes"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport type {SignupPolicy} from '#core/ports.js';\nimport {authCookiePlugin} from '#presentation/auth/refresh-cookie.js';\nimport {createVerifyEmailConfirmRoute} from './email-verification/verify-email-confirm.js';\nimport {verifyEmailResendRoute} from './email-verification/verify-email-resend.js';\nimport {changePasswordRoute} from './password/change-password.js';\nimport {createPasswordResetConfirmRoute} from './password/password-reset-confirm.js';\nimport {passwordResetRequestRoute} from './password/password-reset-request.js';\nimport {createSignupRoute} from './registration/signup.js';\nimport {createLoginRoute} from './session/login.js';\nimport {logoutRoute} from './session/logout.js';\nimport {meRoute} from './session/me.js';\nimport {createRefreshRoute} from './session/refresh.js';\n\nexport function buildAuthRoutes(\n passwordEnabled: boolean,\n workspaces: WorkspacesInterModuleClient,\n signupPolicy?: SignupPolicy,\n): RouteGroup {\n const passwordRoutes = passwordEnabled\n ? [\n createSignupRoute(workspaces, signupPolicy),\n createVerifyEmailConfirmRoute(workspaces),\n verifyEmailResendRoute,\n createLoginRoute(workspaces),\n ]\n : [];\n\n return {\n prefix: '/auth',\n plugins: [authCookiePlugin],\n routes: [\n ...passwordRoutes,\n createRefreshRoute(workspaces),\n logoutRoute,\n meRoute,\n ...(passwordEnabled\n ? [\n changePasswordRoute,\n passwordResetRequestRoute,\n createPasswordResetConfirmRoute(workspaces),\n ]\n : []),\n ],\n };\n}\n"],"names":["authCookiePlugin","createVerifyEmailConfirmRoute","verifyEmailResendRoute","changePasswordRoute","createPasswordResetConfirmRoute","passwordResetRequestRoute","createSignupRoute","createLoginRoute","logoutRoute","meRoute","createRefreshRoute","buildAuthRoutes","passwordEnabled","workspaces","signupPolicy","passwordRoutes","prefix","plugins","routes"],"mappings":"AAGA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,6BAA6B,QAAO,+CAA+C;AAC3F,SAAQC,sBAAsB,QAAO,8CAA8C;AACnF,SAAQC,mBAAmB,QAAO,gCAAgC;AAClE,SAAQC,+BAA+B,QAAO,uCAAuC;AACrF,SAAQC,yBAAyB,QAAO,uCAAuC;AAC/E,SAAQC,iBAAiB,QAAO,2BAA2B;AAC3D,SAAQC,gBAAgB,QAAO,qBAAqB;AACpD,SAAQC,WAAW,QAAO,sBAAsB;AAChD,SAAQC,OAAO,QAAO,kBAAkB;AACxC,SAAQC,kBAAkB,QAAO,uBAAuB;AAExD,OAAO,SAASC,gBACdC,eAAwB,EACxBC,UAAuC,EACvCC,YAA2B;IAE3B,MAAMC,iBAAiBH,kBACnB;QACEN,kBAAkBO,YAAYC;QAC9Bb,8BAA8BY;QAC9BX;QACAK,iBAAiBM;KAClB,GACD,EAAE;IAEN,OAAO;QACLG,QAAQ;QACRC,SAAS;YAACjB;SAAiB;QAC3BkB,QAAQ;eACHH;YACHL,mBAAmBG;YACnBL;YACAC;eACIG,kBACA;gBACET;gBACAE;gBACAD,gCAAgCS;aACjC,GACD,EAAE;SACP;IACH;AACF"}
|
|
@@ -6,5 +6,6 @@ interface EmailBody {
|
|
|
6
6
|
export declare function createAuthRateLimitPreHandler(action: AuthRateLimitAction): (request: FastifyRequest<{
|
|
7
7
|
Body: EmailBody;
|
|
8
8
|
}>, reply: FastifyReply) => Promise<void>;
|
|
9
|
+
export declare function createAuthIpRateLimitPreHandler(action: AuthRateLimitAction): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
9
10
|
export {};
|
|
10
11
|
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAmB7B,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAgFD,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;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,mBAAmB,IAC3D,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAS3E"}
|
|
@@ -20,18 +20,26 @@ const policies = {
|
|
|
20
20
|
limit: 3,
|
|
21
21
|
windowSeconds: 60 * 60
|
|
22
22
|
}
|
|
23
|
+
},
|
|
24
|
+
bootstrap: {
|
|
25
|
+
ip: {
|
|
26
|
+
limit: 5,
|
|
27
|
+
windowSeconds: 15 * 60
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
};
|
|
25
31
|
function routeName(request) {
|
|
26
32
|
return request.routeOptions.url ?? request.url.split('?')[0] ?? 'unknown';
|
|
27
33
|
}
|
|
28
34
|
async function enforceRateLimit(params) {
|
|
35
|
+
const policy = policies[params.action][params.scope];
|
|
36
|
+
if (!policy) return;
|
|
29
37
|
try {
|
|
30
38
|
await checkAuthRateLimit({
|
|
31
39
|
action: params.action,
|
|
32
40
|
scope: params.scope,
|
|
33
41
|
identifier: params.identifier,
|
|
34
|
-
...
|
|
42
|
+
...policy
|
|
35
43
|
});
|
|
36
44
|
} catch (error) {
|
|
37
45
|
if (error instanceof AuthRateLimitExceededError) {
|
|
@@ -97,5 +105,16 @@ export function createAuthRateLimitPreHandler(action) {
|
|
|
97
105
|
});
|
|
98
106
|
};
|
|
99
107
|
}
|
|
108
|
+
export function createAuthIpRateLimitPreHandler(action) {
|
|
109
|
+
return async (request, reply)=>{
|
|
110
|
+
await enforceRateLimit({
|
|
111
|
+
request,
|
|
112
|
+
reply,
|
|
113
|
+
action,
|
|
114
|
+
scope: 'ip',
|
|
115
|
+
identifier: request.ip
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
}
|
|
100
119
|
|
|
101
120
|
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -1 +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<
|
|
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<\n AuthRateLimitAction,\n Partial<Record<AuthRateLimitScope, AuthRateLimitPolicy>>\n> = {\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 bootstrap: {\n ip: {limit: 5, windowSeconds: 15 * 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 const policy = policies[params.action][params.scope];\n if (!policy) return;\n\n try {\n await checkAuthRateLimit({\n action: params.action,\n scope: params.scope,\n identifier: params.identifier,\n ...policy,\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\nexport function createAuthIpRateLimitPreHandler(action: AuthRateLimitAction) {\n return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n await enforceRateLimit({\n request,\n reply,\n action,\n scope: 'ip',\n identifier: request.ip,\n });\n };\n}\n"],"names":["ClientError","AuthRateLimitExceededError","AuthRateLimitUnavailableError","checkAuthRateLimit","policies","login","ip","limit","windowSeconds","email","bootstrap","routeName","request","routeOptions","url","split","enforceRateLimit","params","policy","action","scope","identifier","error","log","warn","route","retryAfterSeconds","identifierHmacPrefix","reply","header","String","status","details","retry_after_seconds","data","cause","err","createAuthRateLimitPreHandler","body","createAuthIpRateLimitPreHandler"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { WorkspacesInterModuleClient } from '@shipfox/api-workspaces-dto/inter-module';
|
|
2
|
-
|
|
2
|
+
import type { SignupPolicy } from '#core/ports.js';
|
|
3
|
+
export declare function createSignupRoute(workspaces: WorkspacesInterModuleClient, signupPolicy?: SignupPolicy): import("@shipfox/node-fastify").RouteDefinition;
|
|
3
4
|
//# sourceMappingURL=signup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/registration/signup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"signup.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/registration/signup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,0CAA0C,CAAC;AAW1F,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAMjD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,2BAA2B,EACvC,YAAY,CAAC,EAAE,YAAY,mDAkG5B"}
|
|
@@ -2,10 +2,11 @@ import { signupBodySchema, signupResponseSchema } from '@shipfox/api-auth-dto';
|
|
|
2
2
|
import { EmailChallengeError } from '@shipfox/api-email-challenges';
|
|
3
3
|
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
4
|
import { signup, signupWithInvitation } from '#core/auth.js';
|
|
5
|
-
import { EmailTakenError, InvitationEmailMismatchError, TokenAlreadyUsedError, TokenExpiredError, TokenInvalidError } from '#core/errors.js';
|
|
5
|
+
import { EmailTakenError, InvitationEmailMismatchError, SignupNotAllowedError, TokenAlreadyUsedError, TokenExpiredError, TokenInvalidError } from '#core/errors.js';
|
|
6
6
|
import { setRefreshTokenCookie } from '#presentation/auth/refresh-cookie.js';
|
|
7
7
|
import { toUserDto } from '#presentation/dto/user.js';
|
|
8
|
-
|
|
8
|
+
const SIGNUP_NOT_ALLOWED_MESSAGE_MAX_LENGTH = 500;
|
|
9
|
+
export function createSignupRoute(workspaces, signupPolicy) {
|
|
9
10
|
return defineRoute({
|
|
10
11
|
method: 'POST',
|
|
11
12
|
path: '/signup',
|
|
@@ -32,6 +33,15 @@ export function createSignupRoute(workspaces) {
|
|
|
32
33
|
status: 409
|
|
33
34
|
});
|
|
34
35
|
}
|
|
36
|
+
if (error instanceof SignupNotAllowedError) {
|
|
37
|
+
const message = error.message.slice(0, SIGNUP_NOT_ALLOWED_MESSAGE_MAX_LENGTH);
|
|
38
|
+
throw new ClientError(message, 'signup-not-allowed', {
|
|
39
|
+
status: 403,
|
|
40
|
+
details: {
|
|
41
|
+
message
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
35
45
|
if (error instanceof TokenInvalidError) {
|
|
36
46
|
throw new ClientError('Invitation token is invalid', 'invitation-token-invalid', {
|
|
37
47
|
status: 410
|
|
@@ -61,7 +71,8 @@ export function createSignupRoute(workspaces) {
|
|
|
61
71
|
email,
|
|
62
72
|
password,
|
|
63
73
|
name,
|
|
64
|
-
sourceIp: request.ip
|
|
74
|
+
sourceIp: request.ip,
|
|
75
|
+
signupPolicy
|
|
65
76
|
});
|
|
66
77
|
reply.code(201);
|
|
67
78
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/presentation/routes/registration/signup.ts"],"sourcesContent":["import {signupBodySchema, signupResponseSchema} from '@shipfox/api-auth-dto';\nimport {EmailChallengeError} from '@shipfox/api-email-challenges';\nimport type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {signup, signupWithInvitation} from '#core/auth.js';\nimport {\n EmailTakenError,\n InvitationEmailMismatchError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n} from '#core/errors.js';\nimport {setRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\nimport {toUserDto} from '#presentation/dto/user.js';\n\nexport function createSignupRoute(workspaces: WorkspacesInterModuleClient) {\n return 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 EmailChallengeError) {\n throw new ClientError(error.message, `email-challenge-${error.code}`, {\n status: error.code === 'limited' ? 429 : 400,\n ...(error.retryAt ? {details: {retry_at: error.retryAt.toISOString()}} : {}),\n });\n }\n if (error instanceof EmailTakenError) {\n throw new ClientError('Email already registered', 'email-taken', {status: 409});\n }\n if (error instanceof TokenInvalidError) {\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', {\n status: 410,\n });\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 result = await signup({email
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/registration/signup.ts"],"sourcesContent":["import {signupBodySchema, signupResponseSchema} from '@shipfox/api-auth-dto';\nimport {EmailChallengeError} from '@shipfox/api-email-challenges';\nimport type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {signup, signupWithInvitation} from '#core/auth.js';\nimport {\n EmailTakenError,\n InvitationEmailMismatchError,\n SignupNotAllowedError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n} from '#core/errors.js';\nimport type {SignupPolicy} from '#core/ports.js';\nimport {setRefreshTokenCookie} from '#presentation/auth/refresh-cookie.js';\nimport {toUserDto} from '#presentation/dto/user.js';\n\nconst SIGNUP_NOT_ALLOWED_MESSAGE_MAX_LENGTH = 500;\n\nexport function createSignupRoute(\n workspaces: WorkspacesInterModuleClient,\n signupPolicy?: SignupPolicy,\n) {\n return 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 EmailChallengeError) {\n throw new ClientError(error.message, `email-challenge-${error.code}`, {\n status: error.code === 'limited' ? 429 : 400,\n ...(error.retryAt ? {details: {retry_at: error.retryAt.toISOString()}} : {}),\n });\n }\n if (error instanceof EmailTakenError) {\n throw new ClientError('Email already registered', 'email-taken', {status: 409});\n }\n if (error instanceof SignupNotAllowedError) {\n const message = error.message.slice(0, SIGNUP_NOT_ALLOWED_MESSAGE_MAX_LENGTH);\n throw new ClientError(message, 'signup-not-allowed', {\n status: 403,\n details: {message},\n });\n }\n if (error instanceof TokenInvalidError) {\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', {\n status: 410,\n });\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 result = await signup({\n email,\n password,\n name,\n sourceIp: request.ip,\n signupPolicy,\n });\n reply.code(201);\n return {\n user: toUserDto(result),\n email_challenge: {\n id: result.emailChallenge.id,\n next_resend_available_at: result.emailChallenge.nextResendAvailableAt.toISOString(),\n },\n };\n }\n\n const result = await signupWithInvitation({\n email,\n password,\n name,\n invitationToken: invitation_token,\n workspaces,\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}\n"],"names":["signupBodySchema","signupResponseSchema","EmailChallengeError","ClientError","defineRoute","signup","signupWithInvitation","EmailTakenError","InvitationEmailMismatchError","SignupNotAllowedError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","setRefreshTokenCookie","toUserDto","SIGNUP_NOT_ALLOWED_MESSAGE_MAX_LENGTH","createSignupRoute","workspaces","signupPolicy","method","path","description","schema","body","response","errorHandler","error","message","code","status","retryAt","details","retry_at","toISOString","slice","handler","request","reply","email","password","name","invitation_token","undefined","result","sourceIp","ip","user","email_challenge","id","emailChallenge","next_resend_available_at","nextResendAvailableAt","invitationToken","refreshToken","token","membership","user_id","userId","workspace_id","workspaceId","acceptError","accept_error"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/session/me.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/session/me.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,OAAO,iDA6BlB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AUTH_USER } from '@shipfox/api-auth-context';
|
|
2
2
|
import { meResponseSchema } from '@shipfox/api-auth-dto';
|
|
3
3
|
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { getCurrentAdminRole } from '#core/admin-role.js';
|
|
4
5
|
import { getCurrentUser } from '#core/auth.js';
|
|
5
6
|
import { UserNotFoundError } from '#core/errors.js';
|
|
6
7
|
import { getClientContext } from '#presentation/auth/jwt-auth.js';
|
|
@@ -34,7 +35,10 @@ export const meRoute = defineRoute({
|
|
|
34
35
|
userId: client.userId
|
|
35
36
|
});
|
|
36
37
|
return {
|
|
37
|
-
user: toUserDto(result.user)
|
|
38
|
+
user: toUserDto(result.user),
|
|
39
|
+
admin_role: await getCurrentAdminRole({
|
|
40
|
+
userId: client.userId
|
|
41
|
+
})
|
|
38
42
|
};
|
|
39
43
|
}
|
|
40
44
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/presentation/routes/session/me.ts"],"sourcesContent":["import {AUTH_USER} from '@shipfox/api-auth-context';\nimport {meResponseSchema} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {getCurrentUser} from '#core/auth.js';\nimport {UserNotFoundError} from '#core/errors.js';\nimport {getClientContext} from '#presentation/auth/jwt-auth.js';\nimport {toUserDto} from '#presentation/dto/user.js';\n\nexport const meRoute = defineRoute({\n method: 'GET',\n path: '/me',\n description: 'Get the signed-in user.',\n auth: AUTH_USER,\n schema: {\n response: {\n 200: meResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof UserNotFoundError) {\n throw new ClientError('User not found', 'not-found', {status: 404});\n }\n throw error;\n },\n handler: async (request) => {\n const client = getClientContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const result = await getCurrentUser({userId: client.userId});\n\n return {user: toUserDto(result.user)};\n },\n});\n"],"names":["AUTH_USER","meResponseSchema","ClientError","defineRoute","getCurrentUser","UserNotFoundError","getClientContext","toUserDto","meRoute","method","path","description","auth","schema","response","errorHandler","error","status","handler","request","client","result","userId","user"],"mappings":"AAAA,SAAQA,SAAS,QAAO,4BAA4B;AACpD,SAAQC,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,cAAc,QAAO,gBAAgB;AAC7C,SAAQC,iBAAiB,QAAO,kBAAkB;AAClD,SAAQC,gBAAgB,QAAO,iCAAiC;AAChE,SAAQC,SAAS,QAAO,4BAA4B;AAEpD,OAAO,MAAMC,
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/session/me.ts"],"sourcesContent":["import {AUTH_USER} from '@shipfox/api-auth-context';\nimport {meResponseSchema} from '@shipfox/api-auth-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {getCurrentAdminRole} from '#core/admin-role.js';\nimport {getCurrentUser} from '#core/auth.js';\nimport {UserNotFoundError} from '#core/errors.js';\nimport {getClientContext} from '#presentation/auth/jwt-auth.js';\nimport {toUserDto} from '#presentation/dto/user.js';\n\nexport const meRoute = defineRoute({\n method: 'GET',\n path: '/me',\n description: 'Get the signed-in user.',\n auth: AUTH_USER,\n schema: {\n response: {\n 200: meResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof UserNotFoundError) {\n throw new ClientError('User not found', 'not-found', {status: 404});\n }\n throw error;\n },\n handler: async (request) => {\n const client = getClientContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const result = await getCurrentUser({userId: client.userId});\n\n return {\n user: toUserDto(result.user),\n admin_role: await getCurrentAdminRole({userId: client.userId}),\n };\n },\n});\n"],"names":["AUTH_USER","meResponseSchema","ClientError","defineRoute","getCurrentAdminRole","getCurrentUser","UserNotFoundError","getClientContext","toUserDto","meRoute","method","path","description","auth","schema","response","errorHandler","error","status","handler","request","client","result","userId","user","admin_role"],"mappings":"AAAA,SAAQA,SAAS,QAAO,4BAA4B;AACpD,SAAQC,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,mBAAmB,QAAO,sBAAsB;AACxD,SAAQC,cAAc,QAAO,gBAAgB;AAC7C,SAAQC,iBAAiB,QAAO,kBAAkB;AAClD,SAAQC,gBAAgB,QAAO,iCAAiC;AAChE,SAAQC,SAAS,QAAO,4BAA4B;AAEpD,OAAO,MAAMC,UAAUN,YAAY;IACjCO,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMb;IACNc,QAAQ;QACNC,UAAU;YACR,KAAKd;QACP;IACF;IACAe,cAAc,CAACC;QACb,IAAIA,iBAAiBX,mBAAmB;YACtC,MAAM,IAAIJ,YAAY,kBAAkB,aAAa;gBAACgB,QAAQ;YAAG;QACnE;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC;QACd,MAAMC,SAASd,iBAAiBa;QAChC,IAAI,CAACC,QAAQ;YACX,MAAM,IAAInB,YAAY,2BAA2B,gBAAgB;gBAACgB,QAAQ;YAAG;QAC/E;QAEA,MAAMI,SAAS,MAAMjB,eAAe;YAACkB,QAAQF,OAAOE,MAAM;QAAA;QAE1D,OAAO;YACLC,MAAMhB,UAAUc,OAAOE,IAAI;YAC3BC,YAAY,MAAMrB,oBAAoB;gBAACmB,QAAQF,OAAOE,MAAM;YAAA;QAC9D;IACF;AACF,GAAG"}
|