@spfn/auth 0.1.0-alpha.1 → 0.1.0-alpha.86

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 (124) hide show
  1. package/README.md +250 -0
  2. package/dist/adapters/nextjs/api.d.ts +446 -0
  3. package/dist/adapters/nextjs/api.js +3279 -0
  4. package/dist/adapters/nextjs/api.js.map +1 -0
  5. package/dist/adapters/nextjs/server.d.ts +246 -0
  6. package/dist/adapters/nextjs/server.js +3645 -0
  7. package/dist/adapters/nextjs/server.js.map +1 -0
  8. package/dist/index.d.ts +3 -46
  9. package/dist/index.js +777 -645
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/api/auth-codes-verify.d.ts +37 -0
  12. package/dist/lib/api/auth-codes-verify.js +2949 -0
  13. package/dist/lib/api/auth-codes-verify.js.map +1 -0
  14. package/dist/lib/api/auth-codes.d.ts +37 -0
  15. package/dist/lib/api/auth-codes.js +2949 -0
  16. package/dist/lib/api/auth-codes.js.map +1 -0
  17. package/dist/lib/api/auth-exists.d.ts +38 -0
  18. package/dist/lib/api/auth-exists.js +2949 -0
  19. package/dist/lib/api/auth-exists.js.map +1 -0
  20. package/dist/lib/api/auth-invitations-accept.d.ts +38 -0
  21. package/dist/lib/api/auth-invitations-accept.js +2883 -0
  22. package/dist/lib/api/auth-invitations-accept.js.map +1 -0
  23. package/dist/lib/api/auth-invitations-cancel.d.ts +37 -0
  24. package/dist/lib/api/auth-invitations-cancel.js +2883 -0
  25. package/dist/lib/api/auth-invitations-cancel.js.map +1 -0
  26. package/dist/lib/api/auth-invitations-delete.d.ts +36 -0
  27. package/dist/lib/api/auth-invitations-delete.js +2883 -0
  28. package/dist/lib/api/auth-invitations-delete.js.map +1 -0
  29. package/dist/lib/api/auth-invitations-resend.d.ts +37 -0
  30. package/dist/lib/api/auth-invitations-resend.js +2883 -0
  31. package/dist/lib/api/auth-invitations-resend.js.map +1 -0
  32. package/dist/lib/api/auth-invitations.d.ts +109 -0
  33. package/dist/lib/api/auth-invitations.js +2887 -0
  34. package/dist/lib/api/auth-invitations.js.map +1 -0
  35. package/dist/lib/api/auth-keys-rotate.d.ts +37 -0
  36. package/dist/lib/api/auth-keys-rotate.js +2949 -0
  37. package/dist/lib/api/auth-keys-rotate.js.map +1 -0
  38. package/dist/lib/api/auth-login.d.ts +39 -0
  39. package/dist/lib/api/auth-login.js +2949 -0
  40. package/dist/lib/api/auth-login.js.map +1 -0
  41. package/dist/lib/api/auth-logout.d.ts +36 -0
  42. package/dist/lib/api/auth-logout.js +2949 -0
  43. package/dist/lib/api/auth-logout.js.map +1 -0
  44. package/dist/lib/api/auth-me.d.ts +50 -0
  45. package/dist/lib/api/auth-me.js +2949 -0
  46. package/dist/lib/api/auth-me.js.map +1 -0
  47. package/dist/lib/api/auth-password.d.ts +36 -0
  48. package/dist/lib/api/auth-password.js +2949 -0
  49. package/dist/lib/api/auth-password.js.map +1 -0
  50. package/dist/lib/api/auth-register.d.ts +38 -0
  51. package/dist/lib/api/auth-register.js +2949 -0
  52. package/dist/lib/api/auth-register.js.map +1 -0
  53. package/dist/lib/api/index.d.ts +356 -0
  54. package/dist/lib/api/index.js +3261 -0
  55. package/dist/lib/api/index.js.map +1 -0
  56. package/dist/lib/config.d.ts +70 -0
  57. package/dist/lib/config.js +64 -0
  58. package/dist/lib/config.js.map +1 -0
  59. package/dist/lib/contracts/auth.d.ts +41 -1
  60. package/dist/lib/contracts/auth.js +28 -0
  61. package/dist/lib/contracts/auth.js.map +1 -1
  62. package/dist/lib/contracts/index.d.ts +1 -1
  63. package/dist/lib/contracts/index.js +28 -0
  64. package/dist/lib/contracts/index.js.map +1 -1
  65. package/dist/lib/crypto.d.ts +76 -0
  66. package/dist/lib/crypto.js +127 -0
  67. package/dist/lib/crypto.js.map +1 -0
  68. package/dist/lib/index.d.ts +4 -0
  69. package/dist/lib/index.js +313 -0
  70. package/dist/lib/index.js.map +1 -0
  71. package/dist/lib/session.d.ts +68 -0
  72. package/dist/lib/session.js +126 -0
  73. package/dist/lib/session.js.map +1 -0
  74. package/dist/{api-BcQM4WKb.d.ts → lib/types/api.d.ts} +2 -2
  75. package/dist/lib/types/api.js +1 -0
  76. package/dist/lib/types/api.js.map +1 -0
  77. package/dist/lib/types/index.d.ts +3 -0
  78. package/dist/lib/types/index.js +2647 -0
  79. package/dist/lib/types/index.js.map +1 -0
  80. package/dist/lib/types/schemas.d.ts +45 -0
  81. package/dist/lib/types/schemas.js +2647 -0
  82. package/dist/lib/types/schemas.js.map +1 -0
  83. package/dist/lib.d.ts +2 -0
  84. package/dist/lib.js +1 -0
  85. package/dist/lib.js.map +1 -0
  86. package/dist/plugin.js +777 -645
  87. package/dist/plugin.js.map +1 -1
  88. package/dist/server/entities/index.d.ts +1 -0
  89. package/dist/server/entities/index.js +23 -27
  90. package/dist/server/entities/index.js.map +1 -1
  91. package/dist/server/entities/invitations.js +12 -9
  92. package/dist/server/entities/invitations.js.map +1 -1
  93. package/dist/server/entities/permissions.js +8 -3
  94. package/dist/server/entities/permissions.js.map +1 -1
  95. package/dist/server/entities/role-permissions.js +12 -9
  96. package/dist/server/entities/role-permissions.js.map +1 -1
  97. package/dist/server/entities/roles.js +8 -3
  98. package/dist/server/entities/roles.js.map +1 -1
  99. package/dist/server/entities/schema.d.ts +14 -0
  100. package/dist/server/entities/schema.js +7 -0
  101. package/dist/server/entities/schema.js.map +1 -0
  102. package/dist/server/entities/user-permissions.js +14 -12
  103. package/dist/server/entities/user-permissions.js.map +1 -1
  104. package/dist/server/entities/user-public-keys.js +12 -9
  105. package/dist/server/entities/user-public-keys.js.map +1 -1
  106. package/dist/server/entities/user-social-accounts.js +12 -9
  107. package/dist/server/entities/user-social-accounts.js.map +1 -1
  108. package/dist/server/entities/users.js +10 -6
  109. package/dist/server/entities/users.js.map +1 -1
  110. package/dist/server/entities/verification-codes.js +8 -3
  111. package/dist/server/entities/verification-codes.js.map +1 -1
  112. package/dist/server/routes/auth/index.js +495 -512
  113. package/dist/server/routes/auth/index.js.map +1 -1
  114. package/dist/server/routes/index.js +775 -545
  115. package/dist/server/routes/index.js.map +1 -1
  116. package/dist/server/routes/invitations/index.js +416 -230
  117. package/dist/server/routes/invitations/index.js.map +1 -1
  118. package/dist/server.d.ts +91 -62
  119. package/dist/server.js +320 -327
  120. package/dist/server.js.map +1 -1
  121. package/migrations/{0000_tired_gambit.sql → 0000_complex_swordsman.sql} +2 -0
  122. package/migrations/meta/0000_snapshot.json +4 -2
  123. package/migrations/meta/_journal.json +2 -2
  124. package/package.json +30 -3
