@zintrust/core 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/auth/LoginFlow.d.ts +7 -1
  3. package/src/auth/LoginFlow.d.ts.map +1 -1
  4. package/src/auth/LoginFlow.js +98 -2
  5. package/src/cli/OptionalCliExtensions.d.ts +1 -0
  6. package/src/cli/OptionalCliExtensions.d.ts.map +1 -1
  7. package/src/cli/OptionalCliExtensions.js +24 -2
  8. package/src/cli/commands/MySqlProxyCommand.d.ts.map +1 -1
  9. package/src/cli/commands/MySqlProxyCommand.js +1 -1
  10. package/src/cli/commands/RoutesCommand.d.ts.map +1 -1
  11. package/src/cli/commands/RoutesCommand.js +39 -2
  12. package/src/cli/commands/ScheduleStartCommand.d.ts.map +1 -1
  13. package/src/cli/commands/ScheduleStartCommand.js +14 -9
  14. package/src/cli/commands/schedule/ScheduleCliSupport.d.ts.map +1 -1
  15. package/src/cli/commands/schedule/ScheduleCliSupport.js +29 -4
  16. package/src/config/env.d.ts +2 -0
  17. package/src/config/env.d.ts.map +1 -1
  18. package/src/config/env.js +2 -0
  19. package/src/http/Request.d.ts +1 -0
  20. package/src/http/Request.d.ts.map +1 -1
  21. package/src/http/Request.js +3 -0
  22. package/src/index.d.ts +8 -4
  23. package/src/index.d.ts.map +1 -1
  24. package/src/index.js +7 -4
  25. package/src/middleware/BulletproofAuthMiddleware.d.ts +2 -1
  26. package/src/middleware/BulletproofAuthMiddleware.d.ts.map +1 -1
  27. package/src/middleware/BulletproofAuthMiddleware.js +106 -36
  28. package/src/runtime/useFileLoader.d.ts +5 -0
  29. package/src/runtime/useFileLoader.d.ts.map +1 -1
  30. package/src/runtime/useFileLoader.js +58 -37
  31. package/src/security/BulletproofDeviceStore.d.ts +18 -0
  32. package/src/security/BulletproofDeviceStore.d.ts.map +1 -0
  33. package/src/security/BulletproofDeviceStore.js +243 -0
  34. package/src/security/JwtVerifier.d.ts +75 -0
  35. package/src/security/JwtVerifier.d.ts.map +1 -0
  36. package/src/security/JwtVerifier.js +336 -0
  37. package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +24 -10
  38. package/src/templates/project/basic/config/trace.ts.tpl +73 -0
  39. package/src/templates/project/basic/database/migrations/20260419000000_create_bulletproof_devices_table.ts.tpl +36 -0
@@ -29,6 +29,7 @@ type PasswordLoginContext = {
29
29
  email: string;
30
30
  ipAddress: string;
31
31
  requestId?: string;
32
+ request: IRequest;
32
33
  };
33
34
 
