@stapel/auth-react 1.0.0

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 (191) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/MODULE.md +147 -0
  4. package/README.md +116 -0
  5. package/dist/api/authApi.d.ts +68 -0
  6. package/dist/api/authApi.d.ts.map +1 -0
  7. package/dist/api/authApi.js +90 -0
  8. package/dist/api/authApi.js.map +1 -0
  9. package/dist/api/types.d.ts +238 -0
  10. package/dist/api/types.d.ts.map +1 -0
  11. package/dist/api/types.js +14 -0
  12. package/dist/api/types.js.map +1 -0
  13. package/dist/api/urls.d.ts +41 -0
  14. package/dist/api/urls.d.ts.map +1 -0
  15. package/dist/api/urls.js +86 -0
  16. package/dist/api/urls.js.map +1 -0
  17. package/dist/flows/anonymousFlow.d.ts +33 -0
  18. package/dist/flows/anonymousFlow.d.ts.map +1 -0
  19. package/dist/flows/anonymousFlow.js +26 -0
  20. package/dist/flows/anonymousFlow.js.map +1 -0
  21. package/dist/flows/authenticatorChangeFlow.d.ts +67 -0
  22. package/dist/flows/authenticatorChangeFlow.d.ts.map +1 -0
  23. package/dist/flows/authenticatorChangeFlow.js +79 -0
  24. package/dist/flows/authenticatorChangeFlow.js.map +1 -0
  25. package/dist/flows/createFlowMachine.d.ts +55 -0
  26. package/dist/flows/createFlowMachine.d.ts.map +1 -0
  27. package/dist/flows/createFlowMachine.js +56 -0
  28. package/dist/flows/createFlowMachine.js.map +1 -0
  29. package/dist/flows/errors.d.ts +15 -0
  30. package/dist/flows/errors.d.ts.map +1 -0
  31. package/dist/flows/errors.js +17 -0
  32. package/dist/flows/errors.js.map +1 -0
  33. package/dist/flows/magicLinkFlow.d.ts +41 -0
  34. package/dist/flows/magicLinkFlow.d.ts.map +1 -0
  35. package/dist/flows/magicLinkFlow.js +29 -0
  36. package/dist/flows/magicLinkFlow.js.map +1 -0
  37. package/dist/flows/oauthFlow.d.ts +58 -0
  38. package/dist/flows/oauthFlow.d.ts.map +1 -0
  39. package/dist/flows/oauthFlow.js +53 -0
  40. package/dist/flows/oauthFlow.js.map +1 -0
  41. package/dist/flows/otpFlow.d.ts +74 -0
  42. package/dist/flows/otpFlow.d.ts.map +1 -0
  43. package/dist/flows/otpFlow.js +68 -0
  44. package/dist/flows/otpFlow.js.map +1 -0
  45. package/dist/flows/passkeyFlow.d.ts +75 -0
  46. package/dist/flows/passkeyFlow.d.ts.map +1 -0
  47. package/dist/flows/passkeyFlow.js +100 -0
  48. package/dist/flows/passkeyFlow.js.map +1 -0
  49. package/dist/flows/passwordChangeFlow.d.ts +53 -0
  50. package/dist/flows/passwordChangeFlow.d.ts.map +1 -0
  51. package/dist/flows/passwordChangeFlow.js +51 -0
  52. package/dist/flows/passwordChangeFlow.js.map +1 -0
  53. package/dist/flows/passwordLoginFlow.d.ts +62 -0
  54. package/dist/flows/passwordLoginFlow.d.ts.map +1 -0
  55. package/dist/flows/passwordLoginFlow.js +55 -0
  56. package/dist/flows/passwordLoginFlow.js.map +1 -0
  57. package/dist/flows/passwordResetFlow.d.ts +56 -0
  58. package/dist/flows/passwordResetFlow.d.ts.map +1 -0
  59. package/dist/flows/passwordResetFlow.js +57 -0
  60. package/dist/flows/passwordResetFlow.js.map +1 -0
  61. package/dist/flows/qrLoginFlow.d.ts +55 -0
  62. package/dist/flows/qrLoginFlow.d.ts.map +1 -0
  63. package/dist/flows/qrLoginFlow.js +91 -0
  64. package/dist/flows/qrLoginFlow.js.map +1 -0
  65. package/dist/flows/ssoFlow.d.ts +46 -0
  66. package/dist/flows/ssoFlow.d.ts.map +1 -0
  67. package/dist/flows/ssoFlow.js +34 -0
  68. package/dist/flows/ssoFlow.js.map +1 -0
  69. package/dist/flows/totpSetupFlow.d.ts +49 -0
  70. package/dist/flows/totpSetupFlow.d.ts.map +1 -0
  71. package/dist/flows/totpSetupFlow.js +47 -0
  72. package/dist/flows/totpSetupFlow.js.map +1 -0
  73. package/dist/flows/useFlow.d.ts +9 -0
  74. package/dist/flows/useFlow.d.ts.map +1 -0
  75. package/dist/flows/useFlow.js +11 -0
  76. package/dist/flows/useFlow.js.map +1 -0
  77. package/dist/flows/verificationFlow.d.ts +108 -0
  78. package/dist/flows/verificationFlow.d.ts.map +1 -0
  79. package/dist/flows/verificationFlow.js +195 -0
  80. package/dist/flows/verificationFlow.js.map +1 -0
  81. package/dist/headless/AuthProvider.d.ts +18 -0
  82. package/dist/headless/AuthProvider.d.ts.map +1 -0
  83. package/dist/headless/AuthProvider.js +22 -0
  84. package/dist/headless/AuthProvider.js.map +1 -0
  85. package/dist/headless/Passkey.d.ts +31 -0
  86. package/dist/headless/Passkey.d.ts.map +1 -0
  87. package/dist/headless/Passkey.js +51 -0
  88. package/dist/headless/Passkey.js.map +1 -0
  89. package/dist/headless/PasswordChange.d.ts +20 -0
  90. package/dist/headless/PasswordChange.d.ts.map +1 -0
  91. package/dist/headless/PasswordChange.js +30 -0
  92. package/dist/headless/PasswordChange.js.map +1 -0
  93. package/dist/headless/PasswordLogin.d.ts +17 -0
  94. package/dist/headless/PasswordLogin.d.ts.map +1 -0
  95. package/dist/headless/PasswordLogin.js +31 -0
  96. package/dist/headless/PasswordLogin.js.map +1 -0
  97. package/dist/headless/PasswordReset.d.ts +19 -0
  98. package/dist/headless/PasswordReset.d.ts.map +1 -0
  99. package/dist/headless/PasswordReset.js +34 -0
  100. package/dist/headless/PasswordReset.js.map +1 -0
  101. package/dist/headless/PasswordlessLogin.d.ts +28 -0
  102. package/dist/headless/PasswordlessLogin.d.ts.map +1 -0
  103. package/dist/headless/PasswordlessLogin.js +42 -0
  104. package/dist/headless/PasswordlessLogin.js.map +1 -0
  105. package/dist/headless/QrLogin.d.ts +19 -0
  106. package/dist/headless/QrLogin.d.ts.map +1 -0
  107. package/dist/headless/QrLogin.js +32 -0
  108. package/dist/headless/QrLogin.js.map +1 -0
  109. package/dist/headless/TotpSetup.d.ts +17 -0
  110. package/dist/headless/TotpSetup.d.ts.map +1 -0
  111. package/dist/headless/TotpSetup.js +26 -0
  112. package/dist/headless/TotpSetup.js.map +1 -0
  113. package/dist/headless/VerificationChallenge.d.ts +37 -0
  114. package/dist/headless/VerificationChallenge.d.ts.map +1 -0
  115. package/dist/headless/VerificationChallenge.js +40 -0
  116. package/dist/headless/VerificationChallenge.js.map +1 -0
  117. package/dist/headless/misc.d.ts +47 -0
  118. package/dist/headless/misc.d.ts.map +1 -0
  119. package/dist/headless/misc.js +84 -0
  120. package/dist/headless/misc.js.map +1 -0
  121. package/dist/i18n/keys.d.ts +34 -0
  122. package/dist/i18n/keys.d.ts.map +1 -0
  123. package/dist/i18n/keys.js +83 -0
  124. package/dist/i18n/keys.js.map +1 -0
  125. package/dist/index.d.ts +73 -0
  126. package/dist/index.d.ts.map +1 -0
  127. package/dist/index.js +48 -0
  128. package/dist/index.js.map +1 -0
  129. package/dist/model/context.d.ts +22 -0
  130. package/dist/model/context.d.ts.map +1 -0
  131. package/dist/model/context.js +34 -0
  132. package/dist/model/context.js.map +1 -0
  133. package/dist/model/mutations.d.ts +28 -0
  134. package/dist/model/mutations.d.ts.map +1 -0
  135. package/dist/model/mutations.js +108 -0
  136. package/dist/model/mutations.js.map +1 -0
  137. package/dist/model/queries.d.ts +30 -0
  138. package/dist/model/queries.d.ts.map +1 -0
  139. package/dist/model/queries.js +87 -0
  140. package/dist/model/queries.js.map +1 -0
  141. package/dist/model/queryKeys.d.ts +13 -0
  142. package/dist/model/queryKeys.d.ts.map +1 -0
  143. package/dist/model/queryKeys.js +21 -0
  144. package/dist/model/queryKeys.js.map +1 -0
  145. package/dist/model/runtime.d.ts +39 -0
  146. package/dist/model/runtime.d.ts.map +1 -0
  147. package/dist/model/runtime.js +44 -0
  148. package/dist/model/runtime.js.map +1 -0
  149. package/dist/model/session.d.ts +50 -0
  150. package/dist/model/session.d.ts.map +1 -0
  151. package/dist/model/session.js +124 -0
  152. package/dist/model/session.js.map +1 -0
  153. package/package.json +68 -0
  154. package/src/api/authApi.ts +332 -0
  155. package/src/api/types.ts +291 -0
  156. package/src/api/urls.ts +99 -0
  157. package/src/flows/anonymousFlow.ts +57 -0
  158. package/src/flows/authenticatorChangeFlow.ts +160 -0
  159. package/src/flows/createFlowMachine.ts +126 -0
  160. package/src/flows/errors.ts +29 -0
  161. package/src/flows/magicLinkFlow.ts +68 -0
  162. package/src/flows/oauthFlow.ts +114 -0
  163. package/src/flows/otpFlow.ts +156 -0
  164. package/src/flows/passkeyFlow.ts +191 -0
  165. package/src/flows/passwordChangeFlow.ts +114 -0
  166. package/src/flows/passwordLoginFlow.ts +122 -0
  167. package/src/flows/passwordResetFlow.ts +123 -0
  168. package/src/flows/qrLoginFlow.ts +158 -0
  169. package/src/flows/ssoFlow.ts +84 -0
  170. package/src/flows/totpSetupFlow.ts +96 -0
  171. package/src/flows/useFlow.ts +16 -0
  172. package/src/flows/verificationFlow.ts +341 -0
  173. package/src/headless/AuthProvider.tsx +30 -0
  174. package/src/headless/Passkey.tsx +97 -0
  175. package/src/headless/PasswordChange.tsx +46 -0
  176. package/src/headless/PasswordLogin.tsx +49 -0
  177. package/src/headless/PasswordReset.tsx +51 -0
  178. package/src/headless/PasswordlessLogin.tsx +60 -0
  179. package/src/headless/QrLogin.tsx +52 -0
  180. package/src/headless/TotpSetup.tsx +40 -0
  181. package/src/headless/VerificationChallenge.tsx +54 -0
  182. package/src/headless/misc.tsx +151 -0
  183. package/src/i18n/keys.ts +94 -0
  184. package/src/index.ts +229 -0
  185. package/src/model/context.tsx +51 -0
  186. package/src/model/mutations.ts +152 -0
  187. package/src/model/queries.ts +130 -0
  188. package/src/model/queryKeys.ts +32 -0
  189. package/src/model/runtime.ts +93 -0
  190. package/src/model/session.ts +188 -0
  191. package/tsconfig.json +26 -0
