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

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 +7438 -7304
  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 +7438 -7304
  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 +497 -512
  113. package/dist/server/routes/auth/index.js.map +1 -1
  114. package/dist/server/routes/index.js +777 -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 +323 -327
  120. package/dist/server.js.map +1 -1
  121. package/migrations/{0000_tired_gambit.sql → 0000_familiar_firebrand.sql} +2 -0
  122. package/migrations/meta/0000_snapshot.json +4 -2
  123. package/migrations/meta/_journal.json +2 -2
  124. package/package.json +41 -7
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() {
@@ -1354,6 +1291,8 @@ async function loginService(params) {
1354
1291
  if (!user || !user.passwordHash) {
1355
1292
  throw new InvalidCredentialsError();
1356
1293
  }
1294
+ console.log("user", user);
1295
+ console.log("\uD328\uC2A4\uC6CC\uB4DC: ", password);
1357
1296
  const isValid = await verifyPassword(password, user.passwordHash);
1358
1297
  if (!isValid) {
1359
1298
  throw new InvalidCredentialsError();
@@ -1453,52 +1392,105 @@ async function verifyCodeService(params) {
1453
1392
  };
1454
1393
  }
1455
1394
 
1456
- // src/server/services/rbac.service.ts
1395
+ // src/server/services/me.service.ts
1457
1396
  init_entities();
1458
1397
  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 = {}) {
1398
+ import { eq as eq4, and as and4 } from "drizzle-orm";
1399
+ async function getMeService(userId) {
1461
1400
  const db = getDatabase4();
1462
1401
  if (!db) {
1463
- throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
1402
+ throw new Error("[Auth] Database not initialized");
1464
1403
  }
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
- }
1404
+ const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
1405
+ const [userWithRole] = await db.select({
1406
+ userId: users.id,
1407
+ email: users.email,
1408
+ phone: users.phone,
1409
+ roleId: roles.id,
1410
+ roleName: roles.name,
1411
+ roleDisplayName: roles.displayName,
1412
+ rolePriority: roles.priority
1413
+ }).from(users).innerJoin(roles, eq4(users.roleId, roles.id)).where(eq4(users.id, userIdNum)).limit(1);
1414
+ if (!userWithRole) {
1415
+ throw new Error("[Auth] User not found");
1416
+ }
1417
+ const rolePerms = await db.select({
1418
+ id: permissions.id,
1419
+ name: permissions.name,
1420
+ displayName: permissions.displayName,
1421
+ category: permissions.category
1422
+ }).from(rolePermissions).innerJoin(permissions, eq4(rolePermissions.permissionId, permissions.id)).where(
1423
+ and4(
1424
+ eq4(rolePermissions.roleId, userWithRole.roleId),
1425
+ eq4(permissions.isActive, true)
1426
+ )
1427
+ );
1428
+ return {
1429
+ userId: userWithRole.userId.toString(),
1430
+ email: userWithRole.email ?? void 0,
1431
+ phone: userWithRole.phone ?? void 0,
1432
+ role: {
1433
+ id: userWithRole.roleId,
1434
+ name: userWithRole.roleName,
1435
+ displayName: userWithRole.roleDisplayName,
1436
+ priority: userWithRole.rolePriority
1437
+ },
1438
+ permissions: rolePerms.map((perm) => ({
1439
+ id: perm.id,
1440
+ name: perm.name,
1441
+ displayName: perm.displayName,
1442
+ category: perm.category ?? void 0
1443
+ }))
1444
+ };
1445
+ }
1446
+
1447
+ // src/server/services/rbac.service.ts
1448
+ init_entities();
1449
+ import { getDatabase as getDatabase5 } from "@spfn/core/db";
1450
+ import { logger } from "@spfn/core/logger";
1451
+ import { eq as eq5, and as and5, inArray } from "drizzle-orm";
1452
+
1453
+ // src/lib/config.ts
1454
+ var globalConfig = {
1455
+ sessionTtl: "7d"
1456
+ // Default: 7 days
1457
+ };
1458
+ function configureAuth(config) {
1459
+ globalConfig = {
1460
+ ...globalConfig,
1461
+ ...config
1462
+ };
1463
+ }
1464
+
1465
+ // src/server/services/rbac.service.ts
1466
+ var authLogger = logger.child("@spfn/auth");
1467
+ async function initializeAuth(options = {}) {
1468
+ const db = getDatabase5();
1469
+ if (!db) {
1470
+ throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
1475
1471
  }
1476
- if (options.roles) {
1477
- allRoles.push(...options.roles);
1472
+ authLogger.info("\u{1F510} Initializing RBAC system...");
1473
+ if (options.sessionTtl !== void 0) {
1474
+ configureAuth({
1475
+ sessionTtl: options.sessionTtl
1476
+ });
1477
+ authLogger.info(`\u23F1\uFE0F Session TTL: ${options.sessionTtl}`);
1478
1478
  }
1479
+ const allRoles = [
1480
+ ...Object.values(BUILTIN_ROLES),
1481
+ ...options.roles || []
1482
+ ];
1479
1483
  for (const roleConfig of allRoles) {
1480
1484
  await upsertRole(roleConfig);
1481
1485
  }
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
- }
1486
+ const allPermissions = [
1487
+ ...Object.values(BUILTIN_PERMISSIONS),
1488
+ ...options.permissions || []
1489
+ ];
1495
1490
  for (const permConfig of allPermissions) {
1496
1491
  await upsertPermission(permConfig);
1497
1492
  }
1498
1493
  const allMappings = { ...BUILTIN_ROLE_PERMISSIONS };
1499
- if (options.usePresets) {
1500
- Object.assign(allMappings, PRESET_ROLE_PERMISSIONS);
1501
- }
1502
1494
  if (options.rolePermissions) {
1503
1495
  for (const [roleName, permNames] of Object.entries(options.rolePermissions)) {
1504
1496
  if (allMappings[roleName]) {
@@ -1513,13 +1505,13 @@ async function initializeAuth(options = {}) {
1513
1505
  for (const [roleName, permNames] of Object.entries(allMappings)) {
1514
1506
  await assignPermissionsToRole(roleName, permNames);
1515
1507
  }
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`);
1508
+ authLogger.info("\u2705 RBAC initialization complete");
1509
+ authLogger.info(`\u{1F4CA} Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);
1510
+ authLogger.info("\u{1F512} Built-in roles: user, admin, superadmin");
1519
1511
  }
1520
1512
  async function upsertRole(config) {
1521
- const db = getDatabase4();
1522
- const existing = await db.select().from(roles).where(eq4(roles.name, config.name)).limit(1);
1513
+ const db = getDatabase5();
1514
+ const existing = await db.select().from(roles).where(eq5(roles.name, config.name)).limit(1);
1523
1515
  if (existing.length === 0) {
1524
1516
  await db.insert(roles).values({
1525
1517
  name: config.name,
@@ -1529,7 +1521,7 @@ async function upsertRole(config) {
1529
1521
  isSystem: config.isSystem ?? false,
1530
1522
  isBuiltin: config.isBuiltin ?? false
1531
1523
  });
1532
- console.log(`[Auth] \u2705 Created role: ${config.name}`);
1524
+ authLogger.info(` \u2705 Created role: ${config.name}`);
1533
1525
  } else {
1534
1526
  const updateData = {
1535
1527
  displayName: config.displayName,
@@ -1538,12 +1530,12 @@ async function upsertRole(config) {
1538
1530
  if (!existing[0].isBuiltin) {
1539
1531
  updateData.priority = config.priority ?? existing[0].priority;
1540
1532
  }
1541
- await db.update(roles).set(updateData).where(eq4(roles.id, existing[0].id));
1533
+ await db.update(roles).set(updateData).where(eq5(roles.id, existing[0].id));
1542
1534
  }
1543
1535
  }
1544
1536
  async function upsertPermission(config) {
1545
- const db = getDatabase4();
1546
- const existing = await db.select().from(permissions).where(eq4(permissions.name, config.name)).limit(1);
1537
+ const db = getDatabase5();
1538
+ const existing = await db.select().from(permissions).where(eq5(permissions.name, config.name)).limit(1);
1547
1539
  if (existing.length === 0) {
1548
1540
  await db.insert(permissions).values({
1549
1541
  name: config.name,
@@ -1553,32 +1545,32 @@ async function upsertPermission(config) {
1553
1545
  isSystem: config.isSystem ?? false,
1554
1546
  isBuiltin: config.isBuiltin ?? false
1555
1547
  });
1556
- console.log(`[Auth] \u2705 Created permission: ${config.name}`);
1548
+ authLogger.info(` \u2705 Created permission: ${config.name}`);
1557
1549
  } else {
1558
1550
  await db.update(permissions).set({
1559
1551
  displayName: config.displayName,
1560
1552
  description: config.description,
1561
1553
  category: config.category
1562
- }).where(eq4(permissions.id, existing[0].id));
1554
+ }).where(eq5(permissions.id, existing[0].id));
1563
1555
  }
1564
1556
  }
1565
1557
  async function assignPermissionsToRole(roleName, permissionNames) {
1566
- const db = getDatabase4();
1567
- const [role] = await db.select().from(roles).where(eq4(roles.name, roleName)).limit(1);
1558
+ const db = getDatabase5();
1559
+ const [role] = await db.select().from(roles).where(eq5(roles.name, roleName)).limit(1);
1568
1560
  if (!role) {
1569
- console.warn(`[Auth] \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
1561
+ authLogger.warn(` \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
1570
1562
  return;
1571
1563
  }
1572
1564
  const perms = await db.select().from(permissions).where(inArray(permissions.name, permissionNames));
1573
1565
  if (perms.length === 0) {
1574
- console.warn(`[Auth] \u26A0\uFE0F No permissions found for role: ${roleName}`);
1566
+ authLogger.warn(` \u26A0\uFE0F No permissions found for role: ${roleName}`);
1575
1567
  return;
1576
1568
  }
1577
1569
  for (const perm of perms) {
1578
1570
  const existing = await db.select().from(rolePermissions).where(
1579
- and4(
1580
- eq4(rolePermissions.roleId, role.id),
1581
- eq4(rolePermissions.permissionId, perm.id)
1571
+ and5(
1572
+ eq5(rolePermissions.roleId, role.id),
1573
+ eq5(rolePermissions.permissionId, perm.id)
1582
1574
  )
1583
1575
  ).limit(1);
1584
1576
  if (existing.length === 0) {
@@ -1592,23 +1584,23 @@ async function assignPermissionsToRole(roleName, permissionNames) {
1592
1584
 
1593
1585
  // src/server/services/permission.service.ts
1594
1586
  init_entities();
1595
- import { getDatabase as getDatabase5 } from "@spfn/core/db";
1596
- import { eq as eq5, and as and5 } from "drizzle-orm";
1587
+ import { getDatabase as getDatabase6 } from "@spfn/core/db";
1588
+ import { eq as eq6, and as and6 } from "drizzle-orm";
1597
1589
  async function getUserPermissions(userId) {
1598
- const db = getDatabase5();
1590
+ const db = getDatabase6();
1599
1591
  if (!db) {
1600
1592
  throw new Error("[Auth] Database not initialized");
1601
1593
  }
1602
1594
  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);
1595
+ const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
1604
1596
  if (!user || !user.roleId) {
1605
1597
  return [];
1606
1598
  }
1607
1599
  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)
1600
+ const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq6(rolePermissions.permissionId, permissions.id)).where(
1601
+ and6(
1602
+ eq6(rolePermissions.roleId, user.roleId),
1603
+ eq6(permissions.isActive, true)
1612
1604
  )
1613
1605
  );
1614
1606
  for (const perm of rolePerms) {
@@ -1618,7 +1610,7 @@ async function getUserPermissions(userId) {
1618
1610
  name: permissions.name,
1619
1611
  granted: userPermissions.granted,
1620
1612
  expiresAt: userPermissions.expiresAt
1621
- }).from(userPermissions).innerJoin(permissions, eq5(userPermissions.permissionId, permissions.id)).where(eq5(userPermissions.userId, userIdNum));
1613
+ }).from(userPermissions).innerJoin(permissions, eq6(userPermissions.permissionId, permissions.id)).where(eq6(userPermissions.userId, userIdNum));
1622
1614
  const now = /* @__PURE__ */ new Date();
1623
1615
  for (const userPerm of userPerms) {
1624
1616
  if (userPerm.expiresAt && userPerm.expiresAt < now) {
@@ -1645,16 +1637,16 @@ async function hasAllPermissions(userId, permissionNames) {
1645
1637
  return permissionNames.every((p) => perms.includes(p));
1646
1638
  }
1647
1639
  async function hasRole(userId, roleName) {
1648
- const db = getDatabase5();
1640
+ const db = getDatabase6();
1649
1641
  if (!db) {
1650
1642
  throw new Error("[Auth] Database not initialized");
1651
1643
  }
1652
1644
  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);
1645
+ const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
1654
1646
  if (!user || !user.roleId) {
1655
1647
  return false;
1656
1648
  }
1657
- const [role] = await db.select({ name: roles.name }).from(roles).where(eq5(roles.id, user.roleId)).limit(1);
1649
+ const [role] = await db.select({ name: roles.name }).from(roles).where(eq6(roles.id, user.roleId)).limit(1);
1658
1650
  return role?.name === roleName;
1659
1651
  }
1660
1652
  async function hasAnyRole(userId, roleNames) {
@@ -1671,8 +1663,8 @@ init_role_service();
1671
1663
 
1672
1664
  // src/server/services/invitation.service.ts
1673
1665
  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";
1666
+ import { getDatabase as getDatabase7 } from "@spfn/core/db";
1667
+ import { eq as eq7, and as and7, lt, desc, sql as sql2 } from "drizzle-orm";
1676
1668
  import crypto2 from "crypto";
1677
1669
  function generateInvitationToken() {
1678
1670
  return crypto2.randomUUID();
@@ -1683,7 +1675,7 @@ function calculateExpiresAt(days = 7) {
1683
1675
  return expiresAt;
1684
1676
  }
1685
1677
  async function createInvitation(params) {
1686
- const db = getDatabase6();
1678
+ const db = getDatabase7();
1687
1679
  if (!db) {
1688
1680
  throw new Error("[Auth] Database not initialized");
1689
1681
  }
@@ -1692,24 +1684,24 @@ async function createInvitation(params) {
1692
1684
  if (!emailRegex.test(email)) {
1693
1685
  throw new Error("Invalid email format");
1694
1686
  }
1695
- const existingUser = await db.select().from(users).where(eq6(users.email, email)).limit(1);
1687
+ const existingUser = await db.select().from(users).where(eq7(users.email, email)).limit(1);
1696
1688
  if (existingUser.length > 0) {
1697
1689
  throw new Error("User with this email already exists");
1698
1690
  }
1699
1691
  const existingInvitation = await db.select().from(invitations).where(
1700
- and6(
1701
- eq6(invitations.email, email),
1702
- eq6(invitations.status, "pending")
1692
+ and7(
1693
+ eq7(invitations.email, email),
1694
+ eq7(invitations.status, "pending")
1703
1695
  )
1704
1696
  ).limit(1);
1705
1697
  if (existingInvitation.length > 0) {
1706
1698
  throw new Error("Pending invitation already exists for this email");
1707
1699
  }
1708
- const role = await db.select().from(roles).where(eq6(roles.id, roleId)).limit(1);
1700
+ const role = await db.select().from(roles).where(eq7(roles.id, roleId)).limit(1);
1709
1701
  if (role.length === 0) {
1710
1702
  throw new Error(`Role with id ${roleId} not found`);
1711
1703
  }
1712
- const inviter = await db.select().from(users).where(eq6(users.id, invitedBy)).limit(1);
1704
+ const inviter = await db.select().from(users).where(eq7(users.id, invitedBy)).limit(1);
1713
1705
  if (inviter.length === 0) {
1714
1706
  throw new Error(`User with id ${invitedBy} not found`);
1715
1707
  }
@@ -1728,15 +1720,15 @@ async function createInvitation(params) {
1728
1720
  return invitation;
1729
1721
  }
1730
1722
  async function getInvitationByToken(token) {
1731
- const db = getDatabase6();
1723
+ const db = getDatabase7();
1732
1724
  if (!db) {
1733
1725
  throw new Error("[Auth] Database not initialized");
1734
1726
  }
1735
- const result = await db.select().from(invitations).where(eq6(invitations.token, token)).limit(1);
1727
+ const result = await db.select().from(invitations).where(eq7(invitations.token, token)).limit(1);
1736
1728
  return result[0] || null;
1737
1729
  }
1738
1730
  async function getInvitationWithDetails(token) {
1739
- const db = getDatabase6();
1731
+ const db = getDatabase7();
1740
1732
  if (!db) {
1741
1733
  throw new Error("[Auth] Database not initialized");
1742
1734
  }
@@ -1762,7 +1754,7 @@ async function getInvitationWithDetails(token) {
1762
1754
  id: users.id,
1763
1755
  email: users.email
1764
1756
  }
1765
- }).from(invitations).innerJoin(roles, eq6(invitations.roleId, roles.id)).innerJoin(users, eq6(invitations.invitedBy, users.id)).where(eq6(invitations.token, token)).limit(1);
1757
+ }).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(eq7(invitations.token, token)).limit(1);
1766
1758
  return result[0] || null;
1767
1759
  }
1768
1760
  async function validateInvitation(token) {
@@ -1785,7 +1777,7 @@ async function validateInvitation(token) {
1785
1777
  return { valid: true, invitation };
1786
1778
  }
1787
1779
  async function acceptInvitation(params) {
1788
- const db = getDatabase6();
1780
+ const db = getDatabase7();
1789
1781
  if (!db) {
1790
1782
  throw new Error("[Auth] Database not initialized");
1791
1783
  }
@@ -1795,7 +1787,7 @@ async function acceptInvitation(params) {
1795
1787
  throw new Error(validation.error || "Invalid invitation");
1796
1788
  }
1797
1789
  const invitation = validation.invitation;
1798
- const role = await db.select().from(roles).where(eq6(roles.id, invitation.roleId)).limit(1);
1790
+ const role = await db.select().from(roles).where(eq7(roles.id, invitation.roleId)).limit(1);
1799
1791
  if (role.length === 0) {
1800
1792
  throw new Error("Role not found");
1801
1793
  }
@@ -1825,7 +1817,7 @@ async function acceptInvitation(params) {
1825
1817
  status: "accepted",
1826
1818
  acceptedAt: /* @__PURE__ */ new Date(),
1827
1819
  updatedAt: /* @__PURE__ */ new Date()
1828
- }).where(eq6(invitations.id, invitation.id));
1820
+ }).where(eq7(invitations.id, invitation.id));
1829
1821
  return { newUser, role: role[0] };
1830
1822
  });
1831
1823
  console.log(`[Auth] \u2705 Invitation accepted: ${invitation.email} as ${result.role.name}`);
@@ -1836,7 +1828,7 @@ async function acceptInvitation(params) {
1836
1828
  };
1837
1829
  }
1838
1830
  async function listInvitations(params) {
1839
- const db = getDatabase6();
1831
+ const db = getDatabase7();
1840
1832
  if (!db) {
1841
1833
  throw new Error("[Auth] Database not initialized");
1842
1834
  }
@@ -1844,12 +1836,12 @@ async function listInvitations(params) {
1844
1836
  const offset = (page - 1) * limit;
1845
1837
  const conditions = [];
1846
1838
  if (status) {
1847
- conditions.push(eq6(invitations.status, status));
1839
+ conditions.push(eq7(invitations.status, status));
1848
1840
  }
1849
1841
  if (invitedBy) {
1850
- conditions.push(eq6(invitations.invitedBy, invitedBy));
1842
+ conditions.push(eq7(invitations.invitedBy, invitedBy));
1851
1843
  }
1852
- const whereClause = conditions.length > 0 ? and6(...conditions) : void 0;
1844
+ const whereClause = conditions.length > 0 ? and7(...conditions) : void 0;
1853
1845
  const countResult = await db.select({ count: sql2`count(*)` }).from(invitations).where(whereClause);
1854
1846
  const total = Number(countResult[0]?.count || 0);
1855
1847
  const results = await db.select({
@@ -1874,7 +1866,7 @@ async function listInvitations(params) {
1874
1866
  id: users.id,
1875
1867
  email: users.email
1876
1868
  }
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);
1869
+ }).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
1870
  return {
1879
1871
  invitations: results,
1880
1872
  total,
@@ -1884,11 +1876,11 @@ async function listInvitations(params) {
1884
1876
  };
1885
1877
  }
1886
1878
  async function cancelInvitation(id10, cancelledBy, reason) {
1887
- const db = getDatabase6();
1879
+ const db = getDatabase7();
1888
1880
  if (!db) {
1889
1881
  throw new Error("[Auth] Database not initialized");
1890
1882
  }
1891
- const invitation = await db.select().from(invitations).where(eq6(invitations.id, id10)).limit(1);
1883
+ const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
1892
1884
  if (invitation.length === 0) {
1893
1885
  throw new Error("Invitation not found");
1894
1886
  }
@@ -1900,26 +1892,26 @@ async function cancelInvitation(id10, cancelledBy, reason) {
1900
1892
  cancelledAt: /* @__PURE__ */ new Date(),
1901
1893
  updatedAt: /* @__PURE__ */ new Date(),
1902
1894
  metadata: invitation[0].metadata ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy } : { cancelReason: reason, cancelledBy }
1903
- }).where(eq6(invitations.id, id10));
1895
+ }).where(eq7(invitations.id, id10));
1904
1896
  console.log(`[Auth] \u26A0\uFE0F Invitation cancelled: ${invitation[0].email} (reason: ${reason || "none"})`);
1905
1897
  }
1906
1898
  async function deleteInvitation(id10) {
1907
- const db = getDatabase6();
1899
+ const db = getDatabase7();
1908
1900
  if (!db) {
1909
1901
  throw new Error("[Auth] Database not initialized");
1910
1902
  }
1911
- await db.delete(invitations).where(eq6(invitations.id, id10));
1903
+ await db.delete(invitations).where(eq7(invitations.id, id10));
1912
1904
  console.log(`[Auth] \u{1F5D1}\uFE0F Invitation deleted: ${id10}`);
1913
1905
  }
1914
1906
  async function expireOldInvitations() {
1915
- const db = getDatabase6();
1907
+ const db = getDatabase7();
1916
1908
  if (!db) {
1917
1909
  throw new Error("[Auth] Database not initialized");
1918
1910
  }
1919
1911
  const now = /* @__PURE__ */ new Date();
1920
1912
  const expiredInvitations = await db.select().from(invitations).where(
1921
- and6(
1922
- eq6(invitations.status, "pending"),
1913
+ and7(
1914
+ eq7(invitations.status, "pending"),
1923
1915
  lt(invitations.expiresAt, now)
1924
1916
  )
1925
1917
  );
@@ -1930,8 +1922,8 @@ async function expireOldInvitations() {
1930
1922
  status: "expired",
1931
1923
  updatedAt: now
1932
1924
  }).where(
1933
- and6(
1934
- eq6(invitations.status, "pending"),
1925
+ and7(
1926
+ eq7(invitations.status, "pending"),
1935
1927
  lt(invitations.expiresAt, now)
1936
1928
  )
1937
1929
  );
@@ -1939,11 +1931,11 @@ async function expireOldInvitations() {
1939
1931
  return expiredInvitations.length;
1940
1932
  }
1941
1933
  async function resendInvitation(id10, expiresInDays = 7) {
1942
- const db = getDatabase6();
1934
+ const db = getDatabase7();
1943
1935
  if (!db) {
1944
1936
  throw new Error("[Auth] Database not initialized");
1945
1937
  }
1946
- const invitation = await db.select().from(invitations).where(eq6(invitations.id, id10)).limit(1);
1938
+ const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
1947
1939
  if (invitation.length === 0) {
1948
1940
  throw new Error("Invitation not found");
1949
1941
  }
@@ -1955,31 +1947,34 @@ async function resendInvitation(id10, expiresInDays = 7) {
1955
1947
  status: "pending",
1956
1948
  expiresAt: newExpiresAt,
1957
1949
  updatedAt: /* @__PURE__ */ new Date()
1958
- }).where(eq6(invitations.id, id10)).returning();
1950
+ }).where(eq7(invitations.id, id10)).returning();
1959
1951
  console.log(`[Auth] \u{1F4E7} Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);
1960
1952
  return updated;
1961
1953
  }
1962
1954
 
1963
1955
  // src/server/middleware/authenticate.ts
1956
+ init_jwt();
1964
1957
  init_entities();
1965
- import { findOne as findOne3, getDatabase as getDatabase7 } from "@spfn/core/db";
1958
+ import { findOne as findOne3, getDatabase as getDatabase8 } from "@spfn/core/db";
1966
1959
  import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
1967
- import { eq as eq7, and as and7 } from "drizzle-orm";
1960
+ import { eq as eq8, and as and8 } from "drizzle-orm";
1968
1961
  async function authenticate(c, next) {
1969
1962
  const authHeader = c.req.header("Authorization");
1970
- const keyId = c.req.header("X-Key-Id");
1971
1963
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
1972
1964
  throw new UnauthorizedError2("Missing or invalid authorization header");
1973
1965
  }
1974
- if (!keyId) {
1975
- throw new UnauthorizedError2("Missing X-Key-Id header");
1976
- }
1977
1966
  const token = authHeader.substring(7);
1978
- const db = getDatabase7();
1967
+ const { decodeToken: decodeToken2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
1968
+ const decoded = decodeToken2(token);
1969
+ if (!decoded || !decoded.keyId) {
1970
+ throw new UnauthorizedError2("Invalid token: missing keyId");
1971
+ }
1972
+ const keyId = decoded.keyId;
1973
+ const db = getDatabase8();
1979
1974
  const [keyRecord] = await db.select().from(userPublicKeys).where(
1980
- and7(
1981
- eq7(userPublicKeys.keyId, keyId),
1982
- eq7(userPublicKeys.isActive, true)
1975
+ and8(
1976
+ eq8(userPublicKeys.keyId, keyId),
1977
+ eq8(userPublicKeys.isActive, true)
1983
1978
  )
1984
1979
  );
1985
1980
  if (!keyRecord) {
@@ -2012,7 +2007,7 @@ async function authenticate(c, next) {
2012
2007
  if (user.status !== "active") {
2013
2008
  throw new AccountDisabledError(user.status);
2014
2009
  }
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));
2010
+ 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
2011
  c.set("auth", {
2017
2012
  user,
2018
2013
  userId: String(user.id),
@@ -2078,7 +2073,9 @@ function requireRole(...roleNames) {
2078
2073
  // src/server/setup.ts
2079
2074
  init_entities();
2080
2075
  import { findOne as findOne4, create as create4 } from "@spfn/core/db";
2076
+ import { logger as logger2 } from "@spfn/core/logger";
2081
2077
  init_role_service();
2078
+ var authLogger2 = logger2.child("@spfn/auth");
2082
2079
  function parseAdminAccounts() {
2083
2080
  const accounts = [];
2084
2081
  if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS) {
@@ -2087,12 +2084,12 @@ function parseAdminAccounts() {
2087
2084
  process.env.ADMIN_ACCOUNTS;
2088
2085
  const parsed = JSON.parse(accountsJson);
2089
2086
  if (!Array.isArray(parsed)) {
2090
- console.error("[Auth] \u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
2087
+ authLogger2.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
2091
2088
  return accounts;
2092
2089
  }
2093
2090
  for (const item of parsed) {
2094
2091
  if (!item.email || !item.password) {
2095
- console.warn("[Auth] \u26A0\uFE0F Skipping account: missing email or password");
2092
+ authLogger2.warn("\u26A0\uFE0F Skipping account: missing email or password");
2096
2093
  continue;
2097
2094
  }
2098
2095
  accounts.push({
@@ -2107,7 +2104,7 @@ function parseAdminAccounts() {
2107
2104
  return accounts;
2108
2105
  } catch (error) {
2109
2106
  const err = error;
2110
- console.error("[Auth] \u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err.message);
2107
+ authLogger2.error("\u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err);
2111
2108
  return accounts;
2112
2109
  }
2113
2110
  }
@@ -2122,7 +2119,7 @@ function parseAdminAccounts() {
2122
2119
  process.env.ADMIN_ROLES || // Legacy fallback
2123
2120
  "").split(",").map((s) => s.trim());
2124
2121
  if (passwords.length !== emails.length) {
2125
- console.error("[Auth] \u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
2122
+ authLogger2.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
2126
2123
  return accounts;
2127
2124
  }
2128
2125
  for (let i = 0; i < emails.length; i++) {
@@ -2130,7 +2127,7 @@ function parseAdminAccounts() {
2130
2127
  const password = passwords[i];
2131
2128
  const role = roles2[i] || "user";
2132
2129
  if (!email || !password) {
2133
- console.warn(`[Auth] \u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
2130
+ authLogger2.warn(`\u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
2134
2131
  continue;
2135
2132
  }
2136
2133
  accounts.push({
@@ -2161,22 +2158,23 @@ async function ensureAdminExists() {
2161
2158
  if (accounts.length === 0) {
2162
2159
  return;
2163
2160
  }
2164
- console.log(`[Auth] Creating ${accounts.length} admin account(s)...`);
2161
+ authLogger2.info(`Creating ${accounts.length} admin account(s)...`);
2165
2162
  let created = 0;
2166
2163
  let skipped = 0;
2167
2164
  let failed = 0;
2168
2165
  for (const account of accounts) {
2166
+ authLogger2.info(`Creating ${account.email} admin account(s)...`);
2169
2167
  try {
2170
2168
  const existing = await findOne4(users, { email: account.email });
2171
2169
  if (existing) {
2172
- console.log(`[Auth] \u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
2170
+ authLogger2.info(`\u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
2173
2171
  skipped++;
2174
2172
  continue;
2175
2173
  }
2176
2174
  const roleName = account.role || "user";
2177
2175
  const role = await getRoleByName(roleName);
2178
2176
  if (!role) {
2179
- console.error(`[Auth] \u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
2177
+ authLogger2.error(`\u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
2180
2178
  failed++;
2181
2179
  continue;
2182
2180
  }
@@ -2191,26 +2189,23 @@ async function ensureAdminExists() {
2191
2189
  passwordChangeRequired: account.passwordChangeRequired !== false,
2192
2190
  status: "active"
2193
2191
  });
2194
- console.log(`[Auth] \u2705 Admin account created: ${account.email} (${roleName})`);
2192
+ authLogger2.info(`\u2705 Admin account created: ${account.email} (${roleName})`);
2195
2193
  created++;
2196
2194
  } catch (error) {
2197
2195
  const err = error;
2198
- console.error(`[Auth] \u274C Failed to create account ${account.email}:`, err.message);
2196
+ authLogger2.error(`\u274C Failed to create account ${account.email}:`, err);
2199
2197
  failed++;
2200
2198
  }
2201
2199
  }
2202
- console.log(`[Auth] \u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
2200
+ authLogger2.info(`\u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
2203
2201
  if (created > 0) {
2204
- console.log("[Auth] \u26A0\uFE0F Please change passwords on first login!");
2202
+ authLogger2.info("\u26A0\uFE0F Please change passwords on first login!");
2205
2203
  }
2206
2204
  }
2207
2205
  export {
2208
2206
  BUILTIN_PERMISSIONS,
2209
2207
  BUILTIN_ROLES,
2210
2208
  BUILTIN_ROLE_PERMISSIONS,
2211
- PRESET_PERMISSIONS,
2212
- PRESET_ROLES,
2213
- PRESET_ROLE_PERMISSIONS,
2214
2209
  acceptInvitation,
2215
2210
  addPermissionToRole,
2216
2211
  authenticate,
@@ -2232,6 +2227,7 @@ export {
2232
2227
  getInvitationByToken,
2233
2228
  getInvitationWithDetails,
2234
2229
  getKeyId,
2230
+ getMeService,
2235
2231
  getRoleByName,
2236
2232
  getRolePermissions,
2237
2233
  getUser,