34
35
  const toSubject = (id: unknown): string | undefined => {
@@ -37,10 +38,6 @@ const toSubject = (id: unknown): string | undefined => {
37
38
  return undefined;
38
39
  };
39
40
 
40
- const toDeviceId = (subject: string | undefined): string | undefined => {
41
- return isUndefinedOrNull(subject) ? undefined : `dev-${subject}`;
42
- };
43
-
44
41
  const getClaimString = (claims: unknown, key: string): string | undefined => {
45
42
  if (typeof claims !== 'object' || claims === null) {
46
43
  return undefined;
@@ -55,7 +52,23 @@ const getIssuedToken = (issued: unknown): string => {
55
52
  return issued;
56
53
  }
57
54
 
58
- throw ErrorFactory.createSecurityError('LoginFlow jwt issuer returned an invalid access token');
55
+ if (typeof issued === 'object' && issued !== null) {
56
+ const token = (issued as Record<string, unknown>)['token'];
57
+ if (typeof token === 'string' && token.trim() !== '') {
58
+ return token;
59
+ }
60
+ }
61
+
62
+ throw ErrorFactory.createSecurityError('LoginFlow issuer returned an invalid access token');
63
+ };
64
+
65
+ const getIssuedString = (issued: unknown, key: string): string | undefined => {
66
+ if (typeof issued !== 'object' || issued === null) {
67
+ return undefined;
68
+ }
69
+
70
+ const value = (issued as Record<string, unknown>)[key];
71
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
59
72
  };
60
73
 
61
74
  const isLoginFlowUnauthorizedFailure = (error: unknown): boolean => {
@@ -120,7 +133,6 @@ const passwordLoginProvider = Object.freeze({
120
133
 
121
134
  const user = pickPublicUser(identity);
122
135
  const subject = toSubject(user.id);
123
- const deviceId = toDeviceId(subject);
124
136
 
125
137
  return {
126
138
  user,
@@ -128,7 +140,6 @@ const passwordLoginProvider = Object.freeze({
128
140
  claims: {
129
141
  sub: subject,
130
142
  email: user.email,
131
- ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
132
143
  },
133
144
  };
134
145
  },
@@ -156,17 +167,19 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
156
167
  try {
157
168
  const result = await LoginFlow.create({
158
169
  provider: passwordLoginProvider,
159
- context: Object.freeze({ email, ipAddress, requestId }),
170
+ context: Object.freeze({ email, ipAddress, requestId, request: req }),
160
171
  })
161
172
  .identify({ email })
162
173
  .verify({ password })
163
- .issue('jwt')
174
+ .issue('bulletproof')
164
175
  .audit()
165
176
  .run();
166
177
 
167
178
  const user = result.verified.user as { id: unknown; name: string; email: string };
168
179
  const subject = getClaimString(result.verified.claims, 'sub');
169
- const deviceId = getClaimString(result.verified.claims, 'deviceId');
180
+ const deviceId =
181
+ getIssuedString(result.issued, 'deviceId') ?? getClaimString(result.verified.claims, 'deviceId');
182
+ const deviceSecret = getIssuedString(result.issued, 'deviceSecret');
170
183
  const token = getIssuedToken(result.issued);
171
184
 
172
185
  Logger.info('AuthController.login: successful login', {
@@ -181,6 +194,7 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
181
194
  token,
182
195
  token_type: 'Bearer',
183
196
  ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
197
+ ...(isUndefinedOrNull(deviceSecret) ? {} : { deviceSecret }),
184
198
  user,
185
199
  });
186
200
  } catch (error) {
@@ -0,0 +1,73 @@
1
+ import { Env } from '@zintrust/core';
2
+ import type { TraceConfigOverrides } from '@zintrust/trace';
3
+
4
+ /**
5
+ * SystemTrace Configuration
6
+ *
7
+ * Keep this file declarative:
8
+ * - Package owns defaults and type validation.
9
+ * - Edit values below to override for this project.
10
+ *
11
+ * Usage: import '@zintrust/trace/register' in your bootstrap.
12
+ * Protect /trace with your own middleware (auth, admin role, etc.).
13
+ */
14
+
15
+ export default {
16
+ enabled: Env.getBool('TRACE_ENABLED', false),
17
+
18
+ // Optional: use a separate DB connection for trace tables.
19
+ // Leave undefined to fall back to the app's default connection.
20
+ connection: Env.get('TRACE_DB_CONNECTION', '') || undefined,
21
+
22
+ pruneAfterHours: Env.getInt('TRACE_PRUNE_HOURS', 72),
23
+
24
+ ignoreRoutes: ['/trace', '/health', '/ping', '/metrics', '/api-docs', '/api-docs-json'],
25
+
26
+ ignorePaths: [
27
+ '/telemetry',
28
+ '/favicon.ico',
29
+ '/robots.txt',
30
+ '/sitemap.xml',
31
+ '/workers',
32
+ '/queue-monitor',
33
+ '.js',
34
+ '.css',
35
+ ],
36
+
37
+ slowQueryThreshold: Env.getInt('TRACE_SLOW_QUERY_MS', 100),
38
+
39
+ logMinLevel: Env.get('TRACE_LOG_LEVEL', 'warn') as 'debug' | 'info' | 'warn' | 'error' | 'fatal',
40
+
41
+ watchers: {
42
+ // Set a watcher to false to disable it entirely.
43
+ // All watchers are enabled by default when trace is enabled.
44
+ // Include/exclude filters are contains-based and can be applied per watcher.
45
+ // request: {
46
+ // get: { exclude: ['report','workers/events'] },
47
+ // post: { include: ['auth'] },
48
+ // patch: { include: ['profile'] },
49
+ // delete: { exclude: ['internal'] },
50
+ // },
51
+ // log: { exclude: ['healthcheck'] },
52
+ // exception: { include: ['trace'] },
53
+ // clientRequest: {
54
+ // exclude: ['internal-http'],
55
+ // sources: {
56
+ // termii: { enabled: false },
57
+ // sendgrid: { responseBody: false },
58
+ // s3: { requestHeaders: false, responseHeaders: false },
59
+ // },
60
+ // },
61
+ // cache: { include: ['session:'] },
62
+ // dump: false, // DumpWatcher is opt-in — enable explicitly if needed
63
+ },
64
+
65
+ redaction: {
66
+ // Extra keys to mask recursively before trace entries are persisted.
67
+ // You can also provide these via TRACE_REDACT_KEYS as JSON or CSV.
68
+ keys: ['password', 'token', 'secret', 'authorization', 'card', 'cardNumber', 'cvv'],
69
+ headers: ['authorization', 'cookie', 'x-api-key', 'x-auth-token'],
70
+ body: ['password', 'token', 'secret', 'apiKey', 'api_key', 'jwt', 'bearer'],
71
+ query: [],
72
+ },
73
+ } satisfies TraceConfigOverrides;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Migration: CreateBulletproofDevicesTable
3
+ * Creates the core-backed device store used by Bulletproof authentication.
4
+ */
5
+ import type { Blueprint, IDatabase } from '@zintrust/core';
6
+ import { MigrationSchema } from '@zintrust/core';
7
+
8
+ export interface Migration {
9
+ up(db: IDatabase): Promise<void>;
10
+ down(db: IDatabase): Promise<void>;
11
+ }
12
+
13
+ export const migration: Migration = {
14
+ async up(db: IDatabase): Promise<void> {
15
+ const schema = MigrationSchema.create(db);
16
+
17
+ await schema.create('zintrust_bulletproof_devices', (table: Blueprint) => {
18
+ table.id();
19
+ table.string('user_id', 191).nullable();
20
+ table.string('device_id', 191).unique();
21
+ table.text('signing_secret');
22
+ table.text('user_agent').nullable();
23
+ table.timestamp('last_seen_at').notNullable().default('CURRENT_TIMESTAMP');
24
+ table.timestamp('created_at').notNullable().default('CURRENT_TIMESTAMP');
25
+ table.timestamp('updated_at').notNullable().default('CURRENT_TIMESTAMP');
26
+
27
+ table.index(['user_id']);
28
+ table.index(['last_seen_at']);
29
+ });
30
+ },
31
+
32
+ async down(db: IDatabase): Promise<void> {
33
+ const schema = MigrationSchema.create(db);
34
+ await schema.dropIfExists('zintrust_bulletproof_devices');
35
+ },
36
+ };