package/dist/server.js CHANGED
@@ -8,15 +8,25 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/server/entities/schema.ts
12
+ import { createFunctionSchema } from "@spfn/core/db";
13
+ var authSchema;
14
+ var init_schema = __esm({
15
+ "src/server/entities/schema.ts"() {
16
+ "use strict";
17
+ authSchema = createFunctionSchema("@spfn/auth");
18
+ }
19
+ });
20
+
11
21
  // src/server/entities/roles.ts
12
22
  import { text, boolean, integer, index } from "drizzle-orm/pg-core";
13
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
14
- var schema, roles;
23
+ import { id, timestamps } from "@spfn/core/db";
24
+ var roles;
15
25
  var init_roles = __esm({
16
26
  "src/server/entities/roles.ts"() {
17
27
  "use strict";
18
- schema = createFunctionSchema("@spfn/auth");
19
- roles = schema.table(
28
+ init_schema();
29
+ roles = authSchema.table(
20
30
  "roles",
21
31
  {
22
32
  // Primary key
@@ -58,15 +68,15 @@ var init_roles = __esm({
58
68
 
59
69
  // src/server/entities/users.ts
60
70
  import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
61
- import { id as id2, timestamps as timestamps2, createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
71
+ import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
62
72
  import { sql } from "drizzle-orm";
63
- var schema2, users;
73
+ var users;
64
74
  var init_users = __esm({
65
75
  "src/server/entities/users.ts"() {
66
76
  "use strict";
67
77
  init_roles();
68
- schema2 = createFunctionSchema2("@spfn/auth");
69
- users = schema2.table(
78
+ init_schema();
79
+ users = authSchema.table(
70
80
  "users",
71
81
  {
72
82
  // Identity
@@ -131,14 +141,14 @@ var init_users = __esm({
131
141
 
132
142
  // src/server/entities/user-social-accounts.ts
133
143
  import { text as text3, timestamp as timestamp2, uniqueIndex } from "drizzle-orm/pg-core";
134
- import { id as id3, timestamps as timestamps3, foreignKey, createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
135
- var schema3, userSocialAccounts;
144
+ import { id as id3, timestamps as timestamps3, foreignKey } from "@spfn/core/db";
145
+ var userSocialAccounts;
136
146
  var init_user_social_accounts = __esm({
137
147
  "src/server/entities/user-social-accounts.ts"() {
138
148
  "use strict";
139
149
  init_users();
140
- schema3 = createFunctionSchema3("@spfn/auth");
141
- userSocialAccounts = schema3.table(
150
+ init_schema();
151
+ userSocialAccounts = authSchema.table(
142
152
  "user_social_accounts",
143
153
  {
144
154
  id: id3(),
@@ -169,14 +179,14 @@ var init_user_social_accounts = __esm({
169
179
 
170
180
  // src/server/entities/user-public-keys.ts
171
181
  import { text as text4, timestamp as timestamp3, boolean as boolean3, index as index3 } from "drizzle-orm/pg-core";
172
- import { id as id4, foreignKey as foreignKey2, createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
173
- var schema4, userPublicKeys;
182
+ import { id as id4, foreignKey as foreignKey2 } from "@spfn/core/db";
183
+ var userPublicKeys;
174
184
  var init_user_public_keys = __esm({
175
185
  "src/server/entities/user-public-keys.ts"() {
176
186
  "use strict";
177
187
  init_users();
178
- schema4 = createFunctionSchema4("@spfn/auth");
179
- userPublicKeys = schema4.table(
188
+ init_schema();
189
+ userPublicKeys = authSchema.table(
180
190
  "user_public_keys",
181
191
  {
182
192
  id: id4(),
@@ -214,13 +224,13 @@ var init_user_public_keys = __esm({
214
224
 
215
225
  // src/server/entities/verification-codes.ts
216
226
  import { text as text5, timestamp as timestamp4, index as index4 } from "drizzle-orm/pg-core";
217
- import { id as id5, timestamps as timestamps4, createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
218
- var schema5, verificationCodes;
227
+ import { id as id5, timestamps as timestamps4 } from "@spfn/core/db";
228
+ var verificationCodes;
219
229
  var init_verification_codes = __esm({
220
230
  "src/server/entities/verification-codes.ts"() {
221
231
  "use strict";
222
- schema5 = createFunctionSchema5("@spfn/auth");
223
- verificationCodes = schema5.table(
232
+ init_schema();
233
+ verificationCodes = authSchema.table(
224
234
  "verification_codes",
225
235
  {
226
236
  id: id5(),
@@ -261,15 +271,15 @@ var init_verification_codes = __esm({
261
271
 
262
272
  // src/server/entities/invitations.ts
263
273
  import { text as text6, timestamp as timestamp5, bigint as bigint2, index as index5, jsonb } from "drizzle-orm/pg-core";
264
- import { id as id6, timestamps as timestamps5, createFunctionSchema as createFunctionSchema6 } from "@spfn/core/db";
265
- var schema6, invitations;
274
+ import { id as id6, timestamps as timestamps5 } from "@spfn/core/db";
275
+ var invitations;
266
276
  var init_invitations = __esm({
267
277
  "src/server/entities/invitations.ts"() {
268
278
  "use strict";
269
279
  init_roles();
270
280
  init_users();
271
- schema6 = createFunctionSchema6("@spfn/auth");
272
- invitations = schema6.table(
281
+ init_schema();
282
+ invitations = authSchema.table(
273
283
  "user_invitations",
274
284
  {
275
285
  // Primary key
@@ -337,13 +347,13 @@ var init_invitations = __esm({
337
347
 
338
348
  // src/server/entities/permissions.ts
339
349
  import { text as text7, boolean as boolean4, index as index6 } from "drizzle-orm/pg-core";
340
- import { id as id7, timestamps as timestamps6, createFunctionSchema as createFunctionSchema7 } from "@spfn/core/db";
341
- var schema7, permissions;
350
+ import { id as id7, timestamps as timestamps6 } from "@spfn/core/db";
351
+ var permissions;
342
352
  var init_permissions = __esm({
343
353
  "src/server/entities/permissions.ts"() {
344
354
  "use strict";
345
- schema7 = createFunctionSchema7("@spfn/auth");
346
- permissions = schema7.table(
355
+ init_schema();
356
+ permissions = authSchema.table(
347
357
  "permissions",
348
358
  {
349
359
  // Primary key
@@ -384,15 +394,15 @@ var init_permissions = __esm({
384
394
 
385
395
  // src/server/entities/role-permissions.ts
386
396
  import { bigint as bigint3, index as index7, unique } from "drizzle-orm/pg-core";
387
- import { id as id8, timestamps as timestamps7, createFunctionSchema as createFunctionSchema8 } from "@spfn/core/db";
388
- var schema8, rolePermissions;
397
+ import { id as id8, timestamps as timestamps7 } from "@spfn/core/db";
398
+ var rolePermissions;
389
399
  var init_role_permissions = __esm({
390
400
  "src/server/entities/role-permissions.ts"() {
391
401
  "use strict";
392
402
  init_roles();
393
403
  init_permissions();
394
- schema8 = createFunctionSchema8("@spfn/auth");
395
- rolePermissions = schema8.table(
404
+ init_schema();
405
+ rolePermissions = authSchema.table(
396
406
  "role_permissions",
397
407
  {
398
408
  // Primary key
@@ -416,15 +426,15 @@ var init_role_permissions = __esm({
416
426
 
417
427
  // src/server/entities/user-permissions.ts
418
428
  import { bigint as bigint4, boolean as boolean5, text as text8, timestamp as timestamp6, index as index8, unique as unique2 } from "drizzle-orm/pg-core";
419
- import { id as id9, timestamps as timestamps8, createFunctionSchema as createFunctionSchema9 } from "@spfn/core/db";
420
- var schema9, userPermissions;
429
+ import { id as id9, timestamps as timestamps8 } from "@spfn/core/db";
430
+ var userPermissions;
421
431
  var init_user_permissions = __esm({
422
432
  "src/server/entities/user-permissions.ts"() {
423
433
  "use strict";
424
434
  init_users();
425
435
  init_permissions();
426
- schema9 = createFunctionSchema9("@spfn/auth");
427
- userPermissions = schema9.table(
436
+ init_schema();
437
+ userPermissions = authSchema.table(
428
438
  "user_permissions",
429
439
  {
430
440
  // Primary key
@@ -460,6 +470,7 @@ var init_user_permissions = __esm({
460
470
  // src/server/entities/index.ts
461
471
  var entities_exports = {};
462
472
  __export(entities_exports, {
473
+ authSchema: () => authSchema,
463
474
  invitations: () => invitations,
464
475
  permissions: () => permissions,
465
476
  rolePermissions: () => rolePermissions,
@@ -473,6 +484,7 @@ __export(entities_exports, {
473
484
  var init_entities = __esm({
474
485
  "src/server/entities/index.ts"() {
475
486
  "use strict";
487
+ init_schema();
476
488
  init_users();
477
489
  init_user_social_accounts();
478
490
  init_user_public_keys();
@@ -485,6 +497,83 @@ var init_entities = __esm({
485
497
  }
486
498
  });
487
499
 
500
+ // src/server/helpers/jwt.ts
501
+ var jwt_exports = {};
502
+ __export(jwt_exports, {
503
+ decodeToken: () => decodeToken,
504
+ generateToken: () => generateToken,
505
+ verifyClientToken: () => verifyClientToken,
506
+ verifyKeyFingerprint: () => verifyKeyFingerprint,
507
+ verifyToken: () => verifyToken
508
+ });
509
+ import jwt from "jsonwebtoken";
510
+ import crypto from "crypto";
511
+ function generateToken(payload) {
512
+ return jwt.sign(payload, JWT_SECRET, {
513
+ expiresIn: JWT_EXPIRES_IN
514
+ });
515
+ }
516
+ function verifyToken(token) {
517
+ return jwt.verify(token, JWT_SECRET);
518
+ }
519
+ function verifyClientToken(token, publicKeyB64, algorithm) {
520
+ try {
521
+ const publicKeyDER = Buffer.from(publicKeyB64, "base64");
522
+ const publicKeyObject = crypto.createPublicKey({
523
+ key: publicKeyDER,
524
+ format: "der",
525
+ type: "spki"
526
+ });
527
+ const decoded = jwt.verify(token, publicKeyObject, {
528
+ algorithms: [algorithm],
529
+ // Prevent algorithm confusion attacks
530
+ issuer: "spfn-client"
531
+ // Validate token issuer
532
+ });
533
+ if (typeof decoded === "string") {
534
+ throw new Error("Invalid token format: expected object payload");
535
+ }
536
+ return decoded;
537
+ } catch (error) {
538
+ if (error instanceof jwt.TokenExpiredError) {
539
+ throw new Error("Token has expired");
540
+ }
541
+ if (error instanceof jwt.JsonWebTokenError) {
542
+ throw new Error("Invalid token signature");
543
+ }
544
+ throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
545
+ }
546
+ }
547
+ function decodeToken(token) {
548
+ try {
549
+ return jwt.decode(token);
550
+ } catch {
551
+ return null;
552
+ }
553
+ }
554
+ function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
555
+ try {
556
+ const publicKeyDER = Buffer.from(publicKeyB64, "base64");
557
+ const fingerprint = crypto.createHash("sha256").update(publicKeyDER).digest("hex");
558
+ return fingerprint === expectedFingerprint;
559
+ } catch (error) {
560
+ console.error("Failed to verify key fingerprint:", error);
561
+ return false;
562
+ }
563
+ }
564
+ var JWT_SECRET, JWT_EXPIRES_IN;
565
+ var init_jwt = __esm({
566
+ "src/server/helpers/jwt.ts"() {
567
+ "use strict";
568
+ JWT_SECRET = process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)
569
+ process.env.JWT_SECRET || // Legacy fallback
570
+ "dev-secret-key-change-in-production";
571
+ JWT_EXPIRES_IN = process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)
572
+ process.env.JWT_EXPIRES_IN || // Legacy fallback
573
+ "7d";
574
+ }
575
+ });
576
+
488
577
  // src/server/services/role.service.ts
489
578
  var role_service_exports = {};
490
579
  __export(role_service_exports, {
@@ -710,6 +799,14 @@ var BUILTIN_PERMISSIONS = {
710
799
  isSystem: true,
711
800
  isBuiltin: true
712
801
  },
802
+ USER_INVITE: {
803
+ name: "user:invite",
804
+ displayName: "Invite Users",
805
+ description: "Create and send user invitations",
806
+ category: "user",
807
+ isSystem: true,
808
+ isBuiltin: true
809
+ },
713
810
  // RBAC management (superadmin functions)
714
811
  RBAC_ROLE_MANAGE: {
715
812
  name: "rbac:role:manage",
@@ -734,6 +831,7 @@ var BUILTIN_ROLE_PERMISSIONS = {
734
831
  "user:read",
735
832
  "user:write",
736
833
  "user:delete",
834
+ "user:invite",
737
835
  "rbac:role:manage",
738
836
  "rbac:permission:manage"
739
837
  ],
@@ -741,113 +839,14 @@ var BUILTIN_ROLE_PERMISSIONS = {
741
839
  "auth:self:manage",
742
840
  "user:read",
743
841
  "user:write",
744
- "user:delete"
842
+ "user:delete",
843
+ "user:invite"
745
844
  ],
746
845
  user: [
747
846
  "auth:self:manage"
748
847
  ]
749
848
  };
750
849
 
751
- // src/server/rbac/presets.ts
752
- var PRESET_ROLES = {
753
- MODERATOR: {
754
- name: "moderator",
755
- displayName: "Moderator",
756
- description: "Content moderation and community management",
757
- priority: 50,
758
- isSystem: true
759
- },
760
- EDITOR: {
761
- name: "editor",
762
- displayName: "Editor",
763
- description: "Content creation and editing",
764
- priority: 30,
765
- isSystem: true
766
- },
767
- VIEWER: {
768
- name: "viewer",
769
- displayName: "Viewer",
770
- description: "Read-only access to content",
771
- priority: 5,
772
- isSystem: true
773
- }
774
- };
775
- var PRESET_PERMISSIONS = {
776
- // Content management
777
- CONTENT_READ: {
778
- name: "content:read",
779
- displayName: "Read Content",
780
- description: "View all content including drafts",
781
- category: "content",
782
- isSystem: true
783
- },
784
- CONTENT_WRITE: {
785
- name: "content:write",
786
- displayName: "Write Content",
787
- description: "Create and edit content",
788
- category: "content",
789
- isSystem: true
790
- },
791
- CONTENT_DELETE: {
792
- name: "content:delete",
793
- displayName: "Delete Content",
794
- description: "Delete any content",
795
- category: "content",
796
- isSystem: true
797
- },
798
- CONTENT_PUBLISH: {
799
- name: "content:publish",
800
- displayName: "Publish Content",
801
- description: "Publish content to make it public",
802
- category: "content",
803
- isSystem: true
804
- },
805
- // Moderation
806
- COMMENT_MODERATE: {
807
- name: "comment:moderate",
808
- displayName: "Moderate Comments",
809
- description: "Review and delete inappropriate comments",
810
- category: "moderation",
811
- isSystem: true
812
- },
813
- // System
814
- SYSTEM_CONFIG: {
815
- name: "system:config",
816
- displayName: "System Configuration",
817
- description: "Configure application settings",
818
- category: "system",
819
- isSystem: true
820
- },
821
- // Analytics
822
- ANALYTICS_VIEW: {
823
- name: "analytics:view",
824
- displayName: "View Analytics",
825
- description: "Access analytics dashboard and reports",
826
- category: "analytics",
827
- isSystem: true
828
- }
829
- };
830
- var PRESET_ROLE_PERMISSIONS = {
831
- moderator: [
832
- "auth:self:manage",
833
- "user:read",
834
- "content:read",
835
- "content:write",
836
- "content:delete",
837
- "comment:moderate"
838
- ],
839
- editor: [
840
- "auth:self:manage",
841
- "content:read",
842
- "content:write",
843
- "content:publish"
844
- ],
845
- viewer: [
846
- "auth:self:manage",
847
- "content:read"
848
- ]
849
- };
850
-
851
850
  // src/server/services/auth.service.ts
852
851
  init_entities();
853
852
  import { findOne as findOne2, create as create3 } from "@spfn/core/db";
@@ -899,68 +898,8 @@ function validatePasswordStrength(password) {
899
898
  };
900
899
  }
901
900
 
902
- // src/server/helpers/jwt.ts
903
- import jwt from "jsonwebtoken";
904
- import crypto from "crypto";
905
- var JWT_SECRET = process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)
906
- process.env.JWT_SECRET || // Legacy fallback
907
- "dev-secret-key-change-in-production";
908
- var JWT_EXPIRES_IN = process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)
909
- process.env.JWT_EXPIRES_IN || // Legacy fallback
910
- "7d";
911
- function generateToken(payload) {
912
- return jwt.sign(payload, JWT_SECRET, {
913
- expiresIn: JWT_EXPIRES_IN
914
- });
915
- }
916
- function verifyToken(token) {
917
- return jwt.verify(token, JWT_SECRET);
918
- }
919
- function verifyClientToken(token, publicKeyB64, algorithm) {
920
- try {
921
- const publicKeyDER = Buffer.from(publicKeyB64, "base64");
922
- const publicKeyObject = crypto.createPublicKey({
923
- key: publicKeyDER,
924
- format: "der",
925
- type: "spki"
926
- });
927
- const decoded = jwt.verify(token, publicKeyObject, {
928
- algorithms: [algorithm],
929
- // Prevent algorithm confusion attacks
930
- issuer: "spfn-client"
931
- // Validate token issuer
932
- });
933
- if (typeof decoded === "string") {
934
- throw new Error("Invalid token format: expected object payload");
935
- }
936
- return decoded;
937
- } catch (error) {
938
- if (error instanceof jwt.TokenExpiredError) {
939
- throw new Error("Token has expired");
940
- }
941
- if (error instanceof jwt.JsonWebTokenError) {
942
- throw new Error("Invalid token signature");
943
- }
944
- throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
945
- }
946
- }
947
- function decodeToken(token) {
948
- try {
949
- return jwt.decode(token);
950
- } catch {
951
- return null;
952
- }
953
- }
954
- function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
955
- try {
956
- const publicKeyDER = Buffer.from(publicKeyB64, "base64");
957
- const fingerprint = crypto.createHash("sha256").update(publicKeyDER).digest("hex");
958
- return fingerprint === expectedFingerprint;
959
- } catch (error) {
960
- console.error("Failed to verify key fingerprint:", error);
961
- return false;
962
- }
963
- }
901
+ // src/server/helpers/index.ts
902
+ init_jwt();
964
903
 
965
904
  // src/server/helpers/verification.ts
966
905
  init_verification_codes();
@@ -1119,16 +1058,14 @@ var KeyExpiredError = class extends UnauthorizedError {
1119
1058
  };
1120
1059
  var AccountDisabledError = class extends ForbiddenError {
1121
1060
  constructor(status = "disabled") {
1122
- super(`Account is ${status}`);
1061
+ super(`Account is ${status}`, { details: { status } });
1123
1062
  this.name = "AccountDisabledError";
1124
- this.details = { status };
1125
1063
  }
1126
1064
  };
1127
1065
  var AccountAlreadyExistsError = class extends ConflictError {
1128
1066
  constructor(identifier, identifierType) {
1129
- super("Account already exists");
1067
+ super("Account already exists", { details: { identifier, identifierType } });
1130
1068
  this.name = "AccountAlreadyExistsError";
1131
- this.details = { identifier, identifierType };
1132
1069
  }
1133
1070
  };
1134
1071
  var InvalidVerificationCodeError = class extends ValidationError {
@@ -1151,9 +1088,8 @@ var InvalidKeyFingerprintError = class extends ValidationError {
1151
1088
  };
1152
1089
  var VerificationTokenPurposeMismatchError = class extends ValidationError {
1153
1090
  constructor(expected, actual) {
1154
- super(`Verification token is for ${actual}, but ${expected} was expected`);
1091
+ super(`Verification token is for ${actual}, but ${expected} was expected`, { details: { expected, actual } });
1155
1092
  this.name = "VerificationTokenPurposeMismatchError";
1156
- this.details = { expected, actual };
1157
1093
  }
1158
1094
  };
1159
1095
  var VerificationTokenTargetMismatchError = class extends ValidationError {
@@ -1165,6 +1101,7 @@ var VerificationTokenTargetMismatchError = class extends ValidationError {
1165
1101
 
1166
1102
  // src/server/services/key.service.ts
1167
1103
  init_entities();
1104
+ init_jwt();
1168
1105
  import { create as create2, getDatabase as getDatabase2 } from "@spfn/core/db";
1169
1106
  import { eq as eq2, and as and2 } from "drizzle-orm";
1170
1107
  function getKeyExpiryDate() {
@@ -1453,52 +1390,105 @@ async function verifyCodeService(params) {
1453
1390
  };
1454
1391
  }
1455
1392
 
1456
- // src/server/services/rbac.service.ts
1393
+ // src/server/services/me.service.ts
1457
1394
  init_entities();
1458
1395
  import { getDatabase as getDatabase4 } from "@spfn/core/db";
1459
- import { eq as eq4, and as and4, inArray } from "drizzle-orm";
1460
- async function initializeAuth(options = {}) {
1396
+ import { eq as eq4, and as and4 } from "drizzle-orm";
1397
+ async function getMeService(userId) {
1461
1398
  const db = getDatabase4();
1462
1399
  if (!db) {
1463
- throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
1400
+ throw new Error("[Auth] Database not initialized");
1464
1401
  }
1465
- console.log("[Auth] \u{1F510} Initializing RBAC system...");
1466
- const allRoles = [...Object.values(BUILTIN_ROLES)];
1467
- if (options.usePresets) {
1468
- allRoles.push(...Object.values(PRESET_ROLES));
1469
- } else if (options.presetRoles) {
1470
- for (const presetKey of options.presetRoles) {
1471
- if (PRESET_ROLES[presetKey]) {
1472
- allRoles.push(PRESET_ROLES[presetKey]);
1473
- }
1474
- }
1402
+ const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
1403
+ const [userWithRole] = await db.select({
1404
+ userId: users.id,
1405
+ email: users.email,
1406
+ phone: users.phone,
1407
+ roleId: roles.id,
1408
+ roleName: roles.name,
1409
+ roleDisplayName: roles.displayName,
1410
+ rolePriority: roles.priority
1411
+ }).from(users).innerJoin(roles, eq4(users.roleId, roles.id)).where(eq4(users.id, userIdNum)).limit(1);
1412
+ if (!userWithRole) {
1413
+ throw new Error("[Auth] User not found");
1414
+ }
1415
+ const rolePerms = await db.select({
1416
+ id: permissions.id,
1417
+ name: permissions.name,
1418
+ displayName: permissions.displayName,
1419
+ category: permissions.category
1420
+ }).from(rolePermissions).innerJoin(permissions, eq4(rolePermissions.permissionId, permissions.id)).where(
1421
+ and4(
1422
+ eq4(rolePermissions.roleId, userWithRole.roleId),
1423
+ eq4(permissions.isActive, true)
1424
+ )
1425
+ );
1426
+ return {
1427
+ userId: userWithRole.userId.toString(),
1428
+ email: userWithRole.email ?? void 0,
1429
+ phone: userWithRole.phone ?? void 0,
1430
+ role: {
1431
+ id: userWithRole.roleId,
1432
+ name: userWithRole.roleName,
1433
+ displayName: userWithRole.roleDisplayName,
1434
+ priority: userWithRole.rolePriority
1435
+ },
1436
+ permissions: rolePerms.map((perm) => ({
1437
+ id: perm.id,
1438
+ name: perm.name,
1439
+ displayName: perm.displayName,
1440
+ category: perm.category ?? void 0
1441
+ }))
1442
+ };
1443
+ }
1444
+
1445
+ // src/server/services/rbac.service.ts
1446
+ init_entities();
1447
+ import { getDatabase as getDatabase5 } from "@spfn/core/db";
1448
+ import { logger } from "@spfn/core/logger";
1449
+ import { eq as eq5, and as and5, inArray } from "drizzle-orm";
1450
+
1451
+ // src/lib/config.ts
1452
+ var globalConfig = {
1453
+ sessionTtl: "7d"
1454
+ // Default: 7 days
1455
+ };
1456
+ function configureAuth(config) {
1457
+ globalConfig = {
1458
+ ...globalConfig,
1459
+ ...config
1460
+ };
1461
+ }
1462
+
1463
+ // src/server/services/rbac.service.ts
1464
+ var authLogger = logger.child("@spfn/auth");
1465
+ async function initializeAuth(options = {}) {
1466
+ const db = getDatabase5();
1467
+ if (!db) {
1468
+ throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
1475
1469
  }
1476
- if (options.roles) {
1477
- allRoles.push(...options.roles);
1470
+ authLogger.info("\u{1F510} Initializing RBAC system...");
1471
+ if (options.sessionTtl !== void 0) {
1472
+ configureAuth({
1473
+ sessionTtl: options.sessionTtl
1474
+ });
1475
+ authLogger.info(`\u23F1\uFE0F Session TTL: ${options.sessionTtl}`);
1478
1476
  }
1477
+ const allRoles = [
1478
+ ...Object.values(BUILTIN_ROLES),
1479
+ ...options.roles || []
1480
+ ];
1479
1481
  for (const roleConfig of allRoles) {
1480
1482
  await upsertRole(roleConfig);
1481
1483
  }
1482
- const allPermissions = [...Object.values(BUILTIN_PERMISSIONS)];
1483
- if (options.usePresets) {
1484
- allPermissions.push(...Object.values(PRESET_PERMISSIONS));
1485
- } else if (options.presetPermissions) {
1486
- for (const presetKey of options.presetPermissions) {
1487
- if (PRESET_PERMISSIONS[presetKey]) {
1488
- allPermissions.push(PRESET_PERMISSIONS[presetKey]);
1489
- }
1490
- }
1491
- }
1492
- if (options.permissions) {
1493
- allPermissions.push(...options.permissions);
1494
- }
1484
+ const allPermissions = [
1485
+ ...Object.values(BUILTIN_PERMISSIONS),
1486
+ ...options.permissions || []
1487
+ ];
1495
1488
  for (const permConfig of allPermissions) {
1496
1489
  await upsertPermission(permConfig);
1497
1490
  }
1498
1491
  const allMappings = { ...BUILTIN_ROLE_PERMISSIONS };
1499
- if (options.usePresets) {
1500
- Object.assign(allMappings, PRESET_ROLE_PERMISSIONS);
1501
- }
1502
1492
  if (options.rolePermissions) {
1503
1493
  for (const [roleName, permNames] of Object.entries(options.rolePermissions)) {
1504
1494
  if (allMappings[roleName]) {
@@ -1513,13 +1503,13 @@ async function initializeAuth(options = {}) {
1513
1503
  for (const [roleName, permNames] of Object.entries(allMappings)) {
1514
1504
  await assignPermissionsToRole(roleName, permNames);
1515
1505
  }
1516
- console.log("[Auth] \u2705 RBAC initialization complete");
1517
- console.log(`[Auth] \u{1F4CA} Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);
1518
- console.log(`[Auth] \u{1F512} Built-in roles: user, admin, superadmin`);
1506
+ authLogger.info("\u2705 RBAC initialization complete");
1507
+ authLogger.info(`\u{1F4CA} Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);
1508
+ authLogger.info("\u{1F512} Built-in roles: user, admin, superadmin");
1519
1509
  }
1520
1510
  async function upsertRole(config) {
1521
- const db = getDatabase4();
1522
- const existing = await db.select().from(roles).where(eq4(roles.name, config.name)).limit(1);
1511
+ const db = getDatabase5();
1512
+ const existing = await db.select().from(roles).where(eq5(roles.name, config.name)).limit(1);
1523
1513
  if (existing.length === 0) {
1524
1514
  await db.insert(roles).values({
1525
1515
  name: config.name,
@@ -1529,7 +1519,7 @@ async function upsertRole(config) {
1529
1519
  isSystem: config.isSystem ?? false,
1530
1520
  isBuiltin: config.isBuiltin ?? false
1531
1521
  });
1532
- console.log(`[Auth] \u2705 Created role: ${config.name}`);
1522
+ authLogger.info(` \u2705 Created role: ${config.name}`);
1533
1523
  } else {
1534
1524
  const updateData = {
1535
1525
  displayName: config.displayName,
@@ -1538,12 +1528,12 @@ async function upsertRole(config) {
1538
1528
  if (!existing[0].isBuiltin) {
1539
1529
  updateData.priority = config.priority ?? existing[0].priority;
1540
1530
  }
1541
- await db.update(roles).set(updateData).where(eq4(roles.id, existing[0].id));
1531
+ await db.update(roles).set(updateData).where(eq5(roles.id, existing[0].id));
1542
1532
  }
1543
1533
  }
1544
1534
  async function upsertPermission(config) {
1545
- const db = getDatabase4();
1546
- const existing = await db.select().from(permissions).where(eq4(permissions.name, config.name)).limit(1);
1535
+ const db = getDatabase5();
1536
+ const existing = await db.select().from(permissions).where(eq5(permissions.name, config.name)).limit(1);
1547
1537
  if (existing.length === 0) {
1548
1538
  await db.insert(permissions).values({
1549
1539
  name: config.name,
@@ -1553,32 +1543,32 @@ async function upsertPermission(config) {
1553
1543
  isSystem: config.isSystem ?? false,
1554
1544
  isBuiltin: config.isBuiltin ?? false
1555
1545
  });
1556
- console.log(`[Auth] \u2705 Created permission: ${config.name}`);
1546
+ authLogger.info(` \u2705 Created permission: ${config.name}`);
1557
1547
  } else {
1558
1548
  await db.update(permissions).set({
1559
1549
  displayName: config.displayName,
1560
1550
  description: config.description,
1561
1551
  category: config.category
1562
- }).where(eq4(permissions.id, existing[0].id));
1552
+ }).where(eq5(permissions.id, existing[0].id));
1563
1553
  }
1564
1554
  }
1565
1555
  async function assignPermissionsToRole(roleName, permissionNames) {
1566
- const db = getDatabase4();
1567
- const [role] = await db.select().from(roles).where(eq4(roles.name, roleName)).limit(1);
1556
+ const db = getDatabase5();
1557
+ const [role] = await db.select().from(roles).where(eq5(roles.name, roleName)).limit(1);
1568
1558
  if (!role) {
1569
- console.warn(`[Auth] \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
1559
+ authLogger.warn(` \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
1570
1560
  return;
1571
1561
  }
1572
1562
  const perms = await db.select().from(permissions).where(inArray(permissions.name, permissionNames));
1573
1563
  if (perms.length === 0) {
1574
- console.warn(`[Auth] \u26A0\uFE0F No permissions found for role: ${roleName}`);
1564
+ authLogger.warn(` \u26A0\uFE0F No permissions found for role: ${roleName}`);
1575
1565
  return;
1576
1566
  }
1577
1567
  for (const perm of perms) {
1578
1568
  const existing = await db.select().from(rolePermissions).where(
1579
- and4(
1580
- eq4(rolePermissions.roleId, role.id),
1581
- eq4(rolePermissions.permissionId, perm.id)
1569
+ and5(
1570
+ eq5(rolePermissions.roleId, role.id),
1571
+ eq5(rolePermissions.permissionId, perm.id)
1582
1572
  )
1583
1573
  ).limit(1);
1584
1574
  if (existing.length === 0) {
@@ -1592,23 +1582,23 @@ async function assignPermissionsToRole(roleName, permissionNames) {
1592
1582
 
1593
1583
  // src/server/services/permission.service.ts
1594
1584
  init_entities();
1595
- import { getDatabase as getDatabase5 } from "@spfn/core/db";
1596
- import { eq as eq5, and as and5 } from "drizzle-orm";
1585
+ import { getDatabase as getDatabase6 } from "@spfn/core/db";
1586
+ import { eq as eq6, and as and6 } from "drizzle-orm";
1597
1587
  async function getUserPermissions(userId) {
1598
- const db = getDatabase5();
1588
+ const db = getDatabase6();
1599
1589
  if (!db) {
1600
1590
  throw new Error("[Auth] Database not initialized");
1601
1591
  }
1602
1592
  const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
1603
- const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq5(users.id, userIdNum)).limit(1);
1593
+ const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
1604
1594
  if (!user || !user.roleId) {
1605
1595
  return [];
1606
1596
  }
1607
1597
  const permSet = /* @__PURE__ */ new Set();
1608
- const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq5(rolePermissions.permissionId, permissions.id)).where(
1609
- and5(
1610
- eq5(rolePermissions.roleId, user.roleId),
1611
- eq5(permissions.isActive, true)
1598
+ const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq6(rolePermissions.permissionId, permissions.id)).where(
1599
+ and6(
1600
+ eq6(rolePermissions.roleId, user.roleId),
1601
+ eq6(permissions.isActive, true)
1612
1602
  )
1613
1603
  );
1614
1604
  for (const perm of rolePerms) {
@@ -1618,7 +1608,7 @@ async function getUserPermissions(userId) {
1618
1608
  name: permissions.name,
1619
1609
  granted: userPermissions.granted,
1620
1610
  expiresAt: userPermissions.expiresAt
1621
- }).from(userPermissions).innerJoin(permissions, eq5(userPermissions.permissionId, permissions.id)).where(eq5(userPermissions.userId, userIdNum));
1611
+ }).from(userPermissions).innerJoin(permissions, eq6(userPermissions.permissionId, permissions.id)).where(eq6(userPermissions.userId, userIdNum));
1622
1612
  const now = /* @__PURE__ */ new Date();
1623
1613
  for (const userPerm of userPerms) {
1624
1614
  if (userPerm.expiresAt && userPerm.expiresAt < now) {
@@ -1645,16 +1635,16 @@ async function hasAllPermissions(userId, permissionNames) {
1645
1635
  return permissionNames.every((p) => perms.includes(p));
1646
1636
  }
1647
1637
  async function hasRole(userId, roleName) {
1648
- const db = getDatabase5();
1638
+ const db = getDatabase6();
1649
1639
  if (!db) {
1650
1640
  throw new Error("[Auth] Database not initialized");
1651
1641
  }
1652
1642
  const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
1653
- const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq5(users.id, userIdNum)).limit(1);
1643
+ const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
1654
1644
  if (!user || !user.roleId) {
1655
1645
  return false;
1656
1646
  }
1657
- const [role] = await db.select({ name: roles.name }).from(roles).where(eq5(roles.id, user.roleId)).limit(1);
1647
+ const [role] = await db.select({ name: roles.name }).from(roles).where(eq6(roles.id, user.roleId)).limit(1);
1658
1648
  return role?.name === roleName;
1659
1649
  }
1660
1650
  async function hasAnyRole(userId, roleNames) {
@@ -1671,8 +1661,8 @@ init_role_service();
1671
1661
 
1672
1662
  // src/server/services/invitation.service.ts
1673
1663
  init_entities();
1674
- import { getDatabase as getDatabase6 } from "@spfn/core/db";
1675
- import { eq as eq6, and as and6, lt, desc, sql as sql2 } from "drizzle-orm";
1664
+ import { getDatabase as getDatabase7 } from "@spfn/core/db";
1665
+ import { eq as eq7, and as and7, lt, desc, sql as sql2 } from "drizzle-orm";
1676
1666
  import crypto2 from "crypto";
1677
1667
  function generateInvitationToken() {
1678
1668
  return crypto2.randomUUID();
@@ -1683,7 +1673,7 @@ function calculateExpiresAt(days = 7) {
1683
1673
  return expiresAt;
1684
1674
  }
1685
1675
  async function createInvitation(params) {
1686
- const db = getDatabase6();
1676
+ const db = getDatabase7();
1687
1677
  if (!db) {
1688
1678
  throw new Error("[Auth] Database not initialized");
1689
1679
  }
@@ -1692,24 +1682,24 @@ async function createInvitation(params) {
1692
1682
  if (!emailRegex.test(email)) {
1693
1683
  throw new Error("Invalid email format");
1694
1684
  }
1695
- const existingUser = await db.select().from(users).where(eq6(users.email, email)).limit(1);
1685
+ const existingUser = await db.select().from(users).where(eq7(users.email, email)).limit(1);
1696
1686
  if (existingUser.length > 0) {
1697
1687
  throw new Error("User with this email already exists");
1698
1688
  }
1699
1689
  const existingInvitation = await db.select().from(invitations).where(
1700
- and6(
1701
- eq6(invitations.email, email),
1702
- eq6(invitations.status, "pending")
1690
+ and7(
1691
+ eq7(invitations.email, email),
1692
+ eq7(invitations.status, "pending")
1703
1693
  )
1704
1694
  ).limit(1);
1705
1695
  if (existingInvitation.length > 0) {
1706
1696
  throw new Error("Pending invitation already exists for this email");
1707
1697
  }
1708
- const role = await db.select().from(roles).where(eq6(roles.id, roleId)).limit(1);
1698
+ const role = await db.select().from(roles).where(eq7(roles.id, roleId)).limit(1);
1709
1699
  if (role.length === 0) {
1710
1700
  throw new Error(`Role with id ${roleId} not found`);
1711
1701
  }
1712
- const inviter = await db.select().from(users).where(eq6(users.id, invitedBy)).limit(1);
1702
+ const inviter = await db.select().from(users).where(eq7(users.id, invitedBy)).limit(1);
1713
1703
  if (inviter.length === 0) {
1714
1704
  throw new Error(`User with id ${invitedBy} not found`);
1715
1705
  }
@@ -1728,15 +1718,15 @@ async function createInvitation(params) {
1728
1718
  return invitation;
1729
1719
  }
1730
1720
  async function getInvitationByToken(token) {
1731
- const db = getDatabase6();
1721
+ const db = getDatabase7();
1732
1722
  if (!db) {
1733
1723
  throw new Error("[Auth] Database not initialized");
1734
1724
  }
1735
- const result = await db.select().from(invitations).where(eq6(invitations.token, token)).limit(1);
1725
+ const result = await db.select().from(invitations).where(eq7(invitations.token, token)).limit(1);
1736
1726
  return result[0] || null;
1737
1727
  }
1738
1728
  async function getInvitationWithDetails(token) {
1739
- const db = getDatabase6();
1729
+ const db = getDatabase7();
1740
1730
  if (!db) {
1741
1731
  throw new Error("[Auth] Database not initialized");
1742
1732
  }
@@ -1762,7 +1752,7 @@ async function getInvitationWithDetails(token) {
1762
1752
  id: users.id,
1763
1753
  email: users.email
1764
1754
  }
1765
- }).from(invitations).innerJoin(roles, eq6(invitations.roleId, roles.id)).innerJoin(users, eq6(invitations.invitedBy, users.id)).where(eq6(invitations.token, token)).limit(1);
1755
+ }).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(eq7(invitations.token, token)).limit(1);
1766
1756
  return result[0] || null;
1767
1757
  }
1768
1758
  async function validateInvitation(token) {
@@ -1785,7 +1775,7 @@ async function validateInvitation(token) {
1785
1775
  return { valid: true, invitation };
1786
1776
  }
1787
1777
  async function acceptInvitation(params) {
1788
- const db = getDatabase6();
1778
+ const db = getDatabase7();
1789
1779
  if (!db) {
1790
1780
  throw new Error("[Auth] Database not initialized");
1791
1781
  }
@@ -1795,7 +1785,7 @@ async function acceptInvitation(params) {
1795
1785
  throw new Error(validation.error || "Invalid invitation");
1796
1786
  }
1797
1787
  const invitation = validation.invitation;
1798
- const role = await db.select().from(roles).where(eq6(roles.id, invitation.roleId)).limit(1);
1788
+ const role = await db.select().from(roles).where(eq7(roles.id, invitation.roleId)).limit(1);
1799
1789
  if (role.length === 0) {
1800
1790
  throw new Error("Role not found");
1801
1791
  }
@@ -1825,7 +1815,7 @@ async function acceptInvitation(params) {
1825
1815
  status: "accepted",
1826
1816
  acceptedAt: /* @__PURE__ */ new Date(),
1827
1817
  updatedAt: /* @__PURE__ */ new Date()
1828
- }).where(eq6(invitations.id, invitation.id));
1818
+ }).where(eq7(invitations.id, invitation.id));
1829
1819
  return { newUser, role: role[0] };
1830
1820
  });
1831
1821
  console.log(`[Auth] \u2705 Invitation accepted: ${invitation.email} as ${result.role.name}`);
@@ -1836,7 +1826,7 @@ async function acceptInvitation(params) {
1836
1826
  };
1837
1827
  }
1838
1828
  async function listInvitations(params) {
1839
- const db = getDatabase6();
1829
+ const db = getDatabase7();
1840
1830
  if (!db) {
1841
1831
  throw new Error("[Auth] Database not initialized");
1842
1832
  }
@@ -1844,12 +1834,12 @@ async function listInvitations(params) {
1844
1834
  const offset = (page - 1) * limit;
1845
1835
  const conditions = [];
1846
1836
  if (status) {
1847
- conditions.push(eq6(invitations.status, status));
1837
+ conditions.push(eq7(invitations.status, status));
1848
1838
  }
1849
1839
  if (invitedBy) {
1850
- conditions.push(eq6(invitations.invitedBy, invitedBy));
1840
+ conditions.push(eq7(invitations.invitedBy, invitedBy));
1851
1841
  }
1852
- const whereClause = conditions.length > 0 ? and6(...conditions) : void 0;
1842
+ const whereClause = conditions.length > 0 ? and7(...conditions) : void 0;
1853
1843
  const countResult = await db.select({ count: sql2`count(*)` }).from(invitations).where(whereClause);
1854
1844
  const total = Number(countResult[0]?.count || 0);
1855
1845
  const results = await db.select({
@@ -1874,7 +1864,7 @@ async function listInvitations(params) {
1874
1864
  id: users.id,
1875
1865
  email: users.email
1876
1866
  }
1877
- }).from(invitations).innerJoin(roles, eq6(invitations.roleId, roles.id)).innerJoin(users, eq6(invitations.invitedBy, users.id)).where(whereClause).orderBy(desc(invitations.createdAt)).limit(limit).offset(offset);
1867
+ }).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(whereClause).orderBy(desc(invitations.createdAt)).limit(limit).offset(offset);
1878
1868
  return {
1879
1869
  invitations: results,
1880
1870
  total,
@@ -1884,11 +1874,11 @@ async function listInvitations(params) {
1884
1874
  };
1885
1875
  }
1886
1876
  async function cancelInvitation(id10, cancelledBy, reason) {
1887
- const db = getDatabase6();
1877
+ const db = getDatabase7();
1888
1878
  if (!db) {
1889
1879
  throw new Error("[Auth] Database not initialized");
1890
1880
  }
1891
- const invitation = await db.select().from(invitations).where(eq6(invitations.id, id10)).limit(1);
1881
+ const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
1892
1882
  if (invitation.length === 0) {
1893
1883
  throw new Error("Invitation not found");
1894
1884
  }
@@ -1900,26 +1890,26 @@ async function cancelInvitation(id10, cancelledBy, reason) {
1900
1890
  cancelledAt: /* @__PURE__ */ new Date(),
1901
1891
  updatedAt: /* @__PURE__ */ new Date(),
1902
1892
  metadata: invitation[0].metadata ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy } : { cancelReason: reason, cancelledBy }
1903
- }).where(eq6(invitations.id, id10));
1893
+ }).where(eq7(invitations.id, id10));
1904
1894
  console.log(`[Auth] \u26A0\uFE0F Invitation cancelled: ${invitation[0].email} (reason: ${reason || "none"})`);
1905
1895
  }
1906
1896
  async function deleteInvitation(id10) {
1907
- const db = getDatabase6();
1897
+ const db = getDatabase7();
1908
1898
  if (!db) {
1909
1899
  throw new Error("[Auth] Database not initialized");
1910
1900
  }
1911
- await db.delete(invitations).where(eq6(invitations.id, id10));
1901
+ await db.delete(invitations).where(eq7(invitations.id, id10));
1912
1902
  console.log(`[Auth] \u{1F5D1}\uFE0F Invitation deleted: ${id10}`);
1913
1903
  }
1914
1904
  async function expireOldInvitations() {
1915
- const db = getDatabase6();
1905
+ const db = getDatabase7();
1916
1906
  if (!db) {
1917
1907
  throw new Error("[Auth] Database not initialized");
1918
1908
  }
1919
1909
  const now = /* @__PURE__ */ new Date();
1920
1910
  const expiredInvitations = await db.select().from(invitations).where(
1921
- and6(
1922
- eq6(invitations.status, "pending"),
1911
+ and7(
1912
+ eq7(invitations.status, "pending"),
1923
1913
  lt(invitations.expiresAt, now)
1924
1914
  )
1925
1915
  );
@@ -1930,8 +1920,8 @@ async function expireOldInvitations() {
1930
1920
  status: "expired",
1931
1921
  updatedAt: now
1932
1922
  }).where(
1933
- and6(
1934
- eq6(invitations.status, "pending"),
1923
+ and7(
1924
+ eq7(invitations.status, "pending"),
1935
1925
  lt(invitations.expiresAt, now)
1936
1926
  )
1937
1927
  );
@@ -1939,11 +1929,11 @@ async function expireOldInvitations() {
1939
1929
  return expiredInvitations.length;
1940
1930
  }
1941
1931
  async function resendInvitation(id10, expiresInDays = 7) {
1942
- const db = getDatabase6();
1932
+ const db = getDatabase7();
1943
1933
  if (!db) {
1944
1934
  throw new Error("[Auth] Database not initialized");
1945
1935
  }
1946
- const invitation = await db.select().from(invitations).where(eq6(invitations.id, id10)).limit(1);
1936
+ const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
1947
1937
  if (invitation.length === 0) {
1948
1938
  throw new Error("Invitation not found");
1949
1939
  }
@@ -1955,31 +1945,34 @@ async function resendInvitation(id10, expiresInDays = 7) {
1955
1945
  status: "pending",
1956
1946
  expiresAt: newExpiresAt,
1957
1947
  updatedAt: /* @__PURE__ */ new Date()
1958
- }).where(eq6(invitations.id, id10)).returning();
1948
+ }).where(eq7(invitations.id, id10)).returning();
1959
1949
  console.log(`[Auth] \u{1F4E7} Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);
1960
1950
  return updated;
1961
1951
  }
1962
1952
 
1963
1953
  // src/server/middleware/authenticate.ts
1954
+ init_jwt();
1964
1955
  init_entities();
1965
- import { findOne as findOne3, getDatabase as getDatabase7 } from "@spfn/core/db";
1956
+ import { findOne as findOne3, getDatabase as getDatabase8 } from "@spfn/core/db";
1966
1957
  import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
1967
- import { eq as eq7, and as and7 } from "drizzle-orm";
1958
+ import { eq as eq8, and as and8 } from "drizzle-orm";
1968
1959
  async function authenticate(c, next) {
1969
1960
  const authHeader = c.req.header("Authorization");
1970
- const keyId = c.req.header("X-Key-Id");
1971
1961
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
1972
1962
  throw new UnauthorizedError2("Missing or invalid authorization header");
1973
1963
  }
1974
- if (!keyId) {
1975
- throw new UnauthorizedError2("Missing X-Key-Id header");
1976
- }
1977
1964
  const token = authHeader.substring(7);
1978
- const db = getDatabase7();
1965
+ const { decodeToken: decodeToken2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
1966
+ const decoded = decodeToken2(token);
1967
+ if (!decoded || !decoded.keyId) {
1968
+ throw new UnauthorizedError2("Invalid token: missing keyId");
1969
+ }
1970
+ const keyId = decoded.keyId;
1971
+ const db = getDatabase8();
1979
1972
  const [keyRecord] = await db.select().from(userPublicKeys).where(
1980
- and7(
1981
- eq7(userPublicKeys.keyId, keyId),
1982
- eq7(userPublicKeys.isActive, true)
1973
+ and8(
1974
+ eq8(userPublicKeys.keyId, keyId),
1975
+ eq8(userPublicKeys.isActive, true)
1983
1976
  )
1984
1977
  );
1985
1978
  if (!keyRecord) {
@@ -2012,7 +2005,7 @@ async function authenticate(c, next) {
2012
2005
  if (user.status !== "active") {
2013
2006
  throw new AccountDisabledError(user.status);
2014
2007
  }
2015
- db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq7(userPublicKeys.id, keyRecord.id)).execute().catch((err) => console.error("Failed to update lastUsedAt:", err));
2008
+ db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq8(userPublicKeys.id, keyRecord.id)).execute().catch((err) => console.error("Failed to update lastUsedAt:", err));
2016
2009
  c.set("auth", {
2017
2010
  user,
2018
2011
  userId: String(user.id),
@@ -2078,7 +2071,9 @@ function requireRole(...roleNames) {
2078
2071
  // src/server/setup.ts
2079
2072
  init_entities();
2080
2073
  import { findOne as findOne4, create as create4 } from "@spfn/core/db";
2074
+ import { logger as logger2 } from "@spfn/core/logger";
2081
2075
  init_role_service();
2076
+ var authLogger2 = logger2.child("@spfn/auth");
2082
2077
  function parseAdminAccounts() {
2083
2078
  const accounts = [];
2084
2079
  if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS) {
@@ -2087,12 +2082,12 @@ function parseAdminAccounts() {
2087
2082
  process.env.ADMIN_ACCOUNTS;
2088
2083
  const parsed = JSON.parse(accountsJson);
2089
2084
  if (!Array.isArray(parsed)) {
2090
- console.error("[Auth] \u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
2085
+ authLogger2.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
2091
2086
  return accounts;
2092
2087
  }
2093
2088
  for (const item of parsed) {
2094
2089
  if (!item.email || !item.password) {
2095
- console.warn("[Auth] \u26A0\uFE0F Skipping account: missing email or password");
2090
+ authLogger2.warn("\u26A0\uFE0F Skipping account: missing email or password");
2096
2091
  continue;
2097
2092
  }
2098
2093
  accounts.push({
@@ -2107,7 +2102,7 @@ function parseAdminAccounts() {
2107
2102
  return accounts;
2108
2103
  } catch (error) {
2109
2104
  const err = error;
2110
- console.error("[Auth] \u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err.message);
2105
+ authLogger2.error("\u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err);
2111
2106
  return accounts;
2112
2107
  }
2113
2108
  }
@@ -2122,7 +2117,7 @@ function parseAdminAccounts() {
2122
2117
  process.env.ADMIN_ROLES || // Legacy fallback
2123
2118
  "").split(",").map((s) => s.trim());
2124
2119
  if (passwords.length !== emails.length) {
2125
- console.error("[Auth] \u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
2120
+ authLogger2.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
2126
2121
  return accounts;
2127
2122
  }
2128
2123
  for (let i = 0; i < emails.length; i++) {
@@ -2130,7 +2125,7 @@ function parseAdminAccounts() {
2130
2125
  const password = passwords[i];
2131
2126
  const role = roles2[i] || "user";
2132
2127
  if (!email || !password) {
2133
- console.warn(`[Auth] \u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
2128
+ authLogger2.warn(`\u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
2134
2129
  continue;
2135
2130
  }
2136
2131
  accounts.push({
@@ -2161,7 +2156,7 @@ async function ensureAdminExists() {
2161
2156
  if (accounts.length === 0) {
2162
2157
  return;
2163
2158
  }
2164
- console.log(`[Auth] Creating ${accounts.length} admin account(s)...`);
2159
+ authLogger2.info(`Creating ${accounts.length} admin account(s)...`);
2165
2160
  let created = 0;
2166
2161
  let skipped = 0;
2167
2162
  let failed = 0;
@@ -2169,14 +2164,14 @@ async function ensureAdminExists() {
2169
2164
  try {
2170
2165
  const existing = await findOne4(users, { email: account.email });
2171
2166
  if (existing) {
2172
- console.log(`[Auth] \u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
2167
+ authLogger2.info(`\u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
2173
2168
  skipped++;
2174
2169
  continue;
2175
2170
  }
2176
2171
  const roleName = account.role || "user";
2177
2172
  const role = await getRoleByName(roleName);
2178
2173
  if (!role) {
2179
- console.error(`[Auth] \u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
2174
+ authLogger2.error(`\u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
2180
2175
  failed++;
2181
2176
  continue;
2182
2177
  }
@@ -2191,26 +2186,23 @@ async function ensureAdminExists() {
2191
2186
  passwordChangeRequired: account.passwordChangeRequired !== false,
2192
2187
  status: "active"
2193
2188
  });
2194
- console.log(`[Auth] \u2705 Admin account created: ${account.email} (${roleName})`);
2189
+ authLogger2.info(`\u2705 Admin account created: ${account.email} (${roleName})`);
2195
2190
  created++;
2196
2191
  } catch (error) {
2197
2192
  const err = error;
2198
- console.error(`[Auth] \u274C Failed to create account ${account.email}:`, err.message);
2193
+ authLogger2.error(`\u274C Failed to create account ${account.email}:`, err);
2199
2194
  failed++;
2200
2195
  }
2201
2196
  }
2202
- console.log(`[Auth] \u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
2197
+ authLogger2.info(`\u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
2203
2198
  if (created > 0) {
2204
- console.log("[Auth] \u26A0\uFE0F Please change passwords on first login!");
2199
+ authLogger2.info("\u26A0\uFE0F Please change passwords on first login!");
2205
2200
  }
2206
2201
  }
2207
2202
  export {
2208
2203
  BUILTIN_PERMISSIONS,
2209
2204
  BUILTIN_ROLES,
2210
2205
  BUILTIN_ROLE_PERMISSIONS,
2211
- PRESET_PERMISSIONS,
2212
- PRESET_ROLES,
2213
- PRESET_ROLE_PERMISSIONS,
2214
2206
  acceptInvitation,
2215
2207
  addPermissionToRole,
2216
2208
  authenticate,
@@ -2232,6 +2224,7 @@ export {
2232
2224
  getInvitationByToken,
2233
2225
  getInvitationWithDetails,
2234
2226
  getKeyId,
2227
+ getMeService,
2235
2228
  getRoleByName,
2236
2229
  getRolePermissions,
2237
2230
  getUser,