@betterportal/auth-default 10.6.20 → 10.6.22

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 (128) hide show
  1. package/README.md +140 -10
  2. package/bsb-plugin.json +1 -1
  3. package/lib/account.d.ts +16 -0
  4. package/lib/account.d.ts.map +1 -0
  5. package/lib/account.js +274 -0
  6. package/lib/account.js.map +1 -0
  7. package/lib/accountUI.d.ts +5 -0
  8. package/lib/accountUI.d.ts.map +1 -0
  9. package/lib/accountUI.js +68 -0
  10. package/lib/accountUI.js.map +1 -0
  11. package/lib/bp-contracts/org.betterportal.auth.default.json +2031 -707
  12. package/lib/clientAddress.d.ts +10 -0
  13. package/lib/clientAddress.d.ts.map +1 -0
  14. package/lib/clientAddress.js +35 -0
  15. package/lib/clientAddress.js.map +1 -0
  16. package/lib/config.d.ts +3 -0
  17. package/lib/config.d.ts.map +1 -0
  18. package/lib/config.js +28 -0
  19. package/lib/config.js.map +1 -0
  20. package/lib/defaultAuthManifest.d.ts +7 -0
  21. package/lib/defaultAuthManifest.d.ts.map +1 -1
  22. package/lib/factors.d.ts +18 -0
  23. package/lib/factors.d.ts.map +1 -0
  24. package/lib/factors.js +95 -0
  25. package/lib/factors.js.map +1 -0
  26. package/lib/identity.d.ts +106 -0
  27. package/lib/identity.d.ts.map +1 -0
  28. package/lib/identity.js +315 -0
  29. package/lib/identity.js.map +1 -0
  30. package/lib/index.d.ts +4 -0
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +4 -0
  33. package/lib/index.js.map +1 -1
  34. package/lib/mail.d.ts +19 -0
  35. package/lib/mail.d.ts.map +1 -0
  36. package/lib/mail.js +102 -0
  37. package/lib/mail.js.map +1 -0
  38. package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts +435 -11
  39. package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts.map +1 -1
  40. package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js +85 -0
  41. package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js.map +1 -1
  42. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.d.ts +34 -0
  43. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.d.ts.map +1 -0
  44. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.js +25 -0
  45. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.js.map +1 -0
  46. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.d.ts +42 -0
  47. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.d.ts.map +1 -0
  48. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.js +27 -0
  49. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.js.map +1 -0
  50. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.d.ts +2 -0
  51. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.d.ts.map +1 -0
  52. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.js +2 -0
  53. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.js.map +1 -0
  54. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.d.ts +2 -0
  55. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.d.ts.map +1 -0
  56. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.js +2 -0
  57. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.js.map +1 -0
  58. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.d.ts +3 -0
  59. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.d.ts.map +1 -0
  60. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.js +3 -0
  61. package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.js.map +1 -0
  62. package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.d.ts.map +1 -1
  63. package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.js +11 -8
  64. package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.js.map +1 -1
  65. package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.d.ts +3 -1
  66. package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.d.ts.map +1 -1
  67. package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.js +10 -23
  68. package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.js.map +1 -1
  69. package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.d.ts.map +1 -1
  70. package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.js +2 -2
  71. package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.js.map +1 -1
  72. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.d.ts +34 -0
  73. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.d.ts.map +1 -0
  74. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.js +26 -0
  75. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.js.map +1 -0
  76. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.d.ts +31 -0
  77. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.d.ts.map +1 -0
  78. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.js +23 -0
  79. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.js.map +1 -0
  80. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.d.ts +3 -0
  81. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.d.ts.map +1 -0
  82. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.js +36 -0
  83. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.js.map +1 -0
  84. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.d.ts +3 -0
  85. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.d.ts.map +1 -0
  86. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.js +3 -0
  87. package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.js.map +1 -0
  88. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.d.ts +32 -0
  89. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.d.ts.map +1 -0
  90. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.js +39 -0
  91. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.js.map +1 -0
  92. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.d.ts +41 -0
  93. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.d.ts.map +1 -0
  94. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.js +103 -0
  95. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.js.map +1 -0
  96. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.d.ts +3 -0
  97. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.d.ts.map +1 -0
  98. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.js +19 -0
  99. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.js.map +1 -0
  100. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.d.ts +3 -0
  101. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.d.ts.map +1 -0
  102. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.js +3 -0
  103. package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.js.map +1 -0
  104. package/lib/plugins/service-betterportal-auth-default/index.d.ts +41 -4
  105. package/lib/plugins/service-betterportal-auth-default/index.d.ts.map +1 -1
  106. package/lib/plugins/service-betterportal-auth-default/index.js +205 -9
  107. package/lib/plugins/service-betterportal-auth-default/index.js.map +1 -1
  108. package/lib/plugins/service-betterportal-auth-default/loginFlow.d.ts +17 -2
  109. package/lib/plugins/service-betterportal-auth-default/loginFlow.d.ts.map +1 -1
  110. package/lib/plugins/service-betterportal-auth-default/loginFlow.js +83 -62
  111. package/lib/plugins/service-betterportal-auth-default/loginFlow.js.map +1 -1
  112. package/lib/plugins/service-betterportal-auth-default/logoutFlow.js +1 -1
  113. package/lib/plugins/service-betterportal-auth-default/logoutFlow.js.map +1 -1
  114. package/lib/plugins/service-betterportal-auth-default/registrationFlow.d.ts +4 -2
  115. package/lib/plugins/service-betterportal-auth-default/registrationFlow.d.ts.map +1 -1
  116. package/lib/plugins/service-betterportal-auth-default/registrationFlow.js +15 -17
  117. package/lib/plugins/service-betterportal-auth-default/registrationFlow.js.map +1 -1
  118. package/lib/schemas/service-betterportal-auth-default.json +26 -2
  119. package/lib/schemas/service-betterportal-auth-default.plugin.json +27 -3
  120. package/lib/social.d.ts +40 -0
  121. package/lib/social.d.ts.map +1 -0
  122. package/lib/social.js +178 -0
  123. package/lib/social.js.map +1 -0
  124. package/lib/storage.d.ts +72 -0
  125. package/lib/storage.d.ts.map +1 -0
  126. package/lib/storage.js +427 -0
  127. package/lib/storage.js.map +1 -0
  128. package/package.json +14 -7
