@ibgib/node-gib 0.0.14 → 0.0.17

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 (218) hide show
  1. package/data/identity_index/email/hashes/32/13/49/e64f32730f01a61ada211f29eaf0a210fc2386b668ad04d7e8a9b16766.json +4 -0
  2. package/dist/email/email-helpers.d.mts.map +1 -1
  3. package/dist/email/email-helpers.mjs.map +1 -1
  4. package/dist/email/index.d.mts +3 -0
  5. package/dist/email/index.d.mts.map +1 -1
  6. package/dist/email/index.mjs +3 -0
  7. package/dist/email/index.mjs.map +1 -1
  8. package/dist/email/verify/email-verify-constants.d.mts +3 -0
  9. package/dist/email/verify/email-verify-constants.d.mts.map +1 -0
  10. package/dist/email/verify/email-verify-constants.mjs +3 -0
  11. package/dist/email/verify/email-verify-constants.mjs.map +1 -0
  12. package/dist/email/verify/email-verify-helpers.d.mts +107 -0
  13. package/dist/email/verify/email-verify-helpers.d.mts.map +1 -0
  14. package/dist/email/verify/email-verify-helpers.mjs +357 -0
  15. package/dist/email/verify/email-verify-helpers.mjs.map +1 -0
  16. package/dist/email/verify/email-verify-types.d.mts +41 -0
  17. package/dist/email/verify/email-verify-types.d.mts.map +1 -0
  18. package/dist/email/verify/email-verify-types.mjs +2 -0
  19. package/dist/email/verify/email-verify-types.mjs.map +1 -0
  20. package/dist/identity/sso/custodian-auth-helpers.d.mts +45 -0
  21. package/dist/identity/sso/custodian-auth-helpers.d.mts.map +1 -0
  22. package/dist/identity/sso/custodian-auth-helpers.mjs +108 -0
  23. package/dist/identity/sso/custodian-auth-helpers.mjs.map +1 -0
  24. package/dist/identity/sso/sso-config-helper.d.mts +7 -0
  25. package/dist/identity/sso/sso-config-helper.d.mts.map +1 -0
  26. package/dist/identity/sso/sso-config-helper.mjs +42 -0
  27. package/dist/identity/sso/sso-config-helper.mjs.map +1 -0
  28. package/dist/identity/sso/sso-custodian-service.d.mts +139 -0
  29. package/dist/identity/sso/sso-custodian-service.d.mts.map +1 -0
  30. package/dist/identity/sso/sso-custodian-service.mjs +471 -0
  31. package/dist/identity/sso/sso-custodian-service.mjs.map +1 -0
  32. package/dist/identity/sso/sso-types.d.mts +71 -0
  33. package/dist/identity/sso/sso-types.d.mts.map +1 -0
  34. package/dist/identity/sso/sso-types.mjs +7 -0
  35. package/dist/identity/sso/sso-types.mjs.map +1 -0
  36. package/dist/index.d.mts +5 -1
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/index.mjs +5 -1
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/serve-gib/handlers/api/email/email-outbound.handler.d.mts.map +1 -1
  41. package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs +9 -2
  42. package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs.map +1 -1
  43. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.d.mts +29 -0
  44. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.d.mts.map +1 -0
  45. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.mjs +149 -0
  46. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.mjs.map +1 -0
  47. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.d.mts +24 -0
  48. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.d.mts.map +1 -0
  49. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mjs +212 -0
  50. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mjs.map +1 -0
  51. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.d.mts +37 -0
  52. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.d.mts.map +1 -0
  53. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mjs +192 -0
  54. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mjs.map +1 -0
  55. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.d.mts +33 -0
  56. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.d.mts.map +1 -0
  57. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mjs +173 -0
  58. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mjs.map +1 -0
  59. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.d.mts.map +1 -1
  60. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs +2 -3
  61. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs.map +1 -1
  62. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.d.mts.map +1 -1
  63. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs +38 -3
  64. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs.map +1 -1
  65. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.d.mts.map +1 -1
  66. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs +54 -24
  67. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs.map +1 -1
  68. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.d.mts.map +1 -1
  69. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs +13 -13
  70. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs.map +1 -1
  71. package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs +1 -1
  72. package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs.map +1 -1
  73. package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs +3 -3
  74. package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs.map +1 -1
  75. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.d.mts.map +1 -1
  76. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs +28 -15
  77. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs.map +1 -1
  78. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts +29 -15
  79. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts.map +1 -1
  80. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs +105 -108
  81. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs.map +1 -1
  82. package/dist/serve-gib/identity-index/identity-index-constants.d.mts +3 -2
  83. package/dist/serve-gib/identity-index/identity-index-constants.d.mts.map +1 -1
  84. package/dist/serve-gib/identity-index/identity-index-constants.mjs +4 -2
  85. package/dist/serve-gib/identity-index/identity-index-constants.mjs.map +1 -1
  86. package/dist/serve-gib/identity-index/identity-index-helpers.d.mts.map +1 -1
  87. package/dist/serve-gib/identity-index/identity-index-helpers.mjs +28 -11
  88. package/dist/serve-gib/identity-index/identity-index-helpers.mjs.map +1 -1
  89. package/dist/serve-gib/identity-index/identity-index-service.d.mts +20 -3
  90. package/dist/serve-gib/identity-index/identity-index-service.d.mts.map +1 -1
  91. package/dist/serve-gib/identity-index/identity-index-service.mjs +134 -18
  92. package/dist/serve-gib/identity-index/identity-index-service.mjs.map +1 -1
  93. package/dist/serve-gib/identity-index/identity-index-types.d.mts +4 -2
  94. package/dist/serve-gib/identity-index/identity-index-types.d.mts.map +1 -1
  95. package/dist/serve-gib/index.d.mts +4 -0
  96. package/dist/serve-gib/index.d.mts.map +1 -1
  97. package/dist/serve-gib/index.mjs +12 -0
  98. package/dist/serve-gib/index.mjs.map +1 -1
  99. package/dist/serve-gib/path-constants.d.mts +17 -0
  100. package/dist/serve-gib/path-constants.d.mts.map +1 -1
  101. package/dist/serve-gib/path-constants.mjs +17 -0
  102. package/dist/serve-gib/path-constants.mjs.map +1 -1
  103. package/e2e/identity-custodian-lifecycle.e2e.md +93 -0
  104. package/e2e/identity-sso-lifecycle.e2e.md +132 -0
  105. package/package.json +5 -5
  106. package/src/email/email-helpers.mts +0 -1
  107. package/src/email/index.mts +3 -0
  108. package/src/email/verify/email-verify-constants.mts +3 -0
  109. package/src/email/verify/email-verify-helpers.mts +466 -0
  110. package/src/email/verify/email-verify-types.mts +47 -0
  111. package/src/identity/sso/custodian-auth-helpers.mts +146 -0
  112. package/src/identity/sso/custodian-auth-helpers.respec.mts +97 -0
  113. package/src/identity/sso/sso-config-helper.mts +52 -0
  114. package/src/identity/sso/sso-custodian-service.mts +566 -0
  115. package/src/identity/sso/sso-custodian-service.respec.mts +297 -0
  116. package/src/identity/sso/sso-types.mts +80 -0
  117. package/src/index.mts +14 -0
  118. package/src/serve-gib/handlers/api/email/email-outbound.handler.mts +8 -2
  119. package/src/serve-gib/handlers/api/email/email-verification-code.handler.mts +163 -0
  120. package/src/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mts +231 -0
  121. package/src/serve-gib/handlers/api/email/email-verify.respec.mts +183 -0
  122. package/src/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mts +233 -0
  123. package/src/serve-gib/handlers/api/identity/keystone-custodian-genesis.respec.mts +93 -0
  124. package/src/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mts +210 -0
  125. package/src/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.respec.mts +112 -0
  126. package/src/serve-gib/handlers/api/keystone/keystone-check-email.handler.mts +2 -4
  127. package/src/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +42 -3
  128. package/src/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +46 -23
  129. package/src/serve-gib/handlers/api/keystone/keystone-lookup.handler.mts +11 -15
  130. package/src/serve-gib/handlers/api/keystone/keystone-post.handler.mts +1 -1
  131. package/src/serve-gib/handlers/api/keystone/sso-config.handler.mts +3 -3
  132. package/src/serve-gib/handlers/api/keystone/sso-link.handler.mts +29 -17
  133. package/src/serve-gib/handlers/api/keystone/sso-login.handler.mts +132 -125
  134. package/src/serve-gib/identity-index/identity-index-constants.mts +5 -2
  135. package/src/serve-gib/identity-index/identity-index-helpers.mts +26 -12
  136. package/src/serve-gib/identity-index/identity-index-service.mts +133 -16
  137. package/src/serve-gib/identity-index/identity-index-service.respec.mts +1 -1
  138. package/src/serve-gib/identity-index/identity-index-types.mts +4 -2
  139. package/src/serve-gib/index.mts +12 -0
  140. package/src/serve-gib/path-constants.mts +20 -0
  141. package/tsconfig.json +2 -2
  142. package/tsconfig.test.json +1 -1
  143. package/dist/assumptions.respec.d.mts +0 -2
  144. package/dist/assumptions.respec.d.mts.map +0 -1
  145. package/dist/assumptions.respec.mjs +0 -41
  146. package/dist/assumptions.respec.mjs.map +0 -1
  147. package/dist/email/email-service.respec.d.mts +0 -2
  148. package/dist/email/email-service.respec.d.mts.map +0 -1
  149. package/dist/email/email-service.respec.mjs +0 -115
  150. package/dist/email/email-service.respec.mjs.map +0 -1
  151. package/dist/serve-gib/bootstrap-helper.respec.d.mts +0 -2
  152. package/dist/serve-gib/bootstrap-helper.respec.d.mts.map +0 -1
  153. package/dist/serve-gib/bootstrap-helper.respec.mjs +0 -85
  154. package/dist/serve-gib/bootstrap-helper.respec.mjs.map +0 -1
  155. package/dist/serve-gib/handlers/api/health.respec.d.mts +0 -2
  156. package/dist/serve-gib/handlers/api/health.respec.d.mts.map +0 -1
  157. package/dist/serve-gib/handlers/api/health.respec.mjs +0 -47
  158. package/dist/serve-gib/handlers/api/health.respec.mjs.map +0 -1
  159. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts +0 -2
  160. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts.map +0 -1
  161. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs +0 -98
  162. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs.map +0 -1
  163. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts +0 -2
  164. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts.map +0 -1
  165. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs +0 -131
  166. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs.map +0 -1
  167. package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts +0 -2
  168. package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts.map +0 -1
  169. package/dist/serve-gib/identity-index/identity-index-service.respec.mjs +0 -109
  170. package/dist/serve-gib/identity-index/identity-index-service.respec.mjs.map +0 -1
  171. package/dist/serve-gib/keystone-schema-helpers.respec.d.mts +0 -2
  172. package/dist/serve-gib/keystone-schema-helpers.respec.d.mts.map +0 -1
  173. package/dist/serve-gib/keystone-schema-helpers.respec.mjs +0 -68
  174. package/dist/serve-gib/keystone-schema-helpers.respec.mjs.map +0 -1
  175. package/dist/serve-gib/path-helper.respec.d.mts +0 -2
  176. package/dist/serve-gib/path-helper.respec.d.mts.map +0 -1
  177. package/dist/serve-gib/path-helper.respec.mjs +0 -83
  178. package/dist/serve-gib/path-helper.respec.mjs.map +0 -1
  179. package/dist/serve-gib/serve-gib.respec.d.mts +0 -2
  180. package/dist/serve-gib/serve-gib.respec.d.mts.map +0 -1
  181. package/dist/serve-gib/serve-gib.respec.mjs +0 -85
  182. package/dist/serve-gib/serve-gib.respec.mjs.map +0 -1
  183. package/dist/spec-helper.node.respec.d.mts +0 -13
  184. package/dist/spec-helper.node.respec.d.mts.map +0 -1
  185. package/dist/spec-helper.node.respec.mjs +0 -50
  186. package/dist/spec-helper.node.respec.mjs.map +0 -1
  187. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  188. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  189. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.mjs +0 -52
  190. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  191. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  192. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  193. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.mjs +0 -53
  194. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  195. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  196. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  197. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.mjs +0 -59
  198. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  199. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  200. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  201. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.mjs +0 -55
  202. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  203. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts +0 -2
  204. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts.map +0 -1
  205. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs +0 -67
  206. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs.map +0 -1
  207. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts +0 -2
  208. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts.map +0 -1
  209. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs +0 -67
  210. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs.map +0 -1
  211. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts +0 -2
  212. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts.map +0 -1
  213. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs +0 -68
  214. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs.map +0 -1
  215. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts +0 -2
  216. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts.map +0 -1
  217. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs +0 -69
  218. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs.map +0 -1