@@ -0,0 +1,46 @@
1
+ import type { Analytics } from "@stapel/core";
2
+ import type { AuthApi } from "../api/authApi.js";
3
+ import type { SsoLookupResponse } from "../api/types.js";
4
+ import type { FlowMachine } from "./createFlowMachine.js";
5
+ import type { FlowError } from "./errors.js";
6
+ /**
7
+ * Enterprise SSO discovery (auth-sa.md §18). The flow covers the *frontend*
8
+ * part: look up a domain after the user types their email, then decide what to
9
+ * render. Actual login is a full-page browser navigation to
10
+ * `authUrls(base).ssoLogin(orgSlug)` — the backend handles SAML/OIDC opaquely
11
+ * and drops the user back on `FRONTEND_URL/` with cookies set. `beginLogin`
12
+ * performs that navigation when a redirector is available.
13
+ */
14
+ export type SsoState = {
15
+ readonly step: "idle";
16
+ } | {
17
+ readonly step: "looking";
18
+ readonly domain: string;
19
+ } | {
20
+ readonly step: "resolved";
21
+ readonly domain: string;
22
+ readonly result: SsoLookupResponse;
23
+ } | {
24
+ readonly step: "error";
25
+ readonly domain: string;
26
+ readonly error: FlowError;
27
+ };
28
+ export interface SsoFlow {
29
+ readonly machine: FlowMachine<SsoState>;
30
+ /** Look up SSO for an email domain (`sso_required` / optional / none). */
31
+ lookup(domain: string): Promise<void>;
32
+ /** Navigate the browser to the SSO login endpoint for an org slug. */
33
+ beginLogin(orgSlug: string): void;
34
+ reset(): void;
35
+ }
36
+ export interface SsoFlowDeps {
37
+ readonly api: AuthApi;
38
+ readonly analytics?: Analytics | null;
39
+ /**
40
+ * Full-page redirect seam (default `window.location.assign`). Injectable for
41
+ * tests / custom routers.
42
+ */
43
+ readonly redirect?: (url: string) => void;
44
+ }
45
+ export declare function createSsoFlow(deps: SsoFlowDeps): SsoFlow;
46
+ //# sourceMappingURL=ssoFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssoFlow.d.ts","sourceRoot":"","sources":["../../src/flows/ssoFlow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;CACpC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAEnF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IACxC,0EAA0E;IAC1E,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,sEAAsE;IACtE,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAqCxD"}
@@ -0,0 +1,34 @@
1
+ import { authUrls } from "../api/urls.js";
2
+ import { createFlowMachine } from "./createFlowMachine.js";
3
+ import { toFlowError } from "./errors.js";
4
+ export function createSsoFlow(deps) {
5
+ const machine = createFlowMachine({
6
+ id: "auth.sso",
7
+ initial: { step: "idle" },
8
+ analytics: deps.analytics ?? null,
9
+ });
10
+ async function lookup(domain) {
11
+ await machine.run({ step: "looking", domain }, () => deps.api.ssoLookup(domain), {
12
+ resolve: (result) => ({ step: "resolved", domain, result }),
13
+ reject: (error) => ({
14
+ step: "error",
15
+ domain,
16
+ error: toFlowError(error),
17
+ }),
18
+ });
19
+ }
20
+ function beginLogin(orgSlug) {
21
+ const url = authUrls(deps.api.client.baseUrl).ssoLogin(orgSlug);
22
+ const go = deps.redirect ??
23
+ ((target) => {
24
+ if (typeof window !== "undefined")
25
+ window.location.assign(target);
26
+ });
27
+ go(url);
28
+ }
29
+ function reset() {
30
+ machine.to({ step: "idle" });
31
+ }
32
+ return { machine, lookup, beginLogin, reset };
33
+ }
34
+ //# sourceMappingURL=ssoFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssoFlow.js","sourceRoot":"","sources":["../../src/flows/ssoFlow.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAwC1C,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,MAAM,OAAO,GAAG,iBAAiB,CAAW;QAC1C,EAAE,EAAE,UAAU;QACd,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC;IAEH,KAAK,UAAU,MAAM,CAAC,MAAc;QAClC,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EAChC;YACE,OAAO,EAAE,CAAC,MAAM,EAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YACrE,MAAM,EAAE,CAAC,KAAK,EAAY,EAAE,CAAC,CAAC;gBAC5B,IAAI,EAAE,OAAO;gBACb,MAAM;gBACN,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,SAAS,UAAU,CAAC,OAAe;QACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChE,MAAM,EAAE,GACN,IAAI,CAAC,QAAQ;YACb,CAAC,CAAC,MAAc,EAAE,EAAE;gBAClB,IAAI,OAAO,MAAM,KAAK,WAAW;oBAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;QACL,EAAE,CAAC,GAAG,CAAC,CAAC;IACV,CAAC;IAED,SAAS,KAAK;QACZ,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { Analytics } from "@stapel/core";
2
+ import type { AuthApi } from "../api/authApi.js";
3
+ import type { FlowMachine } from "./createFlowMachine.js";
4
+ import type { FlowError } from "./errors.js";
5
+ /**
6
+ * TOTP enrollment on the security-settings screen (auth-sa.md §11 "TOTP
7
+ * setup"). `start()` mints a pending secret + `otpauth://` URI (render as a
8
+ * QR); `confirm(code)` proves the authenticator and returns the one-time
9
+ * backup codes (shown ONCE — host must surface a copy/warn affordance).
10
+ */
11
+ export type TotpSetupState = {
12
+ readonly step: "idle";
13
+ } | {
14
+ readonly step: "starting";
15
+ } | {
16
+ readonly step: "enrolling";
17
+ readonly secret: string;
18
+ readonly qrUri: string;
19
+ readonly expiresIn: number;
20
+ } | {
21
+ readonly step: "confirming";
22
+ readonly secret: string;
23
+ readonly qrUri: string;
24
+ readonly expiresIn: number;
25
+ } | {
26
+ readonly step: "done";
27
+ readonly backupCodes: readonly string[];
28
+ } | {
29
+ readonly step: "startError";
30
+ readonly error: FlowError;
31
+ } | {
32
+ readonly step: "confirmError";
33
+ readonly secret: string;
34
+ readonly qrUri: string;
35
+ readonly expiresIn: number;
36
+ readonly error: FlowError;
37
+ };
38
+ export interface TotpSetupFlow {
39
+ readonly machine: FlowMachine<TotpSetupState>;
40
+ start(): Promise<void>;
41
+ confirm(code: string): Promise<void>;
42
+ reset(): void;
43
+ }
44
+ export interface TotpSetupFlowDeps {
45
+ readonly api: AuthApi;
46
+ readonly analytics?: Analytics | null;
47
+ }
48
+ export declare function createTotpSetupFlow(deps: TotpSetupFlowDeps): TotpSetupFlow;
49
+ //# sourceMappingURL=totpSetupFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totpSetupFlow.d.ts","sourceRoot":"","sources":["../../src/flows/totpSetupFlow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC1D;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B,CAAC;AAEN,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAkD1E"}
@@ -0,0 +1,47 @@
1
+ import { createFlowMachine } from "./createFlowMachine.js";
2
+ import { toFlowError } from "./errors.js";
3
+ export function createTotpSetupFlow(deps) {
4
+ const machine = createFlowMachine({
5
+ id: "auth.totp_setup",
6
+ initial: { step: "idle" },
7
+ analytics: deps.analytics ?? null,
8
+ });
9
+ async function start() {
10
+ await machine.run({ step: "starting" }, () => deps.api.totpSetup(), {
11
+ resolve: (r) => ({
12
+ step: "enrolling",
13
+ secret: r.secret,
14
+ qrUri: r.qr_uri,
15
+ expiresIn: r.expires_in,
16
+ }),
17
+ reject: (error) => ({
18
+ step: "startError",
19
+ error: toFlowError(error),
20
+ }),
21
+ });
22
+ }
23
+ async function confirm(code) {
24
+ const s = machine.getState();
25
+ if (s.step !== "enrolling" && s.step !== "confirmError")
26
+ return;
27
+ const { secret, qrUri, expiresIn } = s;
28
+ await machine.run({ step: "confirming", secret, qrUri, expiresIn }, () => deps.api.totpSetupConfirm(code), {
29
+ resolve: (r) => ({
30
+ step: "done",
31
+ backupCodes: r.backup_codes,
32
+ }),
33
+ reject: (error) => ({
34
+ step: "confirmError",
35
+ secret,
36
+ qrUri,
37
+ expiresIn,
38
+ error: toFlowError(error),
39
+ }),
40
+ });
41
+ }
42
+ function reset() {
43
+ machine.to({ step: "idle" });
44
+ }
45
+ return { machine, start, confirm, reset };
46
+ }
47
+ //# sourceMappingURL=totpSetupFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totpSetupFlow.js","sourceRoot":"","sources":["../../src/flows/totpSetupFlow.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyC1C,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,MAAM,OAAO,GAAG,iBAAiB,CAAiB;QAChD,EAAE,EAAE,iBAAiB;QACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC;IAEH,KAAK,UAAU,KAAK;QAClB,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE;YAClE,OAAO,EAAE,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC;gBAC/B,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,KAAK,EAAE,CAAC,CAAC,MAAM;gBACf,SAAS,EAAE,CAAC,CAAC,UAAU;aACxB,CAAC;YACF,MAAM,EAAE,CAAC,KAAK,EAAkB,EAAE,CAAC,CAAC;gBAClC,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,IAAY;QACjC,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc;YAAE,OAAO;QAChE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACvC,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAChD,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,EACrC;YACE,OAAO,EAAE,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC;gBAC/B,IAAI,EAAE,MAAM;gBACZ,WAAW,EAAE,CAAC,CAAC,YAAY;aAC5B,CAAC;YACF,MAAM,EAAE,CAAC,KAAK,EAAkB,EAAE,CAAC,CAAC;gBAClC,IAAI,EAAE,cAAc;gBACpB,MAAM;gBACN,KAAK;gBACL,SAAS;gBACT,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,SAAS,KAAK;QACZ,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { FlowMachine, FlowStateBase } from "./createFlowMachine.js";
2
+ /**
3
+ * React binding for a {@link FlowMachine}: subscribes to transitions and
4
+ * returns the current state. Because the machine stores immutable state
5
+ * snapshots, `getState` is a stable reference read — no tearing under
6
+ * concurrent React.
7
+ */
8
+ export declare function useFlow<S extends FlowStateBase>(machine: FlowMachine<S>): S;
9
+ //# sourceMappingURL=useFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFlow.d.ts","sourceRoot":"","sources":["../../src/flows/useFlow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEzE;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,aAAa,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAM3E"}
@@ -0,0 +1,11 @@
1
+ import { useSyncExternalStore } from "react";
2
+ /**
3
+ * React binding for a {@link FlowMachine}: subscribes to transitions and
4
+ * returns the current state. Because the machine stores immutable state
5
+ * snapshots, `getState` is a stable reference read — no tearing under
6
+ * concurrent React.
7
+ */
8
+ export function useFlow(machine) {
9
+ return useSyncExternalStore(machine.subscribe, machine.getState, machine.getState);
10
+ }
11
+ //# sourceMappingURL=useFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFlow.js","sourceRoot":"","sources":["../../src/flows/useFlow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAG7C;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAA0B,OAAuB;IACtE,OAAO,oBAAoB,CACzB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,QAAQ,CACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,108 @@
1
+ import type { Analytics, VerificationChallengeHandler } from "@stapel/core";
2
+ import type { AuthApi } from "../api/authApi.js";
3
+ import type { VerificationEnvelope, VerificationFactorId } from "../api/types.js";
4
+ import type { FlowMachine } from "./createFlowMachine.js";
5
+ import type { FlowError } from "./errors.js";
6
+ /**
7
+ * THE FLAGSHIP CROSS-MODULE FLOW (frontend-standard §2).
8
+ *
9
+ * `@stapel/core`'s client intercepts a `403` whose body carries a
10
+ * `verification` envelope and calls `onVerificationChallenge(challenge)`,
11
+ * awaiting a {@link VerificationOutcome}. This controller *is* that handler: it
12
+ * parks a React flow-machine on the challenge, drives the user through one
13
+ * interchangeable factor (`otp_email` / `otp_phone` / `totp` / `passkey`), and
14
+ * resolves the awaited promise with `{ retry: true, token }` so core replays
15
+ * the original request with `X-Verification-Token`. Cancelling resolves
16
+ * `{ retry: false }` and the original 403 propagates.
17
+ *
18
+ * Wire it once:
19
+ * ```ts
20
+ * const verification = createVerificationController({ api });
21
+ * const client = createStapelClient({
22
+ * baseUrl,
23
+ * onVerificationChallenge: verification.handler,
24
+ * });
25
+ * // render <VerificationChallenge controller={verification}> at the app root
26
+ * ```
27
+ *
28
+ * Lifecycle safety: exactly one challenge is live at a time. It self-releases
29
+ * on `expires_at` (an abandoned modal resolves `retry:false` instead of hanging
30
+ * the core request forever and wedging future challenges), and a factor whose
31
+ * `initiate` fails recoverably returns to the picker so a different factor is
32
+ * still choosable — only a 404 (challenge gone) ends the whole challenge.
33
+ *
34
+ * WebAuthn note: the `passkey` factor is **flow-complete but its browser
35
+ * binding is a thin TODO** — the machine surfaces `session_key` + `options`
36
+ * and accepts a finished credential via `submitPasskey`; calling
37
+ * `navigator.credentials.get()` is the host's (or an injected `webauthnGet`)
38
+ * responsibility. See MODULE.md.
39
+ */
40
+ export type VerificationState = {
41
+ readonly step: "idle";
42
+ } | {
43
+ readonly step: "picking";
44
+ readonly challenge: VerificationEnvelope;
45
+ /** Set when a prior factor's initiate failed recoverably — pick another. */
46
+ readonly error?: FlowError;
47
+ } | {
48
+ readonly step: "initiating";
49
+ readonly challenge: VerificationEnvelope;
50
+ readonly factor: VerificationFactorId;
51
+ } | {
52
+ readonly step: "awaitingCode";
53
+ readonly challenge: VerificationEnvelope;
54
+ readonly factor: "otp_email" | "otp_phone" | "totp";
55
+ readonly target: string | null;
56
+ } | {
57
+ readonly step: "awaitingPasskey";
58
+ readonly challenge: VerificationEnvelope;
59
+ readonly sessionKey: string;
60
+ readonly options: Record<string, unknown>;
61
+ } | {
62
+ readonly step: "verifying";
63
+ readonly challenge: VerificationEnvelope;
64
+ readonly factor: VerificationFactorId;
65
+ } | {
66
+ readonly step: "verified";
67
+ readonly token: string;
68
+ } | {
69
+ readonly step: "factorError";
70
+ readonly challenge: VerificationEnvelope;
71
+ readonly factor: VerificationFactorId;
72
+ readonly target: string | null;
73
+ readonly error: FlowError;
74
+ } | {
75
+ readonly step: "unavailable";
76
+ readonly error: FlowError;
77
+ } | {
78
+ readonly step: "expired";
79
+ };
80
+ export interface VerificationController {
81
+ readonly machine: FlowMachine<VerificationState>;
82
+ /** The handler wired into `createStapelClient({ onVerificationChallenge })`. */
83
+ readonly handler: VerificationChallengeHandler;
84
+ /** Choose one of the challenge's factors and initiate it. */
85
+ chooseFactor(factor: VerificationFactorId): Promise<void>;
86
+ /** Submit an OTP/TOTP code (or a TOTP backup code). */
87
+ submitCode(proof: {
88
+ code?: string;
89
+ backup_code?: string;
90
+ }): Promise<void>;
91
+ /** Submit a finished WebAuthn assertion for the `passkey` factor. */
92
+ submitPasskey(credential: unknown): Promise<void>;
93
+ /** Abandon the challenge — resolves the awaited outcome with `retry:false`. */
94
+ cancel(): void;
95
+ }
96
+ export interface VerificationControllerDeps {
97
+ /** Lazy-friendly: a thunk breaks the client↔controller wiring cycle. */
98
+ readonly api: AuthApi | (() => AuthApi);
99
+ readonly analytics?: Analytics | null;
100
+ /**
101
+ * Optional WebAuthn binding. When provided, choosing the `passkey` factor
102
+ * auto-calls it with the server `options` and submits the result — the host
103
+ * needs no passkey code. Thin by design; omit to drive it manually.
104
+ */
105
+ readonly webauthnGet?: (options: Record<string, unknown>) => Promise<unknown>;
106
+ }
107
+ export declare function createVerificationController(deps: VerificationControllerDeps): VerificationController;
108
+ //# sourceMappingURL=verificationFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verificationFlow.d.ts","sourceRoot":"","sources":["../../src/flows/verificationFlow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,195 @@
1
+ import { createFlowMachine } from "./createFlowMachine.js";
2
+ import { toFlowError } from "./errors.js";
3
+ const NOT_FOUND_STATUS = 404;
4
+ const LOCKED_STATUS = 423;
5
+ function asEnvelope(challenge) {
6
+ return {
7
+ challenge_id: challenge.challenge_id,
8
+ scope: typeof challenge.scope === "string" ? challenge.scope : "",
9
+ factors: (challenge.factors ?? []),
10
+ expires_at: typeof challenge["expires_at"] === "number"
11
+ ? challenge["expires_at"]
12
+ : 0,
13
+ };
14
+ }
15
+ export function createVerificationController(deps) {
16
+ const machine = createFlowMachine({
17
+ id: "auth.verification",
18
+ initial: { step: "idle" },
19
+ analytics: deps.analytics ?? null,
20
+ });
21
+ const api = () => typeof deps.api === "function" ? deps.api() : deps.api;
22
+ // The awaited outcome for the in-flight core request. Exactly one challenge
23
+ // is handled at a time; a second arriving while one is active is declined.
24
+ let resolveOutcome = null;
25
+ // Self-release timer: without it, a user who abandons the modal (never
26
+ // cancels) leaves the core `await onVerificationChallenge(...)` hanging
27
+ // forever AND — because `resolveOutcome` stays set — wedges every FUTURE
28
+ // challenge into `retry:false`. The envelope's `expires_at` bounds this.
29
+ let expiryTimer = null;
30
+ function clearExpiry() {
31
+ if (expiryTimer !== null) {
32
+ clearTimeout(expiryTimer);
33
+ expiryTimer = null;
34
+ }
35
+ }
36
+ function settle(outcome) {
37
+ clearExpiry();
38
+ const resolve = resolveOutcome;
39
+ resolveOutcome = null;
40
+ resolve?.(outcome);
41
+ }
42
+ function scheduleExpiry(envelope) {
43
+ clearExpiry();
44
+ if (!envelope.expires_at)
45
+ return; // no bound advertised
46
+ const fire = () => {
47
+ if (resolveOutcome === null)
48
+ return; // already settled
49
+ machine.to({ step: "expired" });
50
+ settle({ retry: false });
51
+ };
52
+ const delayMs = envelope.expires_at * 1000 - Date.now();
53
+ if (delayMs <= 0) {
54
+ fire();
55
+ return;
56
+ }
57
+ expiryTimer = setTimeout(fire, delayMs);
58
+ }
59
+ const handler = (challenge) => {
60
+ if (resolveOutcome !== null) {
61
+ // Already busy with another challenge — decline the new one.
62
+ return Promise.resolve({ retry: false });
63
+ }
64
+ const envelope = asEnvelope(challenge);
65
+ return new Promise((resolve) => {
66
+ resolveOutcome = resolve;
67
+ machine.to({ step: "picking", challenge: envelope });
68
+ scheduleExpiry(envelope);
69
+ });
70
+ };
71
+ function currentChallenge() {
72
+ const s = machine.getState();
73
+ if ("challenge" in s)
74
+ return s.challenge;
75
+ return null;
76
+ }
77
+ function foldUnavailable(error) {
78
+ if (error.status === NOT_FOUND_STATUS || error.status === LOCKED_STATUS) {
79
+ return { step: "unavailable", error };
80
+ }
81
+ return null;
82
+ }
83
+ async function chooseFactor(factor) {
84
+ const challenge = currentChallenge();
85
+ if (challenge === null)
86
+ return;
87
+ await machine.run({ step: "initiating", challenge, factor }, () => api().verificationInitiate(challenge.challenge_id, factor), {
88
+ resolve: (r) => {
89
+ if (factor === "passkey") {
90
+ const sessionKey = String(r.data["session_key"] ?? "");
91
+ const options = (r.data["options"] ?? {});
92
+ return {
93
+ step: "awaitingPasskey",
94
+ challenge,
95
+ sessionKey,
96
+ options,
97
+ };
98
+ }
99
+ const target = typeof r.data["target"] === "string"
100
+ ? r.data["target"]
101
+ : null;
102
+ return { step: "awaitingCode", challenge, factor, target };
103
+ },
104
+ reject: (error) => {
105
+ const flowError = toFlowError(error);
106
+ // A 404 means the whole challenge is gone — restart the original
107
+ // action; release the core request.
108
+ if (flowError.status === NOT_FOUND_STATUS) {
109
+ settle({ retry: false });
110
+ return { step: "unavailable", error: flowError };
111
+ }
112
+ // Any other initiate failure (a locked/invalid factor, network) is
113
+ // factor-scoped: keep the challenge alive and return to the picker so
114
+ // the user can choose a DIFFERENT factor. A 423-locked email factor
115
+ // must not kill an available TOTP factor. Do NOT settle — the expiry
116
+ // timer still bounds abandonment.
117
+ return { step: "picking", challenge, error: flowError };
118
+ },
119
+ });
120
+ // Auto-drive the passkey factor when a binding is injected (thin seam).
121
+ const after = machine.getState();
122
+ if (after.step === "awaitingPasskey" && deps.webauthnGet) {
123
+ try {
124
+ const credential = await deps.webauthnGet(after.options);
125
+ await submitPasskey(credential);
126
+ }
127
+ catch (error) {
128
+ machine.to({
129
+ step: "factorError",
130
+ challenge,
131
+ factor: "passkey",
132
+ target: null,
133
+ error: toFlowError(error),
134
+ });
135
+ }
136
+ }
137
+ }
138
+ async function complete(challenge, factor, target, body) {
139
+ await machine.run({ step: "verifying", challenge, factor }, () => api().verificationComplete(challenge.challenge_id, body), {
140
+ resolve: (r) => {
141
+ settle({ retry: true, token: r.verification_token });
142
+ return { step: "verified", token: r.verification_token };
143
+ },
144
+ reject: (error) => {
145
+ const flowError = toFlowError(error);
146
+ const unavailable = foldUnavailable(flowError);
147
+ if (unavailable) {
148
+ settle({ retry: false });
149
+ return unavailable;
150
+ }
151
+ // Recoverable (wrong code) — stay in the flow for a retry.
152
+ return { step: "factorError", challenge, factor, target, error: flowError };
153
+ },
154
+ });
155
+ }
156
+ async function submitCode(proof) {
157
+ const s = machine.getState();
158
+ let challenge;
159
+ let factor;
160
+ let target;
161
+ if (s.step === "awaitingCode") {
162
+ ({ challenge, factor, target } = s);
163
+ }
164
+ else if (s.step === "factorError" && s.factor !== "passkey") {
165
+ ({ challenge, factor, target } = s);
166
+ }
167
+ else {
168
+ return;
169
+ }
170
+ await complete(challenge, factor, target, { factor, ...proof });
171
+ }
172
+ async function submitPasskey(credential) {
173
+ const s = machine.getState();
174
+ if (s.step !== "awaitingPasskey")
175
+ return;
176
+ await complete(s.challenge, "passkey", null, {
177
+ factor: "passkey",
178
+ session_key: s.sessionKey,
179
+ credential,
180
+ });
181
+ }
182
+ function cancel() {
183
+ settle({ retry: false });
184
+ machine.to({ step: "idle" });
185
+ }
186
+ return {
187
+ machine,
188
+ handler,
189
+ chooseFactor,
190
+ submitCode,
191
+ submitPasskey,
192
+ cancel,
193
+ };
194
+ }
195
+ //# sourceMappingURL=verificationFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verificationFlow.js","sourceRoot":"","sources":["../../src/flows/verificationFlow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import type { AuthRuntime } from "../model/runtime.js";
3
+ /**
4
+ * Provides the wired {@link AuthRuntime} to every auth hook and headless
5
+ * component below it, and restores any persisted session once on mount. Bring
6
+ * your own visual shell — this component renders nothing of its own.
7
+ *
8
+ * ```tsx
9
+ * const runtime = createAuthRuntime({ baseUrl: "/auth/api", storage });
10
+ * // give runtime.client to core's <StapelConfigProvider config={{ client }}>
11
+ * <AuthProvider runtime={runtime}>{app}</AuthProvider>
12
+ * ```
13
+ */
14
+ export declare function AuthProvider(props: {
15
+ runtime: AuthRuntime;
16
+ children: ReactNode;
17
+ }): ReactElement;
18
+ //# sourceMappingURL=AuthProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../../src/headless/AuthProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,GAAG,YAAY,CAUf"}
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect } from "react";
3
+ import { AuthRuntimeContext } from "../model/context.js";
4
+ /**
5
+ * Provides the wired {@link AuthRuntime} to every auth hook and headless
6
+ * component below it, and restores any persisted session once on mount. Bring
7
+ * your own visual shell — this component renders nothing of its own.
8
+ *
9
+ * ```tsx
10
+ * const runtime = createAuthRuntime({ baseUrl: "/auth/api", storage });
11
+ * // give runtime.client to core's <StapelConfigProvider config={{ client }}>
12
+ * <AuthProvider runtime={runtime}>{app}</AuthProvider>
13
+ * ```
14
+ */
15
+ export function AuthProvider(props) {
16
+ const { runtime } = props;
17
+ useEffect(() => {
18
+ void runtime.session.restore();
19
+ }, [runtime]);
20
+ return (_jsx(AuthRuntimeContext.Provider, { value: runtime, children: props.children }));
21
+ }
22
+ //# sourceMappingURL=AuthProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../../src/headless/AuthProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,KAG5B;IACC,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACd,OAAO,CACL,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,OAAO,YACxC,KAAK,CAAC,QAAQ,GACa,CAC/B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from "react";
2
+ import type { PasskeyLoginState, PasskeyRegisterState } from "../flows/passkeyFlow.js";
3
+ /**
4
+ * Optional WebAuthn binding injected into the passkey headless components.
5
+ * THIN by design (auth-sa.md §17): when omitted, the host performs the
6
+ * `navigator.credentials.*` ceremony and calls `submit*` with the result.
7
+ */
8
+ export type WebauthnBinding = (options: Record<string, unknown>) => Promise<unknown>;
9
+ export interface PasskeyRegistrationBag {
10
+ readonly state: PasskeyRegisterState;
11
+ begin(deviceName?: string): void;
12
+ submitCredential(credential: unknown): void;
13
+ reset(): void;
14
+ }
15
+ /** Headless passkey registration (auth-sa.md §17, requires auth). */
16
+ export declare function PasskeyRegistration(props: {
17
+ children: (bag: PasskeyRegistrationBag) => ReactNode;
18
+ webauthnCreate?: WebauthnBinding;
19
+ }): ReactNode;
20
+ export interface PasskeyLoginBag {
21
+ readonly state: PasskeyLoginState;
22
+ begin(email?: string): void;
23
+ submitAssertion(credential: unknown): void;
24
+ reset(): void;
25
+ }
26
+ /** Headless passkey login (auth-sa.md §17, no auth required). */
27
+ export declare function PasskeyLogin(props: {
28
+ children: (bag: PasskeyLoginBag) => ReactNode;
29
+ webauthnGet?: WebauthnBinding;
30
+ }): ReactNode;
31
+ //# sourceMappingURL=Passkey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Passkey.d.ts","sourceRoot":"","sources":["../../src/headless/Passkey.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAIjC;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,QAAQ,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,SAAS,CAAC;IACrD,cAAc,CAAC,EAAE,eAAe,CAAC;CAClC,GAAG,SAAS,CAwBZ;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,SAAS,CAAC;IAC9C,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,GAAG,SAAS,CA0BZ"}