@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @stapel/auth-react
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 809b706: New package: headless React auth flow pair for stapel-auth (frontend-standard
|
|
8
|
+
§2), built on `@stapel/core`. First instance of the framework's
|
|
9
|
+
`createFlowMachine` pattern (typed steps, human-wait vs async `run`,
|
|
10
|
+
auto-instrumented `flow.<id>.<step>` analytics).
|
|
11
|
+
|
|
12
|
+
Full journeys: Email/Phone OTP, password login (with TOTP challenge branch),
|
|
13
|
+
password change/reset, the step-up **verification factor flow** wired into
|
|
14
|
+
core's verification-403 interception (the flagship cross-module seam), TOTP
|
|
15
|
+
setup, OAuth token exchange, sessions, token refresh with rotation + teardown,
|
|
16
|
+
QR login polling, magic-link request, anonymous, instant authenticator change,
|
|
17
|
+
and SSO discovery. Passkeys + the passkey verification factor are flow-complete
|
|
18
|
+
with a thin injectable WebAuthn binding (see MODULE.md).
|
|
19
|
+
|
|
20
|
+
Ships typed API client (CSRF on mutations), open-redirect guards (§19.2),
|
|
21
|
+
namespaced TanStack Query hooks/mutations, `createAuthRuntime` (session token
|
|
22
|
+
seam + verification controller wired into the client), render-prop headless
|
|
23
|
+
components, and an i18n key bundle.
|
|
24
|
+
|
|
25
|
+
Not released — Opus-authored first instance, awaits independent adversarial
|
|
26
|
+
review.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [5dfa61e]
|
|
31
|
+
- @stapel/core@0.2.0
|
|
32
|
+
|
|
33
|
+
## 0.1.0 (unreleased)
|
|
34
|
+
|
|
35
|
+
Initial headless auth flow pair for stapel-auth (frontend-standard §2), built on
|
|
36
|
+
`@stapel/core`. First instance of the framework's `createFlowMachine` pattern.
|
|
37
|
+
|
|
38
|
+
- **flows/** — `createFlowMachine` primitive (typed steps, human-wait vs async
|
|
39
|
+
`run`, auto-instrumented `flow.<id>.<step>` analytics) + machines for OTP,
|
|
40
|
+
password login (with TOTP branch), password change/reset, step-up
|
|
41
|
+
verification, TOTP setup, OAuth, QR login, magic link, anonymous,
|
|
42
|
+
authenticator change, SSO, and passkeys.
|
|
43
|
+
- **api/** — typed client over `StapelClient` for the auth-sa.md endpoints
|
|
44
|
+
(CSRF header on mutations), browser-redirect URL builders, and the §19.2
|
|
45
|
+
open-redirect guards.
|
|
46
|
+
- **model/** — `createAuthRuntime` (wires the session token seam and the
|
|
47
|
+
verification-403 controller into the client), `AuthSession` (refresh rotation
|
|
48
|
+
- teardown + persistence), namespaced TanStack Query hooks and mutations.
|
|
49
|
+
- **headless/** — render-prop components incl. the flagship
|
|
50
|
+
`<VerificationChallenge>` factor UI, plus `<AuthProvider>`.
|
|
51
|
+
- **i18n/** — auth-react key bundle registered into core's engine.
|
|
52
|
+
|
|
53
|
+
Passkeys and the passkey verification factor are flow-complete; the WebAuthn
|
|
54
|
+
browser binding is a thin injectable seam (see MODULE.md).
|
|
55
|
+
|
|
56
|
+
**NOT released** — awaits independent adversarial review.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stapel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MODULE.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @stapel/auth-react — MODULE.md
|
|
2
|
+
|
|
3
|
+
Headless auth flow pair for stapel-auth (frontend-standard §2/§5). First
|
|
4
|
+
instance of the framework's **flow-machine** pattern; later `-react` pairs copy
|
|
5
|
+
it. Layers: `api/` (typed client over `@stapel/core`) → `model/` (runtime,
|
|
6
|
+
session, query hooks) → `flows/` (state machines) → `headless/` (render props)
|
|
7
|
+
→ `i18n/` (key bundle).
|
|
8
|
+
|
|
9
|
+
## The `createFlowMachine` pattern (reusable primitive)
|
|
10
|
+
|
|
11
|
+
A flow is a tiny state container whose state is a discriminated union keyed by
|
|
12
|
+
`step`. `createFlowMachine({ id, initial, analytics })` gives every flow three
|
|
13
|
+
things for free:
|
|
14
|
+
|
|
15
|
+
- **Typed transitions** — `to(next)` swaps state (immutable snapshots) and
|
|
16
|
+
notifies React via `useFlow` (`useSyncExternalStore`).
|
|
17
|
+
- **Human-wait vs async steps** — a resting step waits for user input (`to`);
|
|
18
|
+
an async step goes through `run(pending, task, { resolve, reject })`, which
|
|
19
|
+
parks in `pending`, awaits, then transitions on the result. `run` **never
|
|
20
|
+
throws** — rejections fold into an error state, so hosts render errors
|
|
21
|
+
instead of catching them. `run` is **staleness-guarded** (an epoch counter
|
|
22
|
+
bumped on every `to`): if a newer transition happens while the task is in
|
|
23
|
+
flight — double-submit, cancel, navigate, challenge expiry — the late result
|
|
24
|
+
is dropped, never clobbering the newer state nor running its resolve/reject
|
|
25
|
+
side effects (analytics still fires). This guard lives in the primitive so
|
|
26
|
+
every future pair inherits it.
|
|
27
|
+
- **Auto-instrumentation** — each transition emits `flow.<id>.<step>` started;
|
|
28
|
+
each `run` emits `completed`/`failed` for its pending step
|
|
29
|
+
(analytics-standard §1.2). Funnels exist without hand-written tracking.
|
|
30
|
+
|
|
31
|
+
Each flow is a factory `createXxxFlow(deps) → { machine, ...actions }`. Deps are
|
|
32
|
+
`{ api, analytics?, onAuthenticated? }`. Copy this shape for new pairs.
|
|
33
|
+
|
|
34
|
+
## Flows & headless components
|
|
35
|
+
|
|
36
|
+
| Journey (auth-sa.md) | Flow factory | Headless | Status |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| Email/Phone OTP §1–2 | `createOtpFlow` | `<PasswordlessLogin>` | **full** |
|
|
39
|
+
| Password login + TOTP branch §3/§11 | `createPasswordLoginFlow` | `<PasswordLogin>` | **full** |
|
|
40
|
+
| Password change §4 | `createPasswordChangeFlow` | `<PasswordChange>` | **full** |
|
|
41
|
+
| Password reset §5 | `createPasswordResetFlow` | `<PasswordReset>` | **full** |
|
|
42
|
+
| Step-up verification §11 | `createVerificationController` | `<VerificationChallenge>` | **full** (passkey factor thin) |
|
|
43
|
+
| TOTP setup §11 | `createTotpSetupFlow` | `<TotpSetup>` | **full** |
|
|
44
|
+
| OAuth token exchange §7 | `createOAuthFlow` | — (+ `authUrls().oauthAuthorize`) | **full** |
|
|
45
|
+
| Sessions §12 | model hooks/mutations | — | **full** |
|
|
46
|
+
| Token refresh §13 | `AuthSession.onAuthRefresh` | — | **full** |
|
|
47
|
+
| QR login/session-share §8 | `createQrLoginFlow` | `<QrLogin>` | **full** (polling) |
|
|
48
|
+
| Magic link §15 | `createMagicLinkFlow` | `<MagicLink>` | **full** (request side) |
|
|
49
|
+
| Anonymous §6 | `createAnonymousFlow` | `<AnonymousSession>` | **full** |
|
|
50
|
+
| Authenticator change (instant) §9 | `createAuthenticatorChangeFlow` | `<AuthenticatorChange>` | **full** (delayed = CRUD hooks) |
|
|
51
|
+
| SSO discovery §18 | `createSsoFlow` | `<SsoDiscovery>` | **full** (redirect handoff) |
|
|
52
|
+
| Passkeys §17 | `createPasskeyRegistrationFlow` / `createPasskeyLoginFlow` | `<PasskeyRegistration>` / `<PasskeyLogin>` | **flow complete, WebAuthn binding THIN** |
|
|
53
|
+
|
|
54
|
+
### Thin-WebAuthn TODO (honest scope)
|
|
55
|
+
|
|
56
|
+
Passkeys and the `passkey` verification factor model the full
|
|
57
|
+
begin→ceremony→complete journey and surface the server `options` /
|
|
58
|
+
`session_key`. The **single browser step** — `navigator.credentials.create()` /
|
|
59
|
+
`.get()` — is **not** implemented here. Provide it one of two ways:
|
|
60
|
+
|
|
61
|
+
1. **Inject a binding** — pass `webauthnCreate` / `webauthnGet` (to the flow,
|
|
62
|
+
the `<Passkey*>` components, or `createAuthRuntime({ webauthnGet })`); the
|
|
63
|
+
flow auto-drives the ceremony.
|
|
64
|
+
2. **Drive it manually** — read the `awaitingCredential` / `awaitingAssertion`
|
|
65
|
+
state's `options`, run the ceremony yourself, call `submitCredential` /
|
|
66
|
+
`submitAssertion` / `submitPasskey`.
|
|
67
|
+
|
|
68
|
+
No "no credentials" heuristics — per auth-sa.md §19.6 that leak works against
|
|
69
|
+
the privacy property; show the single copy key `auth.passkey.no_credentials`.
|
|
70
|
+
|
|
71
|
+
## Verification challenge lifecycle
|
|
72
|
+
|
|
73
|
+
Exactly one challenge is live at a time. The controller holds the core
|
|
74
|
+
request's awaited resolver, so it must always release it:
|
|
75
|
+
|
|
76
|
+
- **Success** → `settle({ retry: true, token })`; core replays with
|
|
77
|
+
`X-Verification-Token`.
|
|
78
|
+
- **Cancel** → `settle({ retry: false })`; the original 403 propagates.
|
|
79
|
+
- **Expiry** → the envelope's `expires_at` schedules an auto-release: an
|
|
80
|
+
abandoned modal (user walks away, never cancels) resolves `{ retry: false }`
|
|
81
|
+
instead of hanging the core `fetch` forever — and because the resolver is
|
|
82
|
+
cleared, subsequent challenges are handled rather than declined (no wedge).
|
|
83
|
+
- **Factor initiate failure** → a 404 (challenge gone) ends the whole challenge
|
|
84
|
+
(`unavailable` + settle `retry:false`); any other failure (a 423-locked or
|
|
85
|
+
invalid factor, network) returns to the **picker** carrying the error, so a
|
|
86
|
+
different, still-valid factor remains choosable.
|
|
87
|
+
|
|
88
|
+
## Model hooks
|
|
89
|
+
|
|
90
|
+
Queries: `useCapabilities`, `useMe`, `useSecurityStatus`, `usePasswordMethods`,
|
|
91
|
+
`useSessions`, `usePasskeys`, `useAuditLog`, `useDelayedChangeStatus`,
|
|
92
|
+
`useSsoLookup`. Mutations (with invalidation): `useLogout`, `useRevokeSession`,
|
|
93
|
+
`useRevokeOtherSessions`, `useConfirmSession`, `useRemovePasskey`,
|
|
94
|
+
`useDisableTotp`, `useCancelDelayedChange`. Keys are namespaced under `["auth"]`
|
|
95
|
+
(`authQueryKeys`).
|
|
96
|
+
|
|
97
|
+
## Session & persist policy
|
|
98
|
+
|
|
99
|
+
`AuthSession` (via `createAuthRuntime`) holds `{ user, tokens, status }`,
|
|
100
|
+
exposes `getAccessToken` / `onAuthRefresh` wired into the client, and rotates
|
|
101
|
+
the refresh token on 401. `error.401.refresh_revoked` → `onTeardown("revoked")`
|
|
102
|
+
(hard logout); any other refresh failure → `"expired"`; explicit
|
|
103
|
+
`logout()` → `"logout"`. A recursion guard ensures the refresh request's own
|
|
104
|
+
401 does not re-enter refresh. Session persists `{ user, tokens }` to core's
|
|
105
|
+
`PersistStorage` (IndexedDB→localStorage) under `stapel-auth:session`; query
|
|
106
|
+
cache persistence is per-user via core's `setPersistUser` — call
|
|
107
|
+
`purgePersistedCache()` from `onTeardown` for GDPR-grade logout.
|
|
108
|
+
|
|
109
|
+
## Extension points (fork-free, frontend-standard §7)
|
|
110
|
+
|
|
111
|
+
- **Client injection** — every flow/hook uses the client from
|
|
112
|
+
`createAuthRuntime` (host-built); a divergent backend injects its own
|
|
113
|
+
generated client into the same machines.
|
|
114
|
+
- **Steps/guards** — flows are plain factories; wrap or replace actions.
|
|
115
|
+
- **Headless layer** — render props are replaceable by definition; shadcn-copy
|
|
116
|
+
them into the app.
|
|
117
|
+
- **i18n** — override any key via core's `loadLocale`.
|
|
118
|
+
|
|
119
|
+
## Ambiguities / conflicts surfaced from auth-sa.md
|
|
120
|
+
|
|
121
|
+
1. **Legacy step-up vs verification envelope.** §11 keeps `POST /totp/step-up/`
|
|
122
|
+
+ `X-Step-Up-Token`, while §19.4 says the interceptor currently handles
|
|
123
|
+
`403 error.403.step_up_required` (the legacy path). auth-sa.md itself says
|
|
124
|
+
"new integrations should implement the envelope flow." We implement **only**
|
|
125
|
+
the envelope flow (`error.403.verification_required` → `verification`
|
|
126
|
+
object), matching `@stapel/core`'s interception seam. The legacy
|
|
127
|
+
`X-Step-Up-Token` path is intentionally **not** implemented — flag for review
|
|
128
|
+
if any endpoint still emits it.
|
|
129
|
+
2. **QR error codes diverge.** The §8 status polling uses a `{status}` body
|
|
130
|
+
(`rejected`/`expired`) while the Error reference lists `error.409.qr_*` /
|
|
131
|
+
`error.403.qr_*` HTTP errors. The flow treats the status body as
|
|
132
|
+
authoritative for polling and surfaces HTTP errors as `error` state.
|
|
133
|
+
3. **OAuth redirect (option A) shapes.** §7 option A + §19.1 imply a
|
|
134
|
+
`/app/oauth/callback` frontend route and a `/totp-challenge` alias; those are
|
|
135
|
+
**host routing** concerns (the backend sets cookies), so auth-react provides
|
|
136
|
+
only `authUrls().oauthAuthorize` and the token-exchange flow (option B), not
|
|
137
|
+
the callback route.
|
|
138
|
+
4. **Magic-link / SSO landings** (`/login?...` params, §15/§18.2) are backend
|
|
139
|
+
redirects to host routes; auth-react provides the `safeNextPath` /
|
|
140
|
+
`safeScanRedirect` guards (§19.2) but the route components are app-layer.
|
|
141
|
+
5. **Capabilities `password` under registration** is documented but no
|
|
142
|
+
password-registration endpoint exists in §1–18; treated as display-only.
|
|
143
|
+
|
|
144
|
+
## Status
|
|
145
|
+
|
|
146
|
+
**NOT released** — Opus-authored first instance; awaits independent adversarial
|
|
147
|
+
review before any npm publish (no-Fable protocol).
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @stapel/auth-react
|
|
2
|
+
|
|
3
|
+
The **headless** React flow pair for [stapel-auth](../../../docs/auth-sa.md)
|
|
4
|
+
(frontend-standard §2). Business + state only — **zero visual opinion**. Every
|
|
5
|
+
flow is a typed state machine; every headless component is a render prop. You
|
|
6
|
+
bring the markup and design; auth-react brings the auth journeys, the token
|
|
7
|
+
seam, and the flagship step-up verification flow.
|
|
8
|
+
|
|
9
|
+
Built entirely on [`@stapel/core`](../core): typed client + error envelope,
|
|
10
|
+
verification-403 interception, token refresh seam, TanStack Query layer, i18n
|
|
11
|
+
engine, and the analytics facade.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
pnpm add @stapel/auth-react @stapel/core @tanstack/react-query react
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Wire the app once
|
|
20
|
+
|
|
21
|
+
`createAuthRuntime` is the single place the flagship seams are connected: it
|
|
22
|
+
builds a `StapelClient` whose token refresh comes from the session and whose
|
|
23
|
+
`onVerificationChallenge` is the verification controller. Hand that client to
|
|
24
|
+
core's provider.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {
|
|
28
|
+
createStapelQueryClient,
|
|
29
|
+
StapelConfigProvider,
|
|
30
|
+
I18nProvider,
|
|
31
|
+
createI18n,
|
|
32
|
+
} from "@stapel/core";
|
|
33
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
34
|
+
import {
|
|
35
|
+
createAuthRuntime,
|
|
36
|
+
AuthProvider,
|
|
37
|
+
VerificationChallenge,
|
|
38
|
+
registerAuthI18n,
|
|
39
|
+
} from "@stapel/auth-react";
|
|
40
|
+
|
|
41
|
+
const runtime = createAuthRuntime({
|
|
42
|
+
baseUrl: "/auth/api",
|
|
43
|
+
// cookieMode: true, // httponly JWT cookies instead of bearer
|
|
44
|
+
onTeardown: (reason) => { // auth-sa.md §19.3
|
|
45
|
+
void query.purgePersistedCache();
|
|
46
|
+
location.assign(reason === "revoked" ? "/sign-in?session_revoked=1" : "/sign-in");
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const query = createStapelQueryClient({ cacheVersion: "0.1.0" });
|
|
51
|
+
const i18n = createI18n({ locale: "en" });
|
|
52
|
+
registerAuthI18n(i18n); // auth-react's key bundle → core's engine
|
|
53
|
+
|
|
54
|
+
export function Root({ children }: { children: React.ReactNode }) {
|
|
55
|
+
return (
|
|
56
|
+
<StapelConfigProvider config={{ client: runtime.client }} analytics={analytics}>
|
|
57
|
+
<QueryClientProvider client={query.queryClient}>
|
|
58
|
+
<I18nProvider i18n={i18n}>
|
|
59
|
+
<AuthProvider runtime={runtime}>
|
|
60
|
+
{children}
|
|
61
|
+
{/* Mount ONCE — renders your step-up modal on demand */}
|
|
62
|
+
<VerificationChallenge>
|
|
63
|
+
{({ state, chooseFactor, submitCode, cancel }) => (
|
|
64
|
+
<YourModal open={state.step !== "idle"} onClose={cancel}>
|
|
65
|
+
{/* render state.challenge.factors → chooseFactor(f) → submitCode({code}) */}
|
|
66
|
+
</YourModal>
|
|
67
|
+
)}
|
|
68
|
+
</VerificationChallenge>
|
|
69
|
+
</AuthProvider>
|
|
70
|
+
</I18nProvider>
|
|
71
|
+
</QueryClientProvider>
|
|
72
|
+
</StapelConfigProvider>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Use a headless component
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { PasswordlessLogin } from "@stapel/auth-react";
|
|
81
|
+
import { useT } from "@stapel/core";
|
|
82
|
+
|
|
83
|
+
function SignIn() {
|
|
84
|
+
const t = useT();
|
|
85
|
+
return (
|
|
86
|
+
<PasswordlessLogin>
|
|
87
|
+
{({ state, requestCode, submitCode, resend }) => {
|
|
88
|
+
if (state.step === "codeSent" || state.step === "codeError")
|
|
89
|
+
return (
|
|
90
|
+
<CodeForm
|
|
91
|
+
hint={t("auth.otp.sent_to", { target: state.target })}
|
|
92
|
+
error={state.step === "codeError" ? t(state.error.code, state.error.params) : null}
|
|
93
|
+
onSubmit={submitCode}
|
|
94
|
+
onResend={() => resend()}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
if (state.step === "authenticated") return <Redirect to="/app" />;
|
|
98
|
+
return <EmailForm onSubmit={(email) => requestCode("email", email)} />;
|
|
99
|
+
}}
|
|
100
|
+
</PasswordlessLogin>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Error codes on every flow error state are backend `localizable_error` keys —
|
|
106
|
+
resolve them with core's `useT()`. auth-react ships an English fallback bundle
|
|
107
|
+
(`registerAuthI18n`) covering both its UI keys and the auth-sa.md error codes.
|
|
108
|
+
|
|
109
|
+
## Shadcn mode (frontend-standard §7)
|
|
110
|
+
|
|
111
|
+
Every headless component is app-layer by definition — copy it into your repo
|
|
112
|
+
and own it while keeping `api/`/`model/`/`flows/` as a dependency. Visual
|
|
113
|
+
divergence never forks the package.
|
|
114
|
+
|
|
115
|
+
See **[MODULE.md](./MODULE.md)** for the full flow/hook catalogue, extension
|
|
116
|
+
points, and which journeys are full vs thin-WebAuthn.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { StapelClient } from "@stapel/core";
|
|
2
|
+
import type { AuthResponse, AuditPage, AuthSession, Capabilities, ChangeOldVerifiedResponse, DelayedChangeInitiatedResponse, DelayedChangeStatus, LoginResponse, OtpChannel, OtpRequestResponse, Passkey, PasskeyAuthenticateBeginResponse, PasskeyRegisterBeginResponse, PasswordMethods, QrGenerateResponse, QrStatusResponse, QrType, RefreshResponse, SecurityStatus, SsoLookupResponse, StapelUser, StatusResponse, TotpDisableRequest, TotpSetupConfirmResponse, TotpSetupResponse, VerificationCompleteResponse, VerificationEnvelope, VerificationFactorId, VerificationInitiateResponse } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The typed auth surface. One method per auth-sa.md endpoint that a JS client
|
|
5
|
+
* may call. Browser-redirect endpoints (OAuth authorize, SSO login, QR scan,
|
|
6
|
+
* magic-link verify) are intentionally absent — see `authUrls`.
|
|
7
|
+
*/
|
|
8
|
+
export interface AuthApi {
|
|
9
|
+
readonly client: StapelClient;
|
|
10
|
+
capabilities(): Promise<Capabilities>;
|
|
11
|
+
me(): Promise<StapelUser>;
|
|
12
|
+
logout(): Promise<StatusResponse>;
|
|
13
|
+
otpRequest(channel: OtpChannel, value: string, captchaToken?: string): Promise<OtpRequestResponse>;
|
|
14
|
+
otpVerify(channel: OtpChannel, value: string, code: string): Promise<AuthResponse>;
|
|
15
|
+
passwordLogin(login: string, password: string): Promise<LoginResponse>;
|
|
16
|
+
passwordMethods(): Promise<PasswordMethods>;
|
|
17
|
+
passwordChange(oldPassword: string, newPassword: string): Promise<StatusResponse>;
|
|
18
|
+
passwordChangeOtpRequest(method: OtpChannel): Promise<OtpRequestResponse>;
|
|
19
|
+
passwordChangeOtpVerify(method: OtpChannel, code: string, newPassword: string): Promise<StatusResponse>;
|
|
20
|
+
passwordResetRequest(channel: OtpChannel, value: string): Promise<OtpRequestResponse>;
|
|
21
|
+
passwordResetVerify(channel: OtpChannel, value: string, code: string, newPassword: string): Promise<AuthResponse>;
|
|
22
|
+
anonymous(deviceId?: string): Promise<AuthResponse>;
|
|
23
|
+
oauthLogin(provider: string, accessToken: string): Promise<LoginResponse>;
|
|
24
|
+
totpChallengeVerify(challengeToken: string, proof: {
|
|
25
|
+
code?: string;
|
|
26
|
+
backup_code?: string;
|
|
27
|
+
}): Promise<AuthResponse>;
|
|
28
|
+
totpSetup(): Promise<TotpSetupResponse>;
|
|
29
|
+
totpSetupConfirm(code: string): Promise<TotpSetupConfirmResponse>;
|
|
30
|
+
totpDisable(request: TotpDisableRequest): Promise<StatusResponse>;
|
|
31
|
+
totpDisableOtpRequest(): Promise<OtpRequestResponse>;
|
|
32
|
+
verificationGet(challengeId: string): Promise<VerificationEnvelope>;
|
|
33
|
+
verificationInitiate(challengeId: string, factor: VerificationFactorId): Promise<VerificationInitiateResponse>;
|
|
34
|
+
verificationComplete(challengeId: string, body: Record<string, unknown>): Promise<VerificationCompleteResponse>;
|
|
35
|
+
securityStatus(): Promise<SecurityStatus>;
|
|
36
|
+
sessions(): Promise<readonly AuthSession[]>;
|
|
37
|
+
confirmSession(id: string): Promise<StatusResponse>;
|
|
38
|
+
revokeSession(id: string): Promise<StatusResponse>;
|
|
39
|
+
revokeOtherSessions(): Promise<StatusResponse>;
|
|
40
|
+
tokenRefresh(refresh?: string): Promise<RefreshResponse>;
|
|
41
|
+
qrGenerate(type: QrType, redirectUrl: string, allowUnauthenticatedScanner?: boolean): Promise<QrGenerateResponse>;
|
|
42
|
+
qrStatus(key: string): Promise<QrStatusResponse>;
|
|
43
|
+
qrConfirm(key: string): Promise<StatusResponse>;
|
|
44
|
+
qrReject(key: string): Promise<StatusResponse>;
|
|
45
|
+
magicRequest(email: string, redirectUrl?: string): Promise<StatusResponse>;
|
|
46
|
+
passkeys(): Promise<readonly Passkey[]>;
|
|
47
|
+
passkeyRegisterBegin(): Promise<PasskeyRegisterBeginResponse>;
|
|
48
|
+
passkeyRegisterComplete(credential: unknown, deviceName?: string): Promise<Passkey>;
|
|
49
|
+
passkeyAuthenticateBegin(email?: string): Promise<PasskeyAuthenticateBeginResponse>;
|
|
50
|
+
passkeyAuthenticateComplete(sessionKey: string, credential: unknown): Promise<AuthResponse>;
|
|
51
|
+
passkeyRemove(id: string): Promise<void>;
|
|
52
|
+
changeInstantRequestOld(channel: OtpChannel): Promise<OtpRequestResponse>;
|
|
53
|
+
changeInstantVerifyOld(channel: OtpChannel, code: string): Promise<ChangeOldVerifiedResponse>;
|
|
54
|
+
changeInstantRequestNew(channel: OtpChannel, value: string, changeToken: string): Promise<OtpRequestResponse>;
|
|
55
|
+
changeInstantVerifyNew(channel: OtpChannel, value: string, code: string, changeToken: string): Promise<AuthResponse>;
|
|
56
|
+
changeDelayedInitiate(channel: OtpChannel, value: string): Promise<DelayedChangeInitiatedResponse>;
|
|
57
|
+
changeDelayedStatus(channel: OtpChannel): Promise<DelayedChangeStatus>;
|
|
58
|
+
changeDelayedCancel(channel: OtpChannel, changeRequestId: string): Promise<StatusResponse>;
|
|
59
|
+
ssoLookup(domain: string): Promise<SsoLookupResponse>;
|
|
60
|
+
auditLog(page?: number): Promise<AuditPage>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build the auth API bound to an injected {@link StapelClient} (per-module
|
|
64
|
+
* override from `StapelProvider`, the fork-resolution seam of §7.2). All
|
|
65
|
+
* mutations carry the CSRF header.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createAuthApi(client: StapelClient): AuthApi;
|
|
68
|
+
//# sourceMappingURL=authApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authApi.d.ts","sourceRoot":"","sources":["../../src/api/authApi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSRF rule for cookie-authenticated browser clients (auth-sa.md §"CSRF"):
|
|
3
|
+
* the simplest SPA rule is to always send `X-Requested-With: XMLHttpRequest`
|
|
4
|
+
* on mutating requests. Header-token clients are exempt but it is harmless
|
|
5
|
+
* for them, so we send it on every mutation.
|
|
6
|
+
*/
|
|
7
|
+
const CSRF_HEADERS = {
|
|
8
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
9
|
+
};
|
|
10
|
+
function mutating(options) {
|
|
11
|
+
return {
|
|
12
|
+
...options,
|
|
13
|
+
headers: { ...CSRF_HEADERS, ...options?.headers },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the auth API bound to an injected {@link StapelClient} (per-module
|
|
18
|
+
* override from `StapelProvider`, the fork-resolution seam of §7.2). All
|
|
19
|
+
* mutations carry the CSRF header.
|
|
20
|
+
*/
|
|
21
|
+
export function createAuthApi(client) {
|
|
22
|
+
return {
|
|
23
|
+
client,
|
|
24
|
+
capabilities: () => client.get("/capabilities/"),
|
|
25
|
+
me: () => client.get("/me/"),
|
|
26
|
+
logout: () => client.post("/logout/", undefined, mutating()),
|
|
27
|
+
otpRequest: (channel, value, captchaToken) => client.post(`/${channel}/request/`, captchaToken === undefined
|
|
28
|
+
? { [channel]: value }
|
|
29
|
+
: { [channel]: value, captcha_token: captchaToken }, mutating()),
|
|
30
|
+
otpVerify: (channel, value, code) => client.post(`/${channel}/verify/`, { [channel]: value, code }, mutating()),
|
|
31
|
+
passwordLogin: (login, password) => client.post("/password/login/", { login, password }, mutating()),
|
|
32
|
+
passwordMethods: () => client.get("/password/methods/"),
|
|
33
|
+
passwordChange: (oldPassword, newPassword) => client.post("/password/change/", { old_password: oldPassword, new_password: newPassword }, mutating()),
|
|
34
|
+
passwordChangeOtpRequest: (method) => client.post("/password/change/otp/request/", { method }, mutating()),
|
|
35
|
+
passwordChangeOtpVerify: (method, code, newPassword) => client.post("/password/change/otp/verify/", { method, code, new_password: newPassword }, mutating()),
|
|
36
|
+
passwordResetRequest: (channel, value) => client.post(`/password/reset/${channel}/request/`, { [channel]: value }, mutating()),
|
|
37
|
+
passwordResetVerify: (channel, value, code, newPassword) => client.post(`/password/reset/${channel}/verify/`, { [channel]: value, code, new_password: newPassword }, mutating()),
|
|
38
|
+
anonymous: (deviceId) => client.post("/anonymous/", deviceId === undefined ? {} : { device_id: deviceId }, mutating()),
|
|
39
|
+
oauthLogin: (provider, accessToken) => client.post("/oauth/login/", { provider, access_token: accessToken }, mutating()),
|
|
40
|
+
totpChallengeVerify: (challengeToken, proof) => client.post("/totp/challenge/verify/", { challenge_token: challengeToken, ...proof }, mutating()),
|
|
41
|
+
totpSetup: () => client.post("/totp/setup/", undefined, mutating()),
|
|
42
|
+
totpSetupConfirm: (code) => client.post("/totp/setup/confirm/", { code }, mutating()),
|
|
43
|
+
totpDisable: (request) => client.post("/totp/disable/", request, mutating()),
|
|
44
|
+
totpDisableOtpRequest: () => client.post("/totp/disable-otp/request/", undefined, mutating()),
|
|
45
|
+
verificationGet: (challengeId) => client.get(`/verification/${challengeId}/`),
|
|
46
|
+
verificationInitiate: (challengeId, factor) => client.post(`/verification/${challengeId}/initiate/`, { factor }, mutating()),
|
|
47
|
+
verificationComplete: (challengeId, body) => client.post(`/verification/${challengeId}/complete/`, body, mutating()),
|
|
48
|
+
securityStatus: () => client.get("/security/status/"),
|
|
49
|
+
sessions: () => client.get("/sessions/"),
|
|
50
|
+
confirmSession: (id) => client.post(`/sessions/${id}/confirm/`, undefined, mutating()),
|
|
51
|
+
revokeSession: (id) => client.delete(`/sessions/${id}/`, mutating()),
|
|
52
|
+
revokeOtherSessions: () => client.delete("/sessions/", mutating()),
|
|
53
|
+
tokenRefresh: (refresh) => refresh === undefined
|
|
54
|
+
? client.get("/token/refresh/")
|
|
55
|
+
: client.post("/token/refresh/", { refresh }, mutating()),
|
|
56
|
+
qrGenerate: (type, redirectUrl, allowUnauthenticatedScanner) => client.post("/qr/generate/", allowUnauthenticatedScanner === undefined
|
|
57
|
+
? { type, redirect_url: redirectUrl }
|
|
58
|
+
: {
|
|
59
|
+
type,
|
|
60
|
+
redirect_url: redirectUrl,
|
|
61
|
+
allow_unauthenticated_scanner: allowUnauthenticatedScanner,
|
|
62
|
+
}, mutating()),
|
|
63
|
+
qrStatus: (key) => client.get(`/qr/${key}/status/`),
|
|
64
|
+
qrConfirm: (key) => client.post(`/qr/${key}/confirm/`, undefined, mutating()),
|
|
65
|
+
qrReject: (key) => client.post(`/qr/${key}/reject/`, undefined, mutating()),
|
|
66
|
+
magicRequest: (email, redirectUrl) => client.post("/magic/request/", redirectUrl === undefined ? { email } : { email, redirect_url: redirectUrl }, mutating()),
|
|
67
|
+
passkeys: () => client
|
|
68
|
+
.get("/passkey/")
|
|
69
|
+
.then((r) => r.passkeys),
|
|
70
|
+
passkeyRegisterBegin: () => client.post("/passkey/register/begin/", undefined, mutating()),
|
|
71
|
+
passkeyRegisterComplete: (credential, deviceName) => client.post("/passkey/register/complete/", deviceName === undefined
|
|
72
|
+
? { credential }
|
|
73
|
+
: { credential, device_name: deviceName }, mutating()),
|
|
74
|
+
passkeyAuthenticateBegin: (email) => client.post("/passkey/authenticate/begin/", email === undefined ? {} : { email }, mutating()),
|
|
75
|
+
passkeyAuthenticateComplete: (sessionKey, credential) => client.post("/passkey/authenticate/complete/", { session_key: sessionKey, credential }, mutating()),
|
|
76
|
+
passkeyRemove: (id) => client.delete(`/passkey/${id}/`, mutating()),
|
|
77
|
+
changeInstantRequestOld: (channel) => client.post(`/${channel}/change/instant/request-old/`, undefined, mutating()),
|
|
78
|
+
changeInstantVerifyOld: (channel, code) => client.post(`/${channel}/change/instant/verify-old/`, { code }, mutating()),
|
|
79
|
+
changeInstantRequestNew: (channel, value, changeToken) => client.post(`/${channel}/change/instant/request-new/`, { [channel]: value, change_token: changeToken }, mutating()),
|
|
80
|
+
changeInstantVerifyNew: (channel, value, code, changeToken) => client.post(`/${channel}/change/instant/verify-new/`, { [channel]: value, code, change_token: changeToken }, mutating()),
|
|
81
|
+
changeDelayedInitiate: (channel, value) => client.post(`/${channel}/change/delayed/initiate/`, { [channel]: value }, mutating()),
|
|
82
|
+
changeDelayedStatus: (channel) => client.get(`/${channel}/change/delayed/status/`),
|
|
83
|
+
changeDelayedCancel: (channel, changeRequestId) => client.post(`/${channel}/change/delayed/cancel/`, { change_request_id: changeRequestId }, mutating()),
|
|
84
|
+
ssoLookup: (domain) => client.get("/sso/lookup/", { query: { domain } }),
|
|
85
|
+
auditLog: (page) => client.get("/security/audit/", {
|
|
86
|
+
query: page === undefined ? {} : { page },
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=authApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authApi.js","sourceRoot":"","sources":["../../src/api/authApi.ts"],"names":[],"mappings":"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"}
|