@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.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/MODULE.md +147 -0
- package/README.md +116 -0
- package/dist/api/authApi.d.ts +68 -0
- package/dist/api/authApi.d.ts.map +1 -0
- package/dist/api/authApi.js +90 -0
- package/dist/api/authApi.js.map +1 -0
- package/dist/api/types.d.ts +238 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +14 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/urls.d.ts +41 -0
- package/dist/api/urls.d.ts.map +1 -0
- package/dist/api/urls.js +86 -0
- package/dist/api/urls.js.map +1 -0
- package/dist/flows/anonymousFlow.d.ts +33 -0
- package/dist/flows/anonymousFlow.d.ts.map +1 -0
- package/dist/flows/anonymousFlow.js +26 -0
- package/dist/flows/anonymousFlow.js.map +1 -0
- package/dist/flows/authenticatorChangeFlow.d.ts +67 -0
- package/dist/flows/authenticatorChangeFlow.d.ts.map +1 -0
- package/dist/flows/authenticatorChangeFlow.js +79 -0
- package/dist/flows/authenticatorChangeFlow.js.map +1 -0
- package/dist/flows/createFlowMachine.d.ts +55 -0
- package/dist/flows/createFlowMachine.d.ts.map +1 -0
- package/dist/flows/createFlowMachine.js +56 -0
- package/dist/flows/createFlowMachine.js.map +1 -0
- package/dist/flows/errors.d.ts +15 -0
- package/dist/flows/errors.d.ts.map +1 -0
- package/dist/flows/errors.js +17 -0
- package/dist/flows/errors.js.map +1 -0
- package/dist/flows/magicLinkFlow.d.ts +41 -0
- package/dist/flows/magicLinkFlow.d.ts.map +1 -0
- package/dist/flows/magicLinkFlow.js +29 -0
- package/dist/flows/magicLinkFlow.js.map +1 -0
- package/dist/flows/oauthFlow.d.ts +58 -0
- package/dist/flows/oauthFlow.d.ts.map +1 -0
- package/dist/flows/oauthFlow.js +53 -0
- package/dist/flows/oauthFlow.js.map +1 -0
- package/dist/flows/otpFlow.d.ts +74 -0
- package/dist/flows/otpFlow.d.ts.map +1 -0
- package/dist/flows/otpFlow.js +68 -0
- package/dist/flows/otpFlow.js.map +1 -0
- package/dist/flows/passkeyFlow.d.ts +75 -0
- package/dist/flows/passkeyFlow.d.ts.map +1 -0
- package/dist/flows/passkeyFlow.js +100 -0
- package/dist/flows/passkeyFlow.js.map +1 -0
- package/dist/flows/passwordChangeFlow.d.ts +53 -0
- package/dist/flows/passwordChangeFlow.d.ts.map +1 -0
- package/dist/flows/passwordChangeFlow.js +51 -0
- package/dist/flows/passwordChangeFlow.js.map +1 -0
- package/dist/flows/passwordLoginFlow.d.ts +62 -0
- package/dist/flows/passwordLoginFlow.d.ts.map +1 -0
- package/dist/flows/passwordLoginFlow.js +55 -0
- package/dist/flows/passwordLoginFlow.js.map +1 -0
- package/dist/flows/passwordResetFlow.d.ts +56 -0
- package/dist/flows/passwordResetFlow.d.ts.map +1 -0
- package/dist/flows/passwordResetFlow.js +57 -0
- package/dist/flows/passwordResetFlow.js.map +1 -0
- package/dist/flows/qrLoginFlow.d.ts +55 -0
- package/dist/flows/qrLoginFlow.d.ts.map +1 -0
- package/dist/flows/qrLoginFlow.js +91 -0
- package/dist/flows/qrLoginFlow.js.map +1 -0
- package/dist/flows/ssoFlow.d.ts +46 -0
- package/dist/flows/ssoFlow.d.ts.map +1 -0
- package/dist/flows/ssoFlow.js +34 -0
- package/dist/flows/ssoFlow.js.map +1 -0
- package/dist/flows/totpSetupFlow.d.ts +49 -0
- package/dist/flows/totpSetupFlow.d.ts.map +1 -0
- package/dist/flows/totpSetupFlow.js +47 -0
- package/dist/flows/totpSetupFlow.js.map +1 -0
- package/dist/flows/useFlow.d.ts +9 -0
- package/dist/flows/useFlow.d.ts.map +1 -0
- package/dist/flows/useFlow.js +11 -0
- package/dist/flows/useFlow.js.map +1 -0
- package/dist/flows/verificationFlow.d.ts +108 -0
- package/dist/flows/verificationFlow.d.ts.map +1 -0
- package/dist/flows/verificationFlow.js +195 -0
- package/dist/flows/verificationFlow.js.map +1 -0
- package/dist/headless/AuthProvider.d.ts +18 -0
- package/dist/headless/AuthProvider.d.ts.map +1 -0
- package/dist/headless/AuthProvider.js +22 -0
- package/dist/headless/AuthProvider.js.map +1 -0
- package/dist/headless/Passkey.d.ts +31 -0
- package/dist/headless/Passkey.d.ts.map +1 -0
- package/dist/headless/Passkey.js +51 -0
- package/dist/headless/Passkey.js.map +1 -0
- package/dist/headless/PasswordChange.d.ts +20 -0
- package/dist/headless/PasswordChange.d.ts.map +1 -0
- package/dist/headless/PasswordChange.js +30 -0
- package/dist/headless/PasswordChange.js.map +1 -0
- package/dist/headless/PasswordLogin.d.ts +17 -0
- package/dist/headless/PasswordLogin.d.ts.map +1 -0
- package/dist/headless/PasswordLogin.js +31 -0
- package/dist/headless/PasswordLogin.js.map +1 -0
- package/dist/headless/PasswordReset.d.ts +19 -0
- package/dist/headless/PasswordReset.d.ts.map +1 -0
- package/dist/headless/PasswordReset.js +34 -0
- package/dist/headless/PasswordReset.js.map +1 -0
- package/dist/headless/PasswordlessLogin.d.ts +28 -0
- package/dist/headless/PasswordlessLogin.d.ts.map +1 -0
- package/dist/headless/PasswordlessLogin.js +42 -0
- package/dist/headless/PasswordlessLogin.js.map +1 -0
- package/dist/headless/QrLogin.d.ts +19 -0
- package/dist/headless/QrLogin.d.ts.map +1 -0
- package/dist/headless/QrLogin.js +32 -0
- package/dist/headless/QrLogin.js.map +1 -0
- package/dist/headless/TotpSetup.d.ts +17 -0
- package/dist/headless/TotpSetup.d.ts.map +1 -0
- package/dist/headless/TotpSetup.js +26 -0
- package/dist/headless/TotpSetup.js.map +1 -0
- package/dist/headless/VerificationChallenge.d.ts +37 -0
- package/dist/headless/VerificationChallenge.d.ts.map +1 -0
- package/dist/headless/VerificationChallenge.js +40 -0
- package/dist/headless/VerificationChallenge.js.map +1 -0
- package/dist/headless/misc.d.ts +47 -0
- package/dist/headless/misc.d.ts.map +1 -0
- package/dist/headless/misc.js +84 -0
- package/dist/headless/misc.js.map +1 -0
- package/dist/i18n/keys.d.ts +34 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +83 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +22 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +34 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/mutations.d.ts +28 -0
- package/dist/model/mutations.d.ts.map +1 -0
- package/dist/model/mutations.js +108 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/queries.d.ts +30 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +87 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +13 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +21 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/runtime.d.ts +39 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +44 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/model/session.d.ts +50 -0
- package/dist/model/session.d.ts.map +1 -0
- package/dist/model/session.js +124 -0
- package/dist/model/session.js.map +1 -0
- package/package.json +68 -0
- package/src/api/authApi.ts +332 -0
- package/src/api/types.ts +291 -0
- package/src/api/urls.ts +99 -0
- package/src/flows/anonymousFlow.ts +57 -0
- package/src/flows/authenticatorChangeFlow.ts +160 -0
- package/src/flows/createFlowMachine.ts +126 -0
- package/src/flows/errors.ts +29 -0
- package/src/flows/magicLinkFlow.ts +68 -0
- package/src/flows/oauthFlow.ts +114 -0
- package/src/flows/otpFlow.ts +156 -0
- package/src/flows/passkeyFlow.ts +191 -0
- package/src/flows/passwordChangeFlow.ts +114 -0
- package/src/flows/passwordLoginFlow.ts +122 -0
- package/src/flows/passwordResetFlow.ts +123 -0
- package/src/flows/qrLoginFlow.ts +158 -0
- package/src/flows/ssoFlow.ts +84 -0
- package/src/flows/totpSetupFlow.ts +96 -0
- package/src/flows/useFlow.ts +16 -0
- package/src/flows/verificationFlow.ts +341 -0
- package/src/headless/AuthProvider.tsx +30 -0
- package/src/headless/Passkey.tsx +97 -0
- package/src/headless/PasswordChange.tsx +46 -0
- package/src/headless/PasswordLogin.tsx +49 -0
- package/src/headless/PasswordReset.tsx +51 -0
- package/src/headless/PasswordlessLogin.tsx +60 -0
- package/src/headless/QrLogin.tsx +52 -0
- package/src/headless/TotpSetup.tsx +40 -0
- package/src/headless/VerificationChallenge.tsx +54 -0
- package/src/headless/misc.tsx +151 -0
- package/src/i18n/keys.ts +94 -0
- package/src/index.ts +229 -0
- package/src/model/context.tsx +51 -0
- package/src/model/mutations.ts +152 -0
- package/src/model/queries.ts +130 -0
- package/src/model/queryKeys.ts +32 -0
- package/src/model/runtime.ts +93 -0
- package/src/model/session.ts +188 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createFlowMachine } from "./createFlowMachine.js";
|
|
2
|
+
import { toFlowError } from "./errors.js";
|
|
3
|
+
export function createPasskeyRegistrationFlow(deps) {
|
|
4
|
+
const machine = createFlowMachine({
|
|
5
|
+
id: "auth.passkey_register",
|
|
6
|
+
initial: { step: "idle" },
|
|
7
|
+
analytics: deps.analytics ?? null,
|
|
8
|
+
});
|
|
9
|
+
let pendingDeviceName;
|
|
10
|
+
async function submitCredential(credential) {
|
|
11
|
+
const s = machine.getState();
|
|
12
|
+
if (s.step !== "awaitingCredential")
|
|
13
|
+
return;
|
|
14
|
+
await machine.run({ step: "completing" }, () => deps.api.passkeyRegisterComplete(credential, pendingDeviceName), {
|
|
15
|
+
resolve: (passkey) => ({ step: "registered", passkey }),
|
|
16
|
+
reject: (error) => ({
|
|
17
|
+
step: "error",
|
|
18
|
+
error: toFlowError(error),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function begin(deviceName) {
|
|
23
|
+
pendingDeviceName = deviceName;
|
|
24
|
+
await machine.run({ step: "beginning" }, () => deps.api.passkeyRegisterBegin(), {
|
|
25
|
+
resolve: (r) => ({
|
|
26
|
+
step: "awaitingCredential",
|
|
27
|
+
options: r.options,
|
|
28
|
+
}),
|
|
29
|
+
reject: (error) => ({
|
|
30
|
+
step: "error",
|
|
31
|
+
error: toFlowError(error),
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
const after = machine.getState();
|
|
35
|
+
if (after.step === "awaitingCredential" && deps.webauthnCreate) {
|
|
36
|
+
try {
|
|
37
|
+
const credential = await deps.webauthnCreate(after.options);
|
|
38
|
+
await submitCredential(credential);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
machine.to({ step: "error", error: toFlowError(error) });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function reset() {
|
|
46
|
+
machine.to({ step: "idle" });
|
|
47
|
+
}
|
|
48
|
+
return { machine, begin, submitCredential, reset };
|
|
49
|
+
}
|
|
50
|
+
export function createPasskeyLoginFlow(deps) {
|
|
51
|
+
const machine = createFlowMachine({
|
|
52
|
+
id: "auth.passkey_login",
|
|
53
|
+
initial: { step: "idle" },
|
|
54
|
+
analytics: deps.analytics ?? null,
|
|
55
|
+
});
|
|
56
|
+
async function submitAssertion(credential) {
|
|
57
|
+
const s = machine.getState();
|
|
58
|
+
if (s.step !== "awaitingAssertion")
|
|
59
|
+
return;
|
|
60
|
+
const { sessionKey } = s;
|
|
61
|
+
await machine.run({ step: "completing", sessionKey }, () => deps.api.passkeyAuthenticateComplete(sessionKey, credential), {
|
|
62
|
+
resolve: (result) => {
|
|
63
|
+
deps.onAuthenticated?.(result);
|
|
64
|
+
return { step: "authenticated", result };
|
|
65
|
+
},
|
|
66
|
+
reject: (error) => ({
|
|
67
|
+
step: "error",
|
|
68
|
+
error: toFlowError(error),
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function begin(email) {
|
|
73
|
+
await machine.run({ step: "beginning" }, () => deps.api.passkeyAuthenticateBegin(email), {
|
|
74
|
+
resolve: (r) => ({
|
|
75
|
+
step: "awaitingAssertion",
|
|
76
|
+
sessionKey: r.session_key,
|
|
77
|
+
options: r.options,
|
|
78
|
+
}),
|
|
79
|
+
reject: (error) => ({
|
|
80
|
+
step: "error",
|
|
81
|
+
error: toFlowError(error),
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
const after = machine.getState();
|
|
85
|
+
if (after.step === "awaitingAssertion" && deps.webauthnGet) {
|
|
86
|
+
try {
|
|
87
|
+
const credential = await deps.webauthnGet(after.options);
|
|
88
|
+
await submitAssertion(credential);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
machine.to({ step: "error", error: toFlowError(error) });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function reset() {
|
|
96
|
+
machine.to({ step: "idle" });
|
|
97
|
+
}
|
|
98
|
+
return { machine, begin, submitAssertion, reset };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=passkeyFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passkeyFlow.js","sourceRoot":"","sources":["../../src/flows/passkeyFlow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Analytics } from "@stapel/core";
|
|
2
|
+
import type { AuthApi } from "../api/authApi.js";
|
|
3
|
+
import type { OtpChannel } from "../api/types.js";
|
|
4
|
+
import type { FlowMachine } from "./createFlowMachine.js";
|
|
5
|
+
import type { FlowError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* Authenticated password change (auth-sa.md §4). Two host-selected paths that
|
|
8
|
+
* converge on `changed`:
|
|
9
|
+
* - **old password** — `changeWithPassword(old, new)`.
|
|
10
|
+
* - **email / SMS code** — `requestOtp(method)` → `submitOtp(code, new)`.
|
|
11
|
+
*
|
|
12
|
+
* Which tabs to show is a host decision driven by `GET /password/methods/`
|
|
13
|
+
* (see `usePasswordMethods`); the machine serves whichever path is invoked.
|
|
14
|
+
*/
|
|
15
|
+
export type PasswordChangeState = {
|
|
16
|
+
readonly step: "idle";
|
|
17
|
+
} | {
|
|
18
|
+
readonly step: "changing";
|
|
19
|
+
} | {
|
|
20
|
+
readonly step: "requestingOtp";
|
|
21
|
+
readonly method: OtpChannel;
|
|
22
|
+
} | {
|
|
23
|
+
readonly step: "otpSent";
|
|
24
|
+
readonly method: OtpChannel;
|
|
25
|
+
readonly target: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly step: "verifyingOtp";
|
|
28
|
+
readonly method: OtpChannel;
|
|
29
|
+
readonly target: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly step: "changed";
|
|
32
|
+
} | {
|
|
33
|
+
readonly step: "error";
|
|
34
|
+
readonly error: FlowError;
|
|
35
|
+
} | {
|
|
36
|
+
readonly step: "otpError";
|
|
37
|
+
readonly method: OtpChannel;
|
|
38
|
+
readonly target: string;
|
|
39
|
+
readonly error: FlowError;
|
|
40
|
+
};
|
|
41
|
+
export interface PasswordChangeFlow {
|
|
42
|
+
readonly machine: FlowMachine<PasswordChangeState>;
|
|
43
|
+
changeWithPassword(oldPassword: string, newPassword: string): Promise<void>;
|
|
44
|
+
requestOtp(method: OtpChannel): Promise<void>;
|
|
45
|
+
submitOtp(code: string, newPassword: string): Promise<void>;
|
|
46
|
+
reset(): void;
|
|
47
|
+
}
|
|
48
|
+
export interface PasswordChangeFlowDeps {
|
|
49
|
+
readonly api: AuthApi;
|
|
50
|
+
readonly analytics?: Analytics | null;
|
|
51
|
+
}
|
|
52
|
+
export declare function createPasswordChangeFlow(deps: PasswordChangeFlowDeps): PasswordChangeFlow;
|
|
53
|
+
//# sourceMappingURL=passwordChangeFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordChangeFlow.d.ts","sourceRoot":"","sources":["../../src/flows/passwordChangeFlow.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,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClF;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvF;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACrD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B,CAAC;AAEN,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,mBAAmB,CAAC,CAAC;IACnD,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,sBAAsB,GAC3B,kBAAkB,CAkEpB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createFlowMachine } from "./createFlowMachine.js";
|
|
2
|
+
import { toFlowError } from "./errors.js";
|
|
3
|
+
export function createPasswordChangeFlow(deps) {
|
|
4
|
+
const machine = createFlowMachine({
|
|
5
|
+
id: "auth.password_change",
|
|
6
|
+
initial: { step: "idle" },
|
|
7
|
+
analytics: deps.analytics ?? null,
|
|
8
|
+
});
|
|
9
|
+
async function changeWithPassword(oldPassword, newPassword) {
|
|
10
|
+
await machine.run({ step: "changing" }, () => deps.api.passwordChange(oldPassword, newPassword), {
|
|
11
|
+
resolve: () => ({ step: "changed" }),
|
|
12
|
+
reject: (error) => ({
|
|
13
|
+
step: "error",
|
|
14
|
+
error: toFlowError(error),
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async function requestOtp(method) {
|
|
19
|
+
await machine.run({ step: "requestingOtp", method }, () => deps.api.passwordChangeOtpRequest(method), {
|
|
20
|
+
resolve: (r) => ({
|
|
21
|
+
step: "otpSent",
|
|
22
|
+
method,
|
|
23
|
+
target: r.target,
|
|
24
|
+
}),
|
|
25
|
+
reject: (error) => ({
|
|
26
|
+
step: "error",
|
|
27
|
+
error: toFlowError(error),
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function submitOtp(code, newPassword) {
|
|
32
|
+
const s = machine.getState();
|
|
33
|
+
if (s.step !== "otpSent" && s.step !== "otpError")
|
|
34
|
+
return;
|
|
35
|
+
const { method, target } = s;
|
|
36
|
+
await machine.run({ step: "verifyingOtp", method, target }, () => deps.api.passwordChangeOtpVerify(method, code, newPassword), {
|
|
37
|
+
resolve: () => ({ step: "changed" }),
|
|
38
|
+
reject: (error) => ({
|
|
39
|
+
step: "otpError",
|
|
40
|
+
method,
|
|
41
|
+
target,
|
|
42
|
+
error: toFlowError(error),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function reset() {
|
|
47
|
+
machine.to({ step: "idle" });
|
|
48
|
+
}
|
|
49
|
+
return { machine, changeWithPassword, requestOtp, submitOtp, reset };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=passwordChangeFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordChangeFlow.js","sourceRoot":"","sources":["../../src/flows/passwordChangeFlow.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAwC1C,MAAM,UAAU,wBAAwB,CACtC,IAA4B;IAE5B,MAAM,OAAO,GAAG,iBAAiB,CAAsB;QACrD,EAAE,EAAE,sBAAsB;QAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC;IAEH,KAAK,UAAU,kBAAkB,CAC/B,WAAmB,EACnB,WAAmB;QAEnB,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,UAAU,EAAE,EACpB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,EAAE,WAAW,CAAC,EACvD;YACE,OAAO,EAAE,GAAwB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YACzD,MAAM,EAAE,CAAC,KAAK,EAAuB,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,UAAU,CAAC,MAAkB;QAC1C,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,EACjC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAC/C;YACE,OAAO,EAAE,CAAC,CAAC,EAAuB,EAAE,CAAC,CAAC;gBACpC,IAAI,EAAE,SAAS;gBACf,MAAM;gBACN,MAAM,EAAE,CAAC,CAAC,MAAM;aACjB,CAAC;YACF,MAAM,EAAE,CAAC,KAAK,EAAuB,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,WAAmB;QACxD,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO;QAC1D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,EACxC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,EACjE;YACE,OAAO,EAAE,GAAwB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YACzD,MAAM,EAAE,CAAC,KAAK,EAAuB,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,UAAU;gBAChB,MAAM;gBACN,MAAM;gBACN,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,kBAAkB,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Analytics } from "@stapel/core";
|
|
2
|
+
import type { AuthApi } from "../api/authApi.js";
|
|
3
|
+
import type { AuthResponse } from "../api/types.js";
|
|
4
|
+
import type { FlowMachine } from "./createFlowMachine.js";
|
|
5
|
+
import type { FlowError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* Password login with the TOTP step-up branch (auth-sa.md §3 + §11). A login
|
|
8
|
+
* response is a `oneOf` union — when the account has TOTP enabled the server
|
|
9
|
+
* answers `TOTP_REQUIRED` with a `challenge_token` instead of a session, and
|
|
10
|
+
* the flow parks in `totpRequired` until the user enters their 6-digit code
|
|
11
|
+
* (or a backup code).
|
|
12
|
+
*
|
|
13
|
+
* A 423 during TOTP verify **invalidates the challenge** (auth-sa.md §11): the
|
|
14
|
+
* machine goes to `totpLocked` and the user must log in again for a fresh
|
|
15
|
+
* token — modelled as a terminal step (call `reset()` to return to `idle`).
|
|
16
|
+
*/
|
|
17
|
+
export type PasswordLoginState = {
|
|
18
|
+
readonly step: "idle";
|
|
19
|
+
} | {
|
|
20
|
+
readonly step: "authenticating";
|
|
21
|
+
readonly login: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly step: "totpRequired";
|
|
24
|
+
readonly challengeToken: string;
|
|
25
|
+
readonly expiresIn: number;
|
|
26
|
+
} | {
|
|
27
|
+
readonly step: "verifyingTotp";
|
|
28
|
+
readonly challengeToken: string;
|
|
29
|
+
readonly expiresIn: number;
|
|
30
|
+
} | {
|
|
31
|
+
readonly step: "authenticated";
|
|
32
|
+
readonly result: AuthResponse;
|
|
33
|
+
} | {
|
|
34
|
+
readonly step: "error";
|
|
35
|
+
readonly login: string;
|
|
36
|
+
readonly error: FlowError;
|
|
37
|
+
} | {
|
|
38
|
+
readonly step: "totpError";
|
|
39
|
+
readonly challengeToken: string;
|
|
40
|
+
readonly expiresIn: number;
|
|
41
|
+
readonly error: FlowError;
|
|
42
|
+
} | {
|
|
43
|
+
readonly step: "totpLocked";
|
|
44
|
+
readonly error: FlowError;
|
|
45
|
+
};
|
|
46
|
+
export interface TotpProof {
|
|
47
|
+
readonly code?: string;
|
|
48
|
+
readonly backup_code?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface PasswordLoginFlow {
|
|
51
|
+
readonly machine: FlowMachine<PasswordLoginState>;
|
|
52
|
+
login(loginId: string, password: string): Promise<void>;
|
|
53
|
+
submitTotp(proof: TotpProof): Promise<void>;
|
|
54
|
+
reset(): void;
|
|
55
|
+
}
|
|
56
|
+
export interface PasswordLoginFlowDeps {
|
|
57
|
+
readonly api: AuthApi;
|
|
58
|
+
readonly analytics?: Analytics | null;
|
|
59
|
+
readonly onAuthenticated?: (result: AuthResponse) => void;
|
|
60
|
+
}
|
|
61
|
+
export declare function createPasswordLoginFlow(deps: PasswordLoginFlowDeps): PasswordLoginFlow;
|
|
62
|
+
//# sourceMappingURL=passwordLoginFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordLoginFlow.d.ts","sourceRoot":"","sources":["../../src/flows/passwordLoginFlow.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,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACjE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC7E;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC;IAClD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3D;AAID,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,qBAAqB,GAC1B,iBAAiB,CA4DnB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { isTotpChallenge } from "../api/types.js";
|
|
2
|
+
import { createFlowMachine } from "./createFlowMachine.js";
|
|
3
|
+
import { toFlowError } from "./errors.js";
|
|
4
|
+
const LOCKED_STATUS = 423;
|
|
5
|
+
export function createPasswordLoginFlow(deps) {
|
|
6
|
+
const machine = createFlowMachine({
|
|
7
|
+
id: "auth.password_login",
|
|
8
|
+
initial: { step: "idle" },
|
|
9
|
+
analytics: deps.analytics ?? null,
|
|
10
|
+
});
|
|
11
|
+
async function login(loginId, password) {
|
|
12
|
+
await machine.run({ step: "authenticating", login: loginId }, () => deps.api.passwordLogin(loginId, password), {
|
|
13
|
+
resolve: (r) => {
|
|
14
|
+
if (isTotpChallenge(r)) {
|
|
15
|
+
return {
|
|
16
|
+
step: "totpRequired",
|
|
17
|
+
challengeToken: r.challenge_token,
|
|
18
|
+
expiresIn: r.expires_in,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
deps.onAuthenticated?.(r);
|
|
22
|
+
return { step: "authenticated", result: r };
|
|
23
|
+
},
|
|
24
|
+
reject: (error) => ({
|
|
25
|
+
step: "error",
|
|
26
|
+
login: loginId,
|
|
27
|
+
error: toFlowError(error),
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function submitTotp(proof) {
|
|
32
|
+
const s = machine.getState();
|
|
33
|
+
if (s.step !== "totpRequired" && s.step !== "totpError")
|
|
34
|
+
return;
|
|
35
|
+
const { challengeToken, expiresIn } = s;
|
|
36
|
+
await machine.run({ step: "verifyingTotp", challengeToken, expiresIn }, () => deps.api.totpChallengeVerify(challengeToken, proof), {
|
|
37
|
+
resolve: (result) => {
|
|
38
|
+
deps.onAuthenticated?.(result);
|
|
39
|
+
return { step: "authenticated", result };
|
|
40
|
+
},
|
|
41
|
+
reject: (error) => {
|
|
42
|
+
const flowError = toFlowError(error);
|
|
43
|
+
if (flowError.status === LOCKED_STATUS) {
|
|
44
|
+
return { step: "totpLocked", error: flowError };
|
|
45
|
+
}
|
|
46
|
+
return { step: "totpError", challengeToken, expiresIn, error: flowError };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function reset() {
|
|
51
|
+
machine.to({ step: "idle" });
|
|
52
|
+
}
|
|
53
|
+
return { machine, login, submitTotp, reset };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=passwordLoginFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordLoginFlow.js","sourceRoot":"","sources":["../../src/flows/passwordLoginFlow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Analytics } from "@stapel/core";
|
|
2
|
+
import type { AuthApi } from "../api/authApi.js";
|
|
3
|
+
import type { AuthResponse, OtpChannel } from "../api/types.js";
|
|
4
|
+
import type { FlowMachine } from "./createFlowMachine.js";
|
|
5
|
+
import type { FlowError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* Unauthenticated password reset (auth-sa.md §5). User proves ownership of an
|
|
8
|
+
* email/phone via OTP and receives a fresh session with the new password. The
|
|
9
|
+
* verify step carries the new password, so this is a two-input human-wait
|
|
10
|
+
* (`codeSent`): code + new password together.
|
|
11
|
+
*/
|
|
12
|
+
export type PasswordResetState = {
|
|
13
|
+
readonly step: "idle";
|
|
14
|
+
} | {
|
|
15
|
+
readonly step: "requesting";
|
|
16
|
+
readonly channel: OtpChannel;
|
|
17
|
+
readonly value: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly step: "codeSent";
|
|
20
|
+
readonly channel: OtpChannel;
|
|
21
|
+
readonly value: string;
|
|
22
|
+
readonly target: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly step: "verifying";
|
|
25
|
+
readonly channel: OtpChannel;
|
|
26
|
+
readonly value: string;
|
|
27
|
+
readonly target: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly step: "authenticated";
|
|
30
|
+
readonly result: AuthResponse;
|
|
31
|
+
} | {
|
|
32
|
+
readonly step: "requestError";
|
|
33
|
+
readonly channel: OtpChannel;
|
|
34
|
+
readonly value: string;
|
|
35
|
+
readonly error: FlowError;
|
|
36
|
+
} | {
|
|
37
|
+
readonly step: "codeError";
|
|
38
|
+
readonly channel: OtpChannel;
|
|
39
|
+
readonly value: string;
|
|
40
|
+
readonly target: string;
|
|
41
|
+
readonly error: FlowError;
|
|
42
|
+
};
|
|
43
|
+
export interface PasswordResetFlow {
|
|
44
|
+
readonly machine: FlowMachine<PasswordResetState>;
|
|
45
|
+
request(channel: OtpChannel, value: string): Promise<void>;
|
|
46
|
+
resend(): Promise<void>;
|
|
47
|
+
submit(code: string, newPassword: string): Promise<void>;
|
|
48
|
+
reset(): void;
|
|
49
|
+
}
|
|
50
|
+
export interface PasswordResetFlowDeps {
|
|
51
|
+
readonly api: AuthApi;
|
|
52
|
+
readonly analytics?: Analytics | null;
|
|
53
|
+
readonly onAuthenticated?: (result: AuthResponse) => void;
|
|
54
|
+
}
|
|
55
|
+
export declare function createPasswordResetFlow(deps: PasswordResetFlowDeps): PasswordResetFlow;
|
|
56
|
+
//# sourceMappingURL=passwordResetFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordResetFlow.d.ts","sourceRoot":"","sources":["../../src/flows/passwordResetFlow.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,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrF;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACjE;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,qBAAqB,GAC1B,iBAAiB,CAmEnB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createFlowMachine } from "./createFlowMachine.js";
|
|
2
|
+
import { toFlowError } from "./errors.js";
|
|
3
|
+
export function createPasswordResetFlow(deps) {
|
|
4
|
+
const machine = createFlowMachine({
|
|
5
|
+
id: "auth.password_reset",
|
|
6
|
+
initial: { step: "idle" },
|
|
7
|
+
analytics: deps.analytics ?? null,
|
|
8
|
+
});
|
|
9
|
+
async function request(channel, value) {
|
|
10
|
+
await machine.run({ step: "requesting", channel, value }, () => deps.api.passwordResetRequest(channel, value), {
|
|
11
|
+
resolve: (r) => ({
|
|
12
|
+
step: "codeSent",
|
|
13
|
+
channel,
|
|
14
|
+
value,
|
|
15
|
+
target: r.target,
|
|
16
|
+
}),
|
|
17
|
+
reject: (error) => ({
|
|
18
|
+
step: "requestError",
|
|
19
|
+
channel,
|
|
20
|
+
value,
|
|
21
|
+
error: toFlowError(error),
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function resend() {
|
|
26
|
+
const s = machine.getState();
|
|
27
|
+
if (s.step === "codeSent" ||
|
|
28
|
+
s.step === "codeError" ||
|
|
29
|
+
s.step === "requestError") {
|
|
30
|
+
await request(s.channel, s.value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function submit(code, newPassword) {
|
|
34
|
+
const s = machine.getState();
|
|
35
|
+
if (s.step !== "codeSent" && s.step !== "codeError")
|
|
36
|
+
return;
|
|
37
|
+
const { channel, value, target } = s;
|
|
38
|
+
await machine.run({ step: "verifying", channel, value, target }, () => deps.api.passwordResetVerify(channel, value, code, newPassword), {
|
|
39
|
+
resolve: (result) => {
|
|
40
|
+
deps.onAuthenticated?.(result);
|
|
41
|
+
return { step: "authenticated", result };
|
|
42
|
+
},
|
|
43
|
+
reject: (error) => ({
|
|
44
|
+
step: "codeError",
|
|
45
|
+
channel,
|
|
46
|
+
value,
|
|
47
|
+
target,
|
|
48
|
+
error: toFlowError(error),
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function reset() {
|
|
53
|
+
machine.to({ step: "idle" });
|
|
54
|
+
}
|
|
55
|
+
return { machine, request, resend, submit, reset };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=passwordResetFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwordResetFlow.js","sourceRoot":"","sources":["../../src/flows/passwordResetFlow.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgD1C,MAAM,UAAU,uBAAuB,CACrC,IAA2B;IAE3B,MAAM,OAAO,GAAG,iBAAiB,CAAqB;QACpD,EAAE,EAAE,qBAAqB;QACzB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC;IAEH,KAAK,UAAU,OAAO,CAAC,OAAmB,EAAE,KAAa;QACvD,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,EACtC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,EACnD;YACE,OAAO,EAAE,CAAC,CAAC,EAAsB,EAAE,CAAC,CAAC;gBACnC,IAAI,EAAE,UAAU;gBAChB,OAAO;gBACP,KAAK;gBACL,MAAM,EAAE,CAAC,CAAC,MAAM;aACjB,CAAC;YACF,MAAM,EAAE,CAAC,KAAK,EAAsB,EAAE,CAAC,CAAC;gBACtC,IAAI,EAAE,cAAc;gBACpB,OAAO;gBACP,KAAK;gBACL,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IACE,CAAC,CAAC,IAAI,KAAK,UAAU;YACrB,CAAC,CAAC,IAAI,KAAK,WAAW;YACtB,CAAC,CAAC,IAAI,KAAK,cAAc,EACzB,CAAC;YACD,MAAM,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,WAAmB;QACrD,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO;QAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAC7C,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,EACrE;YACE,OAAO,EAAE,CAAC,MAAM,EAAsB,EAAE;gBACtC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,CAAC,CAAC;gBAC/B,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;YAC3C,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,EAAsB,EAAE,CAAC,CAAC;gBACtC,IAAI,EAAE,WAAW;gBACjB,OAAO;gBACP,KAAK;gBACL,MAAM;gBACN,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,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Analytics } from "@stapel/core";
|
|
2
|
+
import type { AuthApi } from "../api/authApi.js";
|
|
3
|
+
import type { AuthTokens, QrType } from "../api/types.js";
|
|
4
|
+
import type { FlowMachine } from "./createFlowMachine.js";
|
|
5
|
+
import type { FlowError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* QR authentication with background polling (auth-sa.md §8). Serves both the
|
|
8
|
+
* `login_request` sign-in tab (polling device receives `access_token` /
|
|
9
|
+
* `refresh_token` on fulfilment) and the `session_share` profile modal (the
|
|
10
|
+
* *scanning* device gets the session; the generator just watches for
|
|
11
|
+
* `fulfilled`).
|
|
12
|
+
*
|
|
13
|
+
* Device binding matters: generate and poll from the same browser context so
|
|
14
|
+
* the httponly `stapel_qr_<key>` cookie is present (the client must send
|
|
15
|
+
* credentials). On `expired` the flow auto-regenerates and resets the poll
|
|
16
|
+
* loop, per spec.
|
|
17
|
+
*/
|
|
18
|
+
export type QrLoginState = {
|
|
19
|
+
readonly step: "idle";
|
|
20
|
+
} | {
|
|
21
|
+
readonly step: "generating";
|
|
22
|
+
readonly type: QrType;
|
|
23
|
+
} | {
|
|
24
|
+
readonly step: "awaitingScan";
|
|
25
|
+
readonly type: QrType;
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly scanUrl: string;
|
|
28
|
+
readonly expiresIn: number;
|
|
29
|
+
} | {
|
|
30
|
+
readonly step: "fulfilled";
|
|
31
|
+
readonly tokens: AuthTokens | null;
|
|
32
|
+
} | {
|
|
33
|
+
readonly step: "rejected";
|
|
34
|
+
readonly key: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly step: "error";
|
|
37
|
+
readonly error: FlowError;
|
|
38
|
+
};
|
|
39
|
+
export interface QrLoginFlow {
|
|
40
|
+
readonly machine: FlowMachine<QrLoginState>;
|
|
41
|
+
/** Generate a QR and begin the poll loop. */
|
|
42
|
+
start(type: QrType, redirectUrl: string, allowUnauthenticatedScanner?: boolean): Promise<void>;
|
|
43
|
+
/** Stop polling and reset to idle (call on modal close / unmount). */
|
|
44
|
+
dispose(): void;
|
|
45
|
+
}
|
|
46
|
+
export interface QrLoginFlowDeps {
|
|
47
|
+
readonly api: AuthApi;
|
|
48
|
+
readonly analytics?: Analytics | null;
|
|
49
|
+
/** For `login_request` fulfilment — receives the delivered tokens. */
|
|
50
|
+
readonly onAuthenticated?: (tokens: AuthTokens) => void;
|
|
51
|
+
/** Poll cadence; default 5000 ms (auth-sa.md §8). */
|
|
52
|
+
readonly pollIntervalMs?: number;
|
|
53
|
+
}
|
|
54
|
+
export declare function createQrLoginFlow(deps: QrLoginFlowDeps): QrLoginFlow;
|
|
55
|
+
//# sourceMappingURL=qrLoginFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qrLoginFlow.d.ts","sourceRoot":"","sources":["../../src/flows/qrLoginFlow.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,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtD;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IAC5C,6CAA6C;IAC7C,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,2BAA2B,CAAC,EAAE,OAAO,GACpC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,sEAAsE;IACtE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACtC,sEAAsE;IACtE,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxD,qDAAqD;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAsGpE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createFlowMachine } from "./createFlowMachine.js";
|
|
2
|
+
import { toFlowError } from "./errors.js";
|
|
3
|
+
export function createQrLoginFlow(deps) {
|
|
4
|
+
const machine = createFlowMachine({
|
|
5
|
+
id: "auth.qr_login",
|
|
6
|
+
initial: { step: "idle" },
|
|
7
|
+
analytics: deps.analytics ?? null,
|
|
8
|
+
});
|
|
9
|
+
const interval = deps.pollIntervalMs ?? 5000;
|
|
10
|
+
let timer = null;
|
|
11
|
+
let params = null;
|
|
12
|
+
function clearTimer() {
|
|
13
|
+
if (timer !== null) {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
timer = null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function schedulePoll(key) {
|
|
19
|
+
clearTimer();
|
|
20
|
+
timer = setTimeout(() => {
|
|
21
|
+
void poll(key);
|
|
22
|
+
}, interval);
|
|
23
|
+
}
|
|
24
|
+
async function poll(key) {
|
|
25
|
+
// Bail if the machine moved on (disposed / regenerated).
|
|
26
|
+
const s = machine.getState();
|
|
27
|
+
if (s.step !== "awaitingScan" || s.key !== key)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
const status = await deps.api.qrStatus(key);
|
|
31
|
+
if (machine.getState().step !== "awaitingScan")
|
|
32
|
+
return;
|
|
33
|
+
switch (status.status) {
|
|
34
|
+
case "pending":
|
|
35
|
+
schedulePoll(key);
|
|
36
|
+
break;
|
|
37
|
+
case "fulfilled": {
|
|
38
|
+
clearTimer();
|
|
39
|
+
const tokens = status.access_token !== undefined && status.refresh_token !== undefined
|
|
40
|
+
? { access: status.access_token, refresh: status.refresh_token }
|
|
41
|
+
: null;
|
|
42
|
+
if (tokens)
|
|
43
|
+
deps.onAuthenticated?.(tokens);
|
|
44
|
+
machine.to({ step: "fulfilled", tokens });
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "expired":
|
|
48
|
+
clearTimer();
|
|
49
|
+
if (params) {
|
|
50
|
+
void start(params.type, params.redirectUrl, params.allowUnauth);
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
case "rejected":
|
|
54
|
+
clearTimer();
|
|
55
|
+
machine.to({ step: "rejected", key });
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
clearTimer();
|
|
61
|
+
machine.to({ step: "error", error: toFlowError(error) });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function start(type, redirectUrl, allowUnauthenticatedScanner) {
|
|
65
|
+
clearTimer();
|
|
66
|
+
params = { type, redirectUrl, allowUnauth: allowUnauthenticatedScanner };
|
|
67
|
+
await machine.run({ step: "generating", type }, () => deps.api.qrGenerate(type, redirectUrl, allowUnauthenticatedScanner), {
|
|
68
|
+
resolve: (r) => ({
|
|
69
|
+
step: "awaitingScan",
|
|
70
|
+
type,
|
|
71
|
+
key: r.key,
|
|
72
|
+
scanUrl: r.scan_url,
|
|
73
|
+
expiresIn: r.expires_in,
|
|
74
|
+
}),
|
|
75
|
+
reject: (error) => ({
|
|
76
|
+
step: "error",
|
|
77
|
+
error: toFlowError(error),
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
const after = machine.getState();
|
|
81
|
+
if (after.step === "awaitingScan")
|
|
82
|
+
schedulePoll(after.key);
|
|
83
|
+
}
|
|
84
|
+
function dispose() {
|
|
85
|
+
clearTimer();
|
|
86
|
+
params = null;
|
|
87
|
+
machine.to({ step: "idle" });
|
|
88
|
+
}
|
|
89
|
+
return { machine, start, dispose };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=qrLoginFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qrLoginFlow.js","sourceRoot":"","sources":["../../src/flows/qrLoginFlow.ts"],"names":[],"mappings":"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"}
|