@@ -0,0 +1,10 @@
1
+ import { type BetterPortalEvent } from "@betterportal/framework/lib/runtime/h3.js";
2
+ interface ProxyTrust {
3
+ trustedProxyHeaders?: boolean;
4
+ cfProxy?: boolean;
5
+ trustedProxyIps?: string[];
6
+ }
7
+ /** Forwarding headers are authoritative only through explicitly trusted socket peers. */
8
+ export declare function clientAddress(event: BetterPortalEvent, trust: ProxyTrust): string | undefined;
9
+ export {};
10
+ //# sourceMappingURL=clientAddress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientAddress.d.ts","sourceRoot":"","sources":["../src/clientAddress.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAEnG,UAAU,UAAU;IAAG,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;AAOrG,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAiB7F"}
@@ -0,0 +1,35 @@
1
+ import { isIP } from "node:net";
2
+ import { getEventPeerIp } from "@betterportal/framework/lib/runtime/h3.js";
3
+ function normalizeIp(value) {
4
+ if (!value || !isIP(value) || value.includes("%"))
5
+ return undefined;
6
+ if (isIP(value) === 4)
7
+ return value;
8
+ return new URL(`http://[${value}]`).hostname.slice(1, -1);
9
+ }
10
+ /** Forwarding headers are authoritative only through explicitly trusted socket peers. */
11
+ export function clientAddress(event, trust) {
12
+ const peer = normalizeIp(getEventPeerIp(event));
13
+ const trusted = new Set((trust.trustedProxyIps ?? []).map(normalizeIp).filter(Boolean));
14
+ if (!peer || !trusted.has(peer))
15
+ return peer;
16
+ if (trust.cfProxy) {
17
+ const cloudflare = normalizeIp(event.req.headers.get("cf-connecting-ip")?.trim());
18
+ if (cloudflare)
19
+ return cloudflare;
20
+ }
21
+ if (!trust.trustedProxyHeaders)
22
+ return peer;
23
+ const forwarded = event.req.headers.get("x-forwarded-for");
24
+ if (!forwarded || forwarded.length > 4096)
25
+ return peer;
26
+ const hops = forwarded.split(",").map(value => normalizeIp(value.trim()));
27
+ if (hops.length > 32 || hops.some(hop => !hop))
28
+ return peer;
29
+ // Stop at the nearest untrusted hop: addresses to its left can be supplied by that client.
30
+ let address = peer;
31
+ for (let i = hops.length - 1; i >= 0 && trusted.has(address); i--)
32
+ address = hops[i];
33
+ return address;
34
+ }
35
+ //# sourceMappingURL=clientAddress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientAddress.js","sourceRoot":"","sources":["../src/clientAddress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,cAAc,EAA0B,MAAM,2CAA2C,CAAC;AAGnG,SAAS,WAAW,CAAC,KAAyB;IAC5C,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACpE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,IAAI,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,KAAwB,EAAE,KAAiB;IACvE,MAAM,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACxF,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,IAAI,UAAU;YAAE,OAAO,UAAU,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,mBAAmB;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,IAAI;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1E,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,2FAA2F;IAC3F,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;IACtF,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ConfigSchemaDescriptor } from "@betterportal/framework";
2
+ export declare const AuthConfigSchemas: ConfigSchemaDescriptor[];
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,eAAO,MAAM,iBAAiB,EAAE,sBAAsB,EA0BrD,CAAC"}
package/lib/config.js ADDED
@@ -0,0 +1,28 @@
1
+ export const AuthConfigSchemas = [
2
+ {
3
+ id: "default-auth.tenant", title: "Account isolation", scope: "tenant",
4
+ description: "Account directory boundaries. Locked permanently when the first user is created.",
5
+ jsonSchema: { userIsolation: "string", directoryAdminAppId: "string" },
6
+ fields: [
7
+ { key: "userIsolation", title: "User isolation", description: "Account directory boundary; cannot change after user creation.", scope: "tenant", visibility: "protected", ownership: "bp", sourceOfTruth: "bp", required: false, defaultValue: "app", ui: { control: "select", options: [{ value: "app", label: "Separate accounts per app" }, { value: "tenant", label: "Shared accounts in this tenant" }] } },
8
+ { key: "directoryAdminAppId", title: "Directory administration app", description: "For shared tenant accounts, the app whose authorized administrators may disable accounts, revoke all sessions and change shared groups. Must be an app in this tenant.", scope: "tenant", visibility: "protected", ownership: "bp", sourceOfTruth: "bp", required: false }
9
+ ]
10
+ },
11
+ {
12
+ id: "default-auth.app", title: "Default authentication", scope: "app",
13
+ description: "Registration, first-login roles, MFA, email delivery and social connections for this app.",
14
+ jsonSchema: { registration: "string", defaultRoleIds: "string", requireMfa: "boolean", mailTransport: "string", mailUrl: "string", mailFrom: "string", mailApiKey: "string", mailHeaders: "string", socialConnections: "string" },
15
+ fields: [
16
+ { key: "registration", title: "Registration", defaultValue: "invite-only", ui: { control: "select", options: [{ value: "invite-only", label: "Invitations only" }, { value: "public", label: "Public registration" }, { value: "closed", label: "Closed" }] } },
17
+ { key: "defaultRoleIds", title: "Default role IDs", description: "JSON array of existing app role IDs. Applied once at first completed login. Empty by default.", defaultValue: "[]" },
18
+ { key: "requireMfa", title: "Require multi-factor authentication", defaultValue: false },
19
+ { key: "mailTransport", title: "Email delivery", ui: { control: "select", options: [{ value: "postal", label: "Postal HTTP API" }, { value: "http", label: "Custom HTTP API" }] } },
20
+ { key: "mailUrl", title: "Email API URL" },
21
+ { key: "mailFrom", title: "Sender email" },
22
+ { key: "mailApiKey", title: "Postal API key", visibility: "secret" },
23
+ { key: "mailHeaders", title: "Custom HTTP headers", description: "JSON object of server-side headers.", visibility: "secret" },
24
+ { key: "socialConnections", title: "Social connections", description: "JSON array of Google, Microsoft or GitHub connections. Each app requires its own registered redirect URL.", visibility: "secret" }
25
+ ].map(field => ({ description: field.title, scope: "app", visibility: "protected", ownership: "bp", sourceOfTruth: "bp", required: false, ...field }))
26
+ }
27
+ ];
28
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,13 @@ export declare const RuntimeJwtClaimsContract: av.ObjectSchema<{
27
27
  tokenType: av.EnumSchema<readonly ["access", "refresh", "cp-envelope", "setup", "install"]>;
28
28
  authProvider: av.OptionalSchema<av.StringSchema>;
29
29
  refreshContext: av.OptionalSchema<av.BaseSchema<unknown, import("@betterportal/framework").JsonObject>>;
30
+ elevation: av.OptionalSchema<av.ObjectSchema<{
31
+ assurance: av.EnumSchema<readonly ["confirmed", "mfa"]>;
32
+ verifiedAt: av.IntSchema;
33
+ expiresAt: av.IntSchema;
34
+ }>>;
35
+ sessionId: av.OptionalSchema<av.StringSchema>;
36
+ sessionVersion: av.OptionalSchema<av.IntSchema>;
30
37
  providerSubject: av.OptionalSchema<av.StringSchema>;
31
38
  provider: av.OptionalSchema<av.ObjectSchema<{
32
39
  username: av.OptionalSchema<av.StringSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"defaultAuthManifest.d.ts","sourceRoot":"","sources":["../src/defaultAuthManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAGhB,cAAc,EACd,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAEjC,eAAO,MAAM,wBAAwB;;;;;EAKnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAExE,eAAO,MAAM,2BAA2B,EAAE,SAAS,gBAAgB,EASlE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,SAAS,wBAAwB,EAarE,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS,kBAAkB,EAS7D,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,cAsBhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;EAAkB,CAAC"}
1
+ {"version":3,"file":"defaultAuthManifest.d.ts","sourceRoot":"","sources":["../src/defaultAuthManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAGhB,cAAc,EACd,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAEjC,eAAO,MAAM,wBAAwB;;;;;EAKnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAExE,eAAO,MAAM,2BAA2B,EAAE,SAAS,gBAAgB,EASlE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,SAAS,wBAAwB,EAarE,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS,kBAAkB,EAS7D,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,cAsBhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAkB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type IdentityService, type User } from "./identity.js";
2
+ import type { AuthTransaction, Scope } from "./storage.js";
3
+ export interface FactorRequest {
4
+ method?: string;
5
+ code?: string;
6
+ credential?: unknown;
7
+ }
8
+ export declare class Factors {
9
+ private readonly identity;
10
+ constructor(identity: IdentityService);
11
+ hasFactors(user: User): Promise<boolean>;
12
+ prepare(user: User, origin: string, enroll?: boolean): Promise<{
13
+ public: Record<string, unknown>;
14
+ private: Record<string, unknown>;
15
+ }>;
16
+ complete(tx: AuthTransaction, scope: Scope, user: User, data: Record<string, unknown>, input: FactorRequest): Promise<string[] | undefined>;
17
+ }
18
+ //# sourceMappingURL=factors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factors.d.ts","sourceRoot":"","sources":["../src/factors.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,eAAe,EAAE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAe,KAAK,EAAE,MAAM,cAAc,CAAC;AAGxE,MAAM,WAAW,aAAa;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE;AACvF,qBAAa,OAAO;IACN,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IAgBnI,QAAQ,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC;CA0ClJ"}
package/lib/factors.js ADDED
@@ -0,0 +1,95 @@
1
+ import { generateSecret, generateURI, verify } from "otplib";
2
+ import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse } from "@simplewebauthn/server";
3
+ import { AuthError, newSecret, secretHash } from "./identity.js";
4
+ export class Factors {
5
+ identity;
6
+ constructor(identity) {
7
+ this.identity = identity;
8
+ }
9
+ // This is account-wide: an unusable passkey must not turn MFA into password-only login.
10
+ async hasFactors(user) {
11
+ if (user.totp)
12
+ return true;
13
+ return this.identity.storage.transaction(user, async (tx) => (await tx.list("passkey", user)).some(p => p.userId === user.id));
14
+ }
15
+ async prepare(user, origin, enroll = false) {
16
+ const rpId = new URL(origin).hostname;
17
+ const keys = await this.identity.storage.transaction(user, async (tx) => (await tx.list("passkey", user)).filter(p => p.userId === user.id && p.rpId === rpId));
18
+ const methods = enroll ? ["totp", "passkey"] : [...(user.totp ? ["totp"] : []), ...(keys.length ? ["passkey"] : []), ...(user.recoveryCodes?.length ? ["recovery"] : [])];
19
+ if (!methods.length)
20
+ throw new AuthError("No authentication factor is available for this app. Sign in to an app where your passkey works and add an authenticator in Account, then try again.", 403);
21
+ const options = enroll
22
+ ? await generateRegistrationOptions({ rpName: "BetterPortal", rpID: rpId, userName: user.username, userID: new Uint8Array(Buffer.from(secretHash(`${user.tenantId}:${user.appId}:${user.id}`), "hex")), excludeCredentials: keys.map(p => ({ id: p.id })), authenticatorSelection: { residentKey: "preferred", userVerification: "required" } })
23
+ : await generateAuthenticationOptions({ rpID: rpId, allowCredentials: keys.map(p => ({ id: p.id })), userVerification: "required" });
24
+ const secret = enroll ? generateSecret() : undefined;
25
+ // WebAuthn options may include undefined optional fields; expose their JSON wire representation.
26
+ const publicOptions = JSON.parse(JSON.stringify(options));
27
+ return {
28
+ public: { methods, options: publicOptions, enroll, ...(secret ? { totpSecret: secret, totpUri: generateURI({ issuer: "BetterPortal", label: user.username, secret }) } : {}) },
29
+ private: { origin, rpId, challenge: options.challenge, enroll, ...(secret ? { totpSecret: this.identity.cipher.encrypt(secret) } : {}) }
30
+ };
31
+ }
32
+ async complete(tx, scope, user, data, input) {
33
+ try {
34
+ const dir = { tenantId: user.tenantId, appId: user.appId };
35
+ const enroll = data.enroll === true;
36
+ if (input.method === "totp") {
37
+ const encrypted = enroll ? String(data.totpSecret ?? "") : user.totp;
38
+ if (!encrypted)
39
+ throw new AuthError("Authenticator not available.");
40
+ if (!/^[0-9]{6}$/.test(input.code ?? ""))
41
+ throw new AuthError("Enter a six-digit authenticator code.");
42
+ const result = await verify({ secret: this.identity.cipher.decrypt(encrypted), token: input.code ?? "", epochTolerance: [30, 0], ...(user.totpLastStep === undefined ? {} : { afterTimeStep: user.totpLastStep }) });
43
+ if (!result.valid || !("timeStep" in result))
44
+ throw new AuthError("Invalid or reused verification code.");
45
+ user.totpLastStep = result.timeStep;
46
+ if (enroll)
47
+ user.totp = encrypted;
48
+ }
49
+ else if (input.method === "passkey") {
50
+ if (enroll) {
51
+ const result = await verifyRegistrationResponse({ response: input.credential, expectedChallenge: String(data.challenge), expectedOrigin: String(data.origin), expectedRPID: String(data.rpId), requireUserVerification: true });
52
+ if (!result.verified || !result.registrationInfo)
53
+ throw new AuthError("Passkey registration failed.");
54
+ const { credential } = result.registrationInfo;
55
+ if (await tx.get("passkey", credential.id, dir))
56
+ throw new AuthError("Passkey already registered.");
57
+ await tx.put("passkey", dir, { id: credential.id, userId: user.id, rpId: String(data.rpId), publicKey: Buffer.from(credential.publicKey).toString("base64url"), counter: credential.counter, name: "Passkey" });
58
+ }
59
+ else {
60
+ const response = input.credential;
61
+ const key = await tx.get("passkey", response?.id ?? "", dir);
62
+ if (!key || key.userId !== user.id || key.rpId !== data.rpId)
63
+ throw new AuthError("Passkey unavailable.");
64
+ const result = await verifyAuthenticationResponse({ response, expectedChallenge: String(data.challenge), expectedOrigin: String(data.origin), expectedRPID: key.rpId, requireUserVerification: true, credential: { id: key.id, publicKey: new Uint8Array(Buffer.from(key.publicKey, "base64url")), counter: key.counter } });
65
+ if (!result.verified)
66
+ throw new AuthError("Passkey verification failed.");
67
+ await tx.put("passkey", dir, { ...key, counter: result.authenticationInfo.newCounter });
68
+ }
69
+ }
70
+ else if (input.method === "recovery" && !enroll) {
71
+ const hash = secretHash(input.code ?? "");
72
+ if (!user.recoveryCodes?.includes(hash))
73
+ throw new AuthError("Invalid recovery code.");
74
+ user.recoveryCodes = user.recoveryCodes.filter(c => c !== hash);
75
+ }
76
+ else
77
+ throw new AuthError("Choose an available verification method.");
78
+ let codes;
79
+ if (enroll) {
80
+ user.refreshVersion++;
81
+ codes = Array.from({ length: 10 }, () => newSecret().slice(0, 20));
82
+ user.recoveryCodes = codes.map(secretHash);
83
+ }
84
+ await tx.put("user", dir, user);
85
+ await this.identity.audit(tx, scope, user.id, enroll ? "factor.enrolled" : input.method === "recovery" ? "factor.recovered" : "factor.verified");
86
+ return codes;
87
+ }
88
+ catch (error) {
89
+ if (error instanceof AuthError)
90
+ throw error;
91
+ throw new AuthError("Authentication factor verification failed.");
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=factors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factors.js","sourceRoot":"","sources":["../src/factors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { type BpTokenIssuer, type JwtClaims } from "@betterportal/framework";
2
+ import type { AuthStorage, AuthTransaction, RecordValue, Scope } from "./storage.js";
3
+ export declare class AuthError extends Error {
4
+ readonly status: number;
5
+ constructor(message: string, status?: number);
6
+ }
7
+ export interface IdentityPolicy {
8
+ isolation: "app" | "tenant";
9
+ registration: "public" | "invite-only" | "closed";
10
+ defaultRoleIds: string[];
11
+ allowedRoleIds: string[];
12
+ requireMfa: boolean;
13
+ canManageDirectory?: boolean;
14
+ }
15
+ export interface User extends RecordValue {
16
+ username: string;
17
+ tenantId: string;
18
+ appId: string;
19
+ passwordHash?: string;
20
+ email?: string;
21
+ emailVerified: boolean;
22
+ name?: string;
23
+ picture?: string;
24
+ enabled: boolean;
25
+ refreshVersion: number;
26
+ createdAt: number;
27
+ updatedAt: number;
28
+ totp?: string;
29
+ totpLastStep?: number;
30
+ recoveryCodes?: string[];
31
+ }
32
+ export interface Challenge extends RecordValue {
33
+ purpose: string;
34
+ userId?: string;
35
+ hash: string;
36
+ expiresAt: number;
37
+ attempts: number;
38
+ data: Record<string, unknown>;
39
+ }
40
+ export interface Session extends RecordValue {
41
+ userId: string;
42
+ userAppId: string;
43
+ version: number;
44
+ jti: string;
45
+ previousJtis: string[];
46
+ expiresAt: number;
47
+ createdAt: number;
48
+ revoked: boolean;
49
+ }
50
+ export declare const secretHash: (value: string) => string;
51
+ export declare const newSecret: () => string;
52
+ export declare const normalizeAccountIdentifier: (value: string) => string;
53
+ export declare function isValidEmail(value: string): boolean;
54
+ export declare function equalSecret(a: string, b: string): boolean;
55
+ export declare class SecretCipher {
56
+ private readonly key;
57
+ constructor(key: Buffer);
58
+ encrypt(value: string): string;
59
+ decrypt(value: string): string;
60
+ }
61
+ export declare function validatePassword(value: string): void;
62
+ export declare function hashPassword(value: string): Promise<string>;
63
+ export declare function verifyPassword(hash: string | undefined, value: string): Promise<boolean>;
64
+ export declare class IdentityService {
65
+ readonly storage: AuthStorage;
66
+ readonly cipher: SecretCipher;
67
+ constructor(storage: AuthStorage, cipher: SecretCipher);
68
+ private directory;
69
+ effectiveIsolation(tenantId: string, requested?: "app" | "tenant"): Promise<"app" | "tenant">;
70
+ hasNoUsers(): Promise<boolean>;
71
+ bootstrapAvailable(scope: Scope): Promise<boolean>;
72
+ createUser(scope: Scope, policy: IdentityPolicy, input: {
73
+ username: string;
74
+ password?: string;
75
+ email?: string;
76
+ name?: string;
77
+ verified?: boolean;
78
+ }, roles?: string[], root?: boolean): Promise<User>;
79
+ createInTransaction(tx: AuthTransaction, scope: Scope, policy: IdentityPolicy, input: {
80
+ username: string;
81
+ email?: string;
82
+ name?: string;
83
+ verified?: boolean;
84
+ }, passwordHash?: string, roles?: string[], root?: boolean): Promise<User>;
85
+ findUser(scope: Scope, policy: IdentityPolicy, identifier: string, byId?: boolean): Promise<User | undefined>;
86
+ authenticate(scope: Scope, policy: IdentityPolicy, username: string, password: string): Promise<User | undefined>;
87
+ updateUser(scope: Scope, policy: IdentityPolicy, id: string, update: (user: User, tx: AuthTransaction) => Promise<void>): Promise<User>;
88
+ roles(tx: AuthTransaction, scope: Scope, user: User, policy: IdentityPolicy, initialize?: boolean): Promise<string[]>;
89
+ issueSession(scope: Scope, policy: IdentityPolicy, user: User, issuer: BpTokenIssuer, refreshSeconds: number): Promise<import("@betterportal/framework").BpIssuedTokenPair>;
90
+ refresh(scope: Scope, policy: IdentityPolicy, claims: JwtClaims, issuer: BpTokenIssuer): Promise<import("@betterportal/framework").BpIssuedTokenPair | undefined>;
91
+ assertActiveSession(tx: AuthTransaction, scope: Scope, claims: JwtClaims, user: User): Promise<void>;
92
+ revokeSession(scope: Scope, claims: JwtClaims): Promise<void>;
93
+ challenge(scope: Scope, purpose: string, data: Record<string, unknown>, userId?: string, lifetimeSeconds?: number): Promise<{
94
+ id: string;
95
+ secret: string;
96
+ }>;
97
+ challengeInTransaction(tx: AuthTransaction, scope: Scope, purpose: string, data: Record<string, unknown>, userId?: string, lifetimeSeconds?: number): Promise<{
98
+ id: string;
99
+ secret: string;
100
+ }>;
101
+ consume<T>(scope: Scope, purpose: string, id: string, secret: string, complete: (challenge: Challenge, tx: AuthTransaction) => Promise<T>): Promise<T>;
102
+ consumeWithPreparation<T, P>(scope: Scope, purpose: string, id: string, secret: string, prepare: (challenge: Challenge) => Promise<P>, complete: (challenge: Challenge, tx: AuthTransaction, prepared: P) => Promise<T>): Promise<T>;
103
+ rateLimit(scope: Scope, action: string, actor: string, max?: number, seconds?: number): Promise<void>;
104
+ audit(tx: AuthTransaction, scope: Scope, actor: string, event: string, data?: Record<string, unknown>): Promise<void>;
105
+ }
106
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}