@@ -0,0 +1,297 @@
1
+ import { generateKeyPairSync, createSign } from 'node:crypto';
2
+ import {
3
+ respecfully,
4
+ ifWeMight,
5
+ iReckon,
6
+ firstOfAll,
7
+ lastOfAll,
8
+ ifWe
9
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
10
+ import { SsoCustodianService } from './sso-custodian-service.mjs';
11
+ import { SsoServerConfig } from './sso-types.mjs';
12
+ import { POOL_ID_CUSTODIAN_MANAGE, POOL_ID_MANAGE } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
13
+ import { createStandardPoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
14
+
15
+ const maam = `[${import.meta.url}]`, sir = maam;
16
+
17
+ await respecfully(sir, 'SsoCustodianService', async () => {
18
+ let mockPrivateKey: any;
19
+ let mockPublicKeyJwk: any;
20
+ let originalFetch: any;
21
+
22
+ firstOfAll(sir, async () => {
23
+ // Generate mock RSA key pair
24
+ const { privateKey, publicKey } = generateKeyPairSync('rsa', {
25
+ modulusLength: 2048
26
+ });
27
+ mockPrivateKey = privateKey;
28
+
29
+ // Export public key as JWK
30
+ mockPublicKeyJwk = publicKey.export({ format: 'jwk' });
31
+ mockPublicKeyJwk.kid = 'mock-kid-123';
32
+ mockPublicKeyJwk.alg = 'RS256';
33
+ mockPublicKeyJwk.use = 'sig';
34
+
35
+ // Mock global fetch to return JWKS containing the mock public key
36
+ originalFetch = (globalThis as any).fetch;
37
+ (globalThis as any).fetch = async (url: string, options?: any) => {
38
+ if (url === 'https://www.googleapis.com/oauth2/v3/certs') {
39
+ return {
40
+ ok: true,
41
+ status: 200,
42
+ json: async () => ({
43
+ keys: [mockPublicKeyJwk]
44
+ })
45
+ } as any;
46
+ }
47
+ return {
48
+ ok: false,
49
+ status: 404,
50
+ text: async () => 'Not Found'
51
+ } as any;
52
+ };
53
+ });
54
+
55
+ lastOfAll(sir, async () => {
56
+ // Restore global fetch
57
+ if (originalFetch) {
58
+ (globalThis as any).fetch = originalFetch;
59
+ }
60
+ });
61
+
62
+ await ifWe(sir, 'verify dynamic RSA RS256 JWT signature successfully', async () => {
63
+ const config: SsoServerConfig = {
64
+ ssoServerKdfSecret: 'mock-kdf-secret',
65
+ sessionSecret: 'mock-session-secret',
66
+ providers: {
67
+ google: {
68
+ providerId: 'google',
69
+ clientId: 'google-client-id-123',
70
+ clientSecret: 'google-client-secret-123',
71
+ tokenUrl: 'https://oauth2.googleapis.com/token',
72
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
73
+ jwksUrl: 'https://www.googleapis.com/oauth2/v3/certs'
74
+ }
75
+ }
76
+ };
77
+
78
+ const service = new SsoCustodianService(config);
79
+
80
+ // 1. Build JWT Header & Payload
81
+ const header = { alg: 'RS256', kid: 'mock-kid-123', typ: 'JWT' };
82
+ const payload = {
83
+ sub: 'user-sub-999',
84
+ email: 'user@example.com',
85
+ name: 'John Doe',
86
+ aud: 'google-client-id-123',
87
+ iss: 'https://accounts.google.com',
88
+ exp: Math.floor(Date.now() / 1000) + 3600 // expires in 1 hour
89
+ };
90
+
91
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
92
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
93
+ const data = `${headerB64}.${payloadB64}`;
94
+
95
+ // 2. Sign JWT using private key
96
+ const sign = createSign('RSA-SHA256');
97
+ sign.update(data);
98
+ const signatureB64 = sign.sign(mockPrivateKey, 'base64url');
99
+
100
+ const mockJwtToken = `${data}.${signatureB64}`;
101
+
102
+ // 3. Verify signature via service
103
+ const decodedPayload = await service.verifyJwt(mockJwtToken, config.providers.google!);
104
+
105
+ iReckon(sir, decodedPayload.sub).isGonnaBe('user-sub-999');
106
+ iReckon(sir, decodedPayload.email).isGonnaBe('user@example.com');
107
+ iReckon(sir, decodedPayload.name).isGonnaBe('John Doe');
108
+ });
109
+
110
+ await ifWe(sir, 'fail validation if JWT has invalid signature', async () => {
111
+ const config: SsoServerConfig = {
112
+ ssoServerKdfSecret: 'mock-kdf-secret',
113
+ sessionSecret: 'mock-session-secret',
114
+ providers: {
115
+ google: {
116
+ providerId: 'google',
117
+ clientId: 'google-client-id-123',
118
+ clientSecret: 'google-client-secret-123',
119
+ tokenUrl: 'https://oauth2.googleapis.com/token',
120
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
121
+ jwksUrl: 'https://www.googleapis.com/oauth2/v3/certs'
122
+ }
123
+ }
124
+ };
125
+
126
+ const service = new SsoCustodianService(config);
127
+
128
+ const header = { alg: 'RS256', kid: 'mock-kid-123', typ: 'JWT' };
129
+ const payload = {
130
+ sub: 'user-sub-999',
131
+ aud: 'google-client-id-123',
132
+ exp: Math.floor(Date.now() / 1000) + 3600
133
+ };
134
+
135
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
136
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
137
+ const data = `${headerB64}.${payloadB64}`;
138
+
139
+ // Create signature using a different/unregistered RSA key
140
+ const { privateKey: badPrivateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
141
+ const sign = createSign('RSA-SHA256');
142
+ sign.update(data);
143
+ const badSignatureB64 = sign.sign(badPrivateKey, 'base64url');
144
+
145
+ const badJwtToken = `${data}.${badSignatureB64}`;
146
+
147
+ let verifyError: any;
148
+ try {
149
+ await service.verifyJwt(badJwtToken, config.providers.google!);
150
+ } catch (error: any) {
151
+ verifyError = error;
152
+ }
153
+
154
+ iReckon(sir, verifyError).not.isGonnaBeUndefined();
155
+ iReckon(sir, verifyError.message).includes('Cryptographic signature verification failed');
156
+ });
157
+
158
+ await ifWe(sir, 'derive server-delegate secret deterministically', async () => {
159
+ const config: SsoServerConfig = {
160
+ ssoServerKdfSecret: 'high-entropy-server-kdf-secret',
161
+ sessionSecret: 'session-secret-123',
162
+ providers: {}
163
+ };
164
+ const service = new SsoCustodianService(config);
165
+
166
+ const key = 'google:123456789';
167
+ const nonce1 = 'nonce-abc-123';
168
+ const nonce2 = 'nonce-def-456';
169
+
170
+ const secret1_a = await service.deriveServerDelegateSecret(key, nonce1);
171
+ const secret1_b = await service.deriveServerDelegateSecret(key, nonce1);
172
+ const secret2 = await service.deriveServerDelegateSecret(key, nonce2);
173
+
174
+ // Identical inputs must yield identical outputs
175
+ iReckon(sir, secret1_a).isGonnaBe(secret1_b);
176
+ // Different nonces must yield different outputs
177
+ iReckon(sir, secret1_a).not.isGonnaBe(secret2);
178
+ // Derived secrets must be string type with high entropy
179
+ iReckon(sir, typeof secret1_a).isGonnaBe('string');
180
+ iReckon(sir, secret1_a.length).isGonnaBe(128); // sha512 output in hex/base64 wrap
181
+ });
182
+
183
+ await ifWe(sir, 'create custodian challenge pool successfully', async () => {
184
+ const config: SsoServerConfig = {
185
+ ssoServerKdfSecret: 'high-entropy-server-kdf-secret',
186
+ sessionSecret: 'session-secret-123',
187
+ providers: {}
188
+ };
189
+ const service = new SsoCustodianService(config);
190
+
191
+ const key = 'google:123456789';
192
+ const nonce = 'nonce-abc-123';
193
+
194
+ // 1. Create a mock target keystone containing a 'manage' pool
195
+ const userManageConfig = createStandardPoolConfig({
196
+ id: POOL_ID_MANAGE,
197
+ salt: 'user-manage-salt'
198
+ });
199
+ userManageConfig.behavior.size = 12; // custom size for verification
200
+ const mockTargetKeystone = {
201
+ ib: 'keystone^some-tjp-addr',
202
+ gib: 'some-gib-value',
203
+ data: {
204
+ challengePools: [
205
+ {
206
+ id: 'manage',
207
+ config: userManageConfig,
208
+ challenges: { 'chal1': 'val1' }
209
+ }
210
+ ],
211
+ n: 0
212
+ }
213
+ };
214
+
215
+ // 2. Mock metaspace and space calls
216
+ const mockSpace = {};
217
+ const mockMetaspace = {
218
+ getLatestAddr: async () => 'latest-keystone-addr',
219
+ get: async () => ({
220
+ success: true,
221
+ ibGibs: [mockTargetKeystone]
222
+ })
223
+ };
224
+
225
+ const custodianPool = await service.createCustodianChallengePool({
226
+ providerId: 'google',
227
+ providerKey: key,
228
+ userNonce: nonce,
229
+ keystoneAddr: 'some-tjp-addr',
230
+ metaspace: mockMetaspace as any,
231
+ space: mockSpace as any
232
+ });
233
+
234
+ iReckon(sir, custodianPool).not.isGonnaBeUndefined();
235
+ iReckon(sir, custodianPool.id).willEqual('custodian-manage-google');
236
+ iReckon(sir, custodianPool.isForeign).isGonnaBeTrue();
237
+ iReckon(sir, custodianPool.metadata?.providerKey).willEqual(key);
238
+
239
+ // Replicated behavior size from parent manage pool config
240
+ iReckon(sir, custodianPool.config.behavior.size).willEqual(userManageConfig.behavior.size);
241
+
242
+ // Structural validation of challenges
243
+ const challengesCount = Object.keys(custodianPool.challenges).length;
244
+ iReckon(sir, challengesCount).willEqual(custodianPool.config.behavior.size);
245
+ });
246
+
247
+ await ifWe(sir, 'revoke tokens successfully, making the correct http fetch calls', async () => {
248
+ const config: SsoServerConfig = {
249
+ ssoServerKdfSecret: 'high-entropy-server-kdf-secret',
250
+ sessionSecret: 'session-secret-123',
251
+ providers: {
252
+ google: {
253
+ providerId: 'google',
254
+ clientId: 'google-client-id-abc',
255
+ clientSecret: 'google-client-secret-abc',
256
+ tokenUrl: 'https://oauth2.googleapis.com/token',
257
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo'
258
+ },
259
+ github: {
260
+ providerId: 'github',
261
+ clientId: 'github-client-id-xyz',
262
+ clientSecret: 'github-client-secret-xyz',
263
+ tokenUrl: 'https://github.com/login/oauth/access_token',
264
+ userInfoUrl: 'https://api.github.com/user'
265
+ }
266
+ }
267
+ };
268
+ const service = new SsoCustodianService(config);
269
+
270
+ const originalFetch = globalThis.fetch;
271
+ let fetchCalls: { url: string, options: any }[] = [];
272
+ globalThis.fetch = (async (url: any, options: any) => {
273
+ fetchCalls.push({ url, options });
274
+ return {
275
+ ok: true,
276
+ status: 200,
277
+ text: async () => 'ok'
278
+ } as any;
279
+ }) as any;
280
+
281
+ try {
282
+ // Revoke Google token
283
+ await service.revokeToken('google', 'google-test-token-123');
284
+ iReckon(sir, fetchCalls.length).willEqual(1);
285
+ iReckon(sir, fetchCalls[0].url).includes('google-test-token-123');
286
+
287
+ // Revoke GitHub token
288
+ await service.revokeToken('github', 'github-test-token-456');
289
+ iReckon(sir, fetchCalls.length).willEqual(2);
290
+ iReckon(sir, fetchCalls[1].url).includes('/applications/github-client-id-xyz/grant');
291
+ iReckon(sir, fetchCalls[1].options.method).willEqual('DELETE');
292
+ iReckon(sir, fetchCalls[1].options.headers['Authorization']).includes('Basic');
293
+ } finally {
294
+ globalThis.fetch = originalFetch;
295
+ }
296
+ });
297
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @module identity/sso/sso-types
3
+ *
4
+ * Core types and interfaces for extensible SSO (OAuth2) authentication on the server.
5
+ */
6
+
7
+ export type SsoProviderId = 'google' | 'github';
8
+
9
+ export interface SsoProviderClientConfig {
10
+ providerId: SsoProviderId;
11
+ clientId: string;
12
+ authUrl: string;
13
+ redirectUri: string;
14
+ scope: string;
15
+ }
16
+
17
+ export interface SsoProviderServerConfig {
18
+ providerId: SsoProviderId;
19
+ clientId: string;
20
+ clientSecret: string;
21
+ tokenUrl: string;
22
+ userInfoUrl: string;
23
+ jwksUrl?: string;
24
+ }
25
+
26
+ export interface SsoServerConfig {
27
+ /** Server's private KDF secret used to derive server-delegate keystones. */
28
+ ssoServerKdfSecret: string;
29
+ /** Server's private session encryption secret. */
30
+ sessionSecret: string;
31
+ /** Map of active OAuth2 providers. */
32
+ providers: Partial<Record<SsoProviderId, SsoProviderServerConfig>>;
33
+ }
34
+
35
+ export interface OAuthUserInfo {
36
+ /** Unique identity key combining provider and sub ID (e.g., `google:107632...`). */
37
+ providerKey: string;
38
+ providerId: SsoProviderId;
39
+ /** Unique user ID within the provider. */
40
+ sub: string;
41
+ email?: string;
42
+ name?: string;
43
+ picture?: string;
44
+ }
45
+
46
+ /**
47
+ * Single-turn authorization requirements object issued by the custodian server
48
+ * after successful passwordless SSO/Email authentication.
49
+ *
50
+ * Placed in `delegateKeystone.data.frameDetails.custodianReqs` when registering
51
+ * a client delegate keystone via passwordless onboarding.
52
+ */
53
+ export interface CustodianRequirements {
54
+ /**
55
+ * The primary identity keystone TJP address (`ib^gib`) to which the delegate keystone will be registered.
56
+ * Prevents Man-In-The-Middle reuse of authorization requirements across different primary identities.
57
+ */
58
+ primaryTjpAddr: string;
59
+
60
+ /**
61
+ * High-entropy, single-turn random nonce generated by the server.
62
+ * Prevents replay attacks when registering delegate keystones.
63
+ */
64
+ nonce: string;
65
+
66
+ /**
67
+ * Single-turn authorization code computed by the server over `Omit<CustodianRequirements, 'authCode'>`.
68
+ */
69
+ authCode: string;
70
+
71
+ /**
72
+ * Expiration timestamp in epoch milliseconds after which this requirements object is invalid.
73
+ */
74
+ expiresAt: number;
75
+
76
+ /**
77
+ * Explicit list of permitted keystone verbs allowed for the delegate keystone (e.g., `['sync', 'connect']`).
78
+ */
79
+ allowedVerbs: string[];
80
+ }
package/src/index.mts CHANGED
@@ -37,5 +37,19 @@ export {
37
37
  EmailSenderAws,
38
38
  EmailReceiverAws,
39
39
  EmailSenderMock,
40
+ createMagicLinkToken,
41
+ verifyMagicLinkToken,
42
+ markNonceUsed,
43
+ isNonceUsed,
44
+ clearConsumedNonces,
45
+ DEFAULT_MAGIC_LINK_TTL_MS,
46
+ getMagicLinkSecret,
40
47
  } from './email/index.mjs';
41
48
  export type * from './email/email-types.mjs';
49
+
50
+ // identity & SSO custodian infrastructure
51
+ export * from './identity/sso/sso-custodian-service.mjs';
52
+ export * from './identity/sso/sso-config-helper.mjs';
53
+ export * from './identity/sso/custodian-auth-helpers.mjs';
54
+ export type * from './identity/sso/sso-types.mjs';
55
+
@@ -33,15 +33,21 @@ export class EmailOutboundHandler extends ServeGibHandlerBase {
33
33
  if (options.sender) {
34
34
  this.sender = options.sender;
35
35
  } else {
36
- const mode = process.env.EMAIL_MODE || 'mock';
36
+ const isProd = process.env.NODE_ENV === 'production';
37
+ const mode = (process.env.EMAIL_MODE || (isProd ? 'aws' : 'mock')).toLowerCase();
37
38
  if (mode === 'aws') {
38
39
  this.sender = new EmailSenderAws({
39
40
  accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
40
41
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
41
42
  region: process.env.AWS_REGION || 'us-east-1',
42
43
  });
43
- } else {
44
+ } else if (mode === 'mock') {
45
+ if (isProd) {
46
+ throw new Error('EMAIL_MODE=mock is prohibited when NODE_ENV=production. (E: 897a6b5c4d3e2f10987654321fedcba0)');
47
+ }
44
48
  this.sender = new EmailSenderMock();
49
+ } else {
50
+ throw new Error(`Unsupported EMAIL_MODE '${mode}'. Must be 'aws' or 'mock'. (E: 987a6b5c4d3e2f10987654321fedcba1)`);
45
51
  }
46
52
  }
47
53
  }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @module serve-gib/handlers/api/email/email-verification-code
3
+ *
4
+ * Handler for POST /api/email/verification-code
5
+ */
6
+
7
+ import { extractErrorMsg, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
8
+ import { EmailSenderAws } from '../../../../email/aws/email-sender-aws.mjs';
9
+ // import { generateVerificationCode, getPendingTjpHash, savePendingCodeRecord, getPendingStagingDir, validateEmail } from '../../../../email/index.mjs';
10
+ import { EmailSender, OutboundEmail } from '../../../../email/email-types.mjs';
11
+ import { EmailSenderMock } from '../../../../email/mock/email-sender-mock.mjs';
12
+ import { API_PATH_REGEXES } from '../../../path-constants.mjs';
13
+ import { RequestContext, ResponseResult, ServeGibHttpMethod } from '../../../types.mjs';
14
+ import { ServeGibHandlerBase } from '../../handler-base.mjs';
15
+ import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
16
+ import { generateVerificationCode, getPendingStagingDir, getPendingTjpHash, savePendingCodeRecord, savePendingStagingKeystone } from '../../../../email/verify/email-verify-helpers.mjs';
17
+ import { validateEmail } from '../../../../email/email-helpers.mjs';
18
+ import { normalizeEmail } from '../../../identity-index/identity-index-helpers.mjs';
19
+
20
+ const logalot = GLOBAL_LOG_A_LOT || true;
21
+
22
+ export interface EmailMagicLinkHandlerOptions {
23
+ sender?: EmailSender;
24
+ }
25
+
26
+ export interface EmailMagicLinkRequestBody {
27
+ email: string;
28
+ primaryTjpAddr?: string;
29
+ keystone?: any;
30
+ }
31
+
32
+ /**
33
+ * POST /api/email/verification-code
34
+ * Generates a 6-digit verification code, saves it to pending staging, and dispatches email.
35
+ */
36
+ export class EmailVerificationCodeHandler extends ServeGibHandlerBase {
37
+ protected override lc = `[${EmailVerificationCodeHandler.name}]`;
38
+ protected override method: ServeGibHttpMethod = 'POST';
39
+ protected override regex = API_PATH_REGEXES.EMAIL_VERIFICATION_CODE;
40
+
41
+ private sender: EmailSender;
42
+
43
+ constructor(options: EmailMagicLinkHandlerOptions = {}) {
44
+ super();
45
+
46
+ if (options.sender) {
47
+ this.sender = options.sender;
48
+ } else {
49
+ const isProd = process.env.NODE_ENV === 'production';
50
+ const mode = (process.env.EMAIL_MODE || (isProd ? 'aws' : 'mock')).toLowerCase();
51
+ if (mode === 'aws') {
52
+ this.sender = new EmailSenderAws({
53
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
54
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
55
+ region: process.env.AWS_REGION || 'us-east-1',
56
+ });
57
+ } else if (mode === 'mock') {
58
+ if (isProd) {
59
+ throw new Error('EMAIL_MODE=mock is prohibited when NODE_ENV=production. (E: 897a6b5c4d3e2f10987654321fedcba0)');
60
+ }
61
+ this.sender = new EmailSenderMock();
62
+ } else {
63
+ throw new Error(`Unsupported EMAIL_MODE '${mode}'. Must be 'aws' or 'mock'. (E: 987a6b5c4d3e2f10987654321fedcba1)`);
64
+ }
65
+ }
66
+ }
67
+
68
+ protected async handleRouteImpl(reqCtx: RequestContext): Promise<ResponseResult | undefined> {
69
+ const lc = `${this.lc}[${this.handleRouteImpl.name}]`;
70
+ const timestampUUID = `${lc}[${(await getUUID()).substring(0, 8)}]`;
71
+ try {
72
+ if (logalot) { console.log(`${lc} starting... (I: f123456789abcdef0123456789a0001)`); }
73
+ if (logalot) { console.time(timestampUUID); }
74
+
75
+ if (reqCtx.method !== 'POST') {
76
+ return this.error(405, 'Method Not Allowed');
77
+ }
78
+
79
+ if (!reqCtx.body) {
80
+ return this.error(400, 'Request body is empty (E: c1d2e3f4a5b67890123456789abcdef0)');
81
+ }
82
+
83
+ let body: EmailMagicLinkRequestBody;
84
+ try {
85
+ body = JSON.parse(reqCtx.body);
86
+ } catch {
87
+ return this.error(400, 'Request body must be valid JSON (E: d2e3f4a5b67890123456789abcdef01)');
88
+ }
89
+
90
+ if (logalot) { console.timeLog(timestampUUID, 'parse complete'); }
91
+
92
+ const { email, primaryTjpAddr } = body;
93
+
94
+ if (!email || !validateEmail(email)) {
95
+ return this.error(400, 'Invalid email address format (E: e3f4a5b67890123456789abcdef012)');
96
+ }
97
+
98
+ const normEmail = normalizeEmail(email);
99
+ const effectivePrimaryTjpAddr = primaryTjpAddr || `pending:${normEmail}`;
100
+ const tjpHash = await getPendingTjpHash(effectivePrimaryTjpAddr);
101
+
102
+ const code = generateVerificationCode();
103
+ if (reqCtx.dataDir && tjpHash) {
104
+ let info = await getPendingStagingDir({ dataDir: reqCtx.dataDir, tjpHash });
105
+ if (!info) {
106
+ // Create pending staging directory for this email if not yet present
107
+ await savePendingStagingKeystone({
108
+ dataDir: reqCtx.dataDir,
109
+ primaryTjpAddr: effectivePrimaryTjpAddr,
110
+ emails: [normEmail],
111
+ });
112
+ info = await getPendingStagingDir({ dataDir: reqCtx.dataDir, tjpHash });
113
+ }
114
+
115
+ if (info) {
116
+ await savePendingCodeRecord({ pendingDir: info.pendingDir, code });
117
+ if (logalot) { console.timeLog(timestampUUID, 'code record saved to pending staging'); }
118
+ } else {
119
+ console.warn(`${lc} Failed to create or access pending staging directory for tjpHash ${tjpHash}`);
120
+ }
121
+ }
122
+
123
+ const outboundEmail: OutboundEmail = {
124
+ from: process.env.AWS_SES_FROM_EMAIL || 'noreply@ibgib.com',
125
+ to: [email],
126
+ subject: 'Verify your Ibgib Identity Email',
127
+ textBody: `Hello,\n\nYour 6-digit verification code for your Ibgib identity is:\n\n${code}\n\nThis code will expire in 15 minutes.\n\nIf you did not request this, please ignore this email.`,
128
+ htmlBody: `<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px;">
129
+ <h2>Verify your Ibgib Identity Email</h2>
130
+ <p>Your 6-digit verification code is:</p>
131
+ <h1 style="font-family: monospace; font-size: 36px; letter-spacing: 6px; color: #4f46e5; margin: 24px 0;">${code}</h1>
132
+ <p style="font-size: 12px; color: #999;">This code will expire in 15 minutes.</p>
133
+ </div>`,
134
+ };
135
+
136
+ const sendResult = await this.sender.sendEmail(outboundEmail);
137
+ if (logalot) { console.timeLog(timestampUUID, 'email send complete'); }
138
+
139
+ if (sendResult.success) {
140
+ console.log(`${lc} Verification code dispatched for ${email} -> ${primaryTjpAddr} msgId: ${sendResult.messageId} (I: a5b67890123456789abcdef012345678)`);
141
+ const isProd = process.env.NODE_ENV === 'production';
142
+ const mode = (process.env.EMAIL_MODE || (isProd ? 'aws' : 'mock')).toLowerCase();
143
+ if (mode === 'mock' || !isProd) {
144
+ console.log(`${lc} ✉️ [EmailSenderMock] Verification Code for ${email}: ${code} (I: c123456789abcdef0123456789abcdef)`);
145
+ }
146
+ return this.ok({ success: true, message: 'Verification code dispatched successfully.' });
147
+ } else {
148
+ console.error(`${lc} Failed to send magic link email: ${sendResult.error} (E: 5b67890123456789abcdef0123456789)`);
149
+ return this.error(sendResult.statusCode || 500, sendResult.error || 'Failed to send magic link email');
150
+ }
151
+
152
+ } catch (error) {
153
+ const emsg = extractErrorMsg(error);
154
+ console.error(`${lc} Magic link dispatch error: ${emsg} (E: 67890123456789abcdef0123456789a)`);
155
+ return this.error(500, `Magic link dispatch error: ${emsg}`);
156
+ } finally {
157
+ if (logalot) {
158
+ console.log(`${lc} complete.`);
159
+ console.timeEnd(timestampUUID);
160
+ }
161
+ }
162
+ }
163
+ }