ab-ecosystem-sso 0.1.18 → 0.1.19
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/README.md +123 -2
- package/dist/ab-ecosystem-sso.cjs +25 -25
- package/dist/ab-ecosystem-sso.js +3347 -3203
- package/dist/index.d.ts +47 -3
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ Drop-in React auth & account UI for the partner-ecosystem **Auth-as-a-Service**
|
|
|
4
4
|
|
|
5
5
|
- ⚛️ React 18 / 19, shipped as ESM + CJS with full TypeScript types.
|
|
6
6
|
- 🎨 Self-contained, theme-aware CSS that **won't leak into or be overridden by** your app.
|
|
7
|
+
- 🌗 Built-in **light and dark mode** — per component, via a single `mode` prop.
|
|
7
8
|
- 🔌 Use it declaratively (`<AccountSettings />`) or imperatively (`openAccountSettings()`).
|
|
8
9
|
- 🧩 Every network call also available as a plain function for custom UIs.
|
|
9
10
|
|
|
@@ -21,6 +22,7 @@ Drop-in React auth & account UI for the partner-ecosystem **Auth-as-a-Service**
|
|
|
21
22
|
- [Fetching user data](#fetching-user-data) — `getUserProfile` · `getUserWallet` · `getUserCreditWallet` · `getUserSubscription`
|
|
22
23
|
- [Two-factor setup](#two-factor-setup)
|
|
23
24
|
- [Hooks](#hooks)
|
|
25
|
+
- [Light & dark mode](#light--dark-mode)
|
|
24
26
|
- [Theming](#theming)
|
|
25
27
|
- [API reference](#api-reference)
|
|
26
28
|
- [TypeScript](#typescript)
|
|
@@ -84,6 +86,8 @@ function LoginPage() {
|
|
|
84
86
|
}
|
|
85
87
|
```
|
|
86
88
|
|
|
89
|
+
The password field has a **show/hide (eye) toggle** — it always starts masked, and the same control appears on `Signup`, the `ForgotPassword` reset step, and the Account Settings change-password modal.
|
|
90
|
+
|
|
87
91
|
`Login` first fetches the project's `auth-config` (captcha provider/site-key, branding, which links to show) and renders accordingly. If the account has **2FA** enabled it automatically shows a **segmented 6-digit OTP** step (with captcha when the project requires it), and only calls `onSuccess` after verification succeeds.
|
|
88
92
|
|
|
89
93
|
**Magic-link sign-in** works automatically: point your magic link at `/login#<access_token>` (a bare `/login#access_token=<token>` is also accepted). On mount, `Login` reads the token from the URL hash, validates it with a `/me` call, and — if valid — strips it from the URL and fires `onSuccess({ token, user })`, exactly like a password sign-in. While it validates, a loading state shows; if the token is invalid or expired, it falls back to the normal sign-in form with a short notice (and calls `onError`). No extra wiring needed beyond routing the link to your login page.
|
|
@@ -177,6 +181,7 @@ On success it calls `onVerified` (send the user to login) and shows a **Continue
|
|
|
177
181
|
| `onSuccess` | `({ token, user }) => void` | On successful auth. |
|
|
178
182
|
| `onError` | `(error: Error) => void` | On failure. |
|
|
179
183
|
| `classNames` | `AuthClassNames` | Per-element class overrides: `card`, `input`, `label`, `button`, `link`, `captcha`, `error`, … |
|
|
184
|
+
| `mode` | `"light" \| "dark"` | Colour scheme. Default `"light"`. See [Light & dark mode](#light--dark-mode). |
|
|
180
185
|
|
|
181
186
|
Per-screen extras: `Login` adds `onForgotPassword` / `onSignupClick`; `Signup` adds `onLoginClick` / `countries`; `ForgotPassword` adds `onBackToLogin` / `resetToken` / `onResetSuccess`; `VerifyEmail` adds `token` / `onVerified` / `onLoginClick` / `onSignupClick` (and has no `onSuccess`, since no session is created).
|
|
182
187
|
|
|
@@ -271,8 +276,10 @@ const [open, setOpen] = useState(false);
|
|
|
271
276
|
| `token` | `string` | Bearer token. |
|
|
272
277
|
| `apiBaseUrl` | `string` | Optional API origin. |
|
|
273
278
|
| `initialSection` | `"general" \| "account" \| "wallet" \| "plans" \| "credits"` | First section shown. Default `"general"`. |
|
|
279
|
+
| `customComponents` | `AccountSettingsCustomPanel[]` | Add your own panels to the modal. See [Custom panels](#custom-panels). |
|
|
274
280
|
| `syncUrlHash` | `boolean` | Mirror open state + active section in the URL hash. Default `true`. See [URL hash & refresh](#url-hash--refresh). |
|
|
275
281
|
| `title` | `string` | Modal heading. Default `"Settings"`. |
|
|
282
|
+
| `mode` | `"light" \| "dark"` | Colour scheme. Default `"light"`. See [Light & dark mode](#light--dark-mode). |
|
|
276
283
|
| `data` | `Partial<AccountSettingsData>` | Override sample data (non-API mode / previews). |
|
|
277
284
|
| `onLogout` | `() => void` | Sign-out action (closes the modal, then runs this). |
|
|
278
285
|
| `onSaveProfile` | `(profile) => void` | Fires alongside the profile save. |
|
|
@@ -282,6 +289,45 @@ const [open, setOpen] = useState(false);
|
|
|
282
289
|
|
|
283
290
|
> In live mode the modal performs the API calls itself (save profile, change password, subscribe, recharge, etc.); the callbacks are observation hooks, not required wiring.
|
|
284
291
|
|
|
292
|
+
### Custom panels
|
|
293
|
+
|
|
294
|
+
Add your own screens to the modal via `customComponents` — an array of `AccountSettingsCustomPanel`. Each entry renders a **trigger** (in the sidebar, pinned to the sidebar bottom, or in the header next to the close button) and shows its `component` in the modal body when activated.
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
<AccountSettings
|
|
298
|
+
open={open}
|
|
299
|
+
onOpenChange={setOpen}
|
|
300
|
+
projectId="your-project-id"
|
|
301
|
+
token={token}
|
|
302
|
+
customComponents={[
|
|
303
|
+
{
|
|
304
|
+
id: "referrals",
|
|
305
|
+
location: "sidebar", // "sidebar" | "sidebar-bottom" | "header"
|
|
306
|
+
label: "Referrals",
|
|
307
|
+
icon: <GiftIcon />, // any node (an inline SVG works best)
|
|
308
|
+
component: <ReferralsPanel />, // rendered in the modal body
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "help",
|
|
312
|
+
location: "header",
|
|
313
|
+
label: "Help",
|
|
314
|
+
icon: <HelpIcon />,
|
|
315
|
+
component: <HelpCenter />,
|
|
316
|
+
},
|
|
317
|
+
]}
|
|
318
|
+
/>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
| Field | Type | Notes |
|
|
322
|
+
| --- | --- | --- |
|
|
323
|
+
| `id` | `string` | **Required**, unique. Tracks which panel is active. |
|
|
324
|
+
| `location` | `"sidebar" \| "sidebar-bottom" \| "header"` | Where the trigger appears. Default `"sidebar"`. |
|
|
325
|
+
| `label` | `string` | Sidebar text; header button tooltip + `aria-label`. |
|
|
326
|
+
| `icon` | `ReactNode` | Optional trigger icon. |
|
|
327
|
+
| `component` | `ReactNode` | Rendered in the body when the panel is active. |
|
|
328
|
+
|
|
329
|
+
The same `customComponents` array works with the imperative `openAccountSettings({ customComponents: [...] })`. Panels are host-owned — they don't affect the URL hash, and activating one doesn't run the built-in section's API calls.
|
|
330
|
+
|
|
285
331
|
### URL hash & refresh
|
|
286
332
|
|
|
287
333
|
While the modal is open it reflects the active section in the URL hash as `#settings-<section>` (e.g. `#settings-general`, `#settings-plans`), updating as the user switches tabs and clearing it on close. This means a **page refresh can re-open the modal on the same section**. It uses `history.replaceState`, so it never adds browser-history entries and never clobbers an unrelated `#anchor`. Opt out with `syncUrlHash={false}`.
|
|
@@ -329,7 +375,7 @@ onClick={() => logout({
|
|
|
329
375
|
await logoutApi("your-project-id", token, "https://your-api");
|
|
330
376
|
```
|
|
331
377
|
|
|
332
|
-
`logout` POSTs to `/auth/logout` and **always runs `onSuccess` once the request settles** — so the user is logged out client-side even if the server call fails (`onError` fires for observability). Inside a `SessionProvider`, `<LogoutButton />` with no props uses the provider's logout instead.
|
|
378
|
+
`<LogoutButton />` also takes `mode="dark"`. `logout` POSTs to `/auth/logout` and **always runs `onSuccess` once the request settles** — so the user is logged out client-side even if the server call fails (`onError` fires for observability). Inside a `SessionProvider`, `<LogoutButton />` with no props uses the provider's logout instead.
|
|
333
379
|
|
|
334
380
|
---
|
|
335
381
|
|
|
@@ -389,6 +435,7 @@ import { TwoFactorSetup } from "ab-ecosystem-sso";
|
|
|
389
435
|
projectId="your-project-id"
|
|
390
436
|
token={token}
|
|
391
437
|
apiBaseUrl="https://your-api"
|
|
438
|
+
mode="dark" // optional — "light" (default) or "dark"
|
|
392
439
|
onEnabled={() => …}
|
|
393
440
|
onDisabled={() => …}
|
|
394
441
|
onError={(err) => …}
|
|
@@ -407,6 +454,58 @@ import { TwoFactorSetup } from "ab-ecosystem-sso";
|
|
|
407
454
|
|
|
408
455
|
---
|
|
409
456
|
|
|
457
|
+
## Light & dark mode
|
|
458
|
+
|
|
459
|
+
Every component renders in **light mode by default**. Pass `mode="dark"` to switch:
|
|
460
|
+
|
|
461
|
+
```tsx
|
|
462
|
+
<Login projectId="…" mode="dark" onSuccess={handleAuth} />
|
|
463
|
+
<Signup projectId="…" mode="dark" onLoginClick={() => navigate("/login")} />
|
|
464
|
+
<ForgotPassword projectId="…" mode="dark" />
|
|
465
|
+
<VerifyEmail projectId="…" mode="dark" />
|
|
466
|
+
<AccountSettings open={open} onOpenChange={setOpen} mode="dark" />
|
|
467
|
+
<TwoFactorSetup projectId="…" token={token} mode="dark" />
|
|
468
|
+
<LogoutButton mode="dark" />
|
|
469
|
+
|
|
470
|
+
openAccountSettings({ projectId, token, mode: "dark" });
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
`mode` is **per component**, not global — each component root carries a
|
|
474
|
+
`data-aas-theme="light" | "dark"` attribute and the palette inherits from there, so a dark
|
|
475
|
+
login card and a light settings modal can coexist on the same page.
|
|
476
|
+
|
|
477
|
+
Follow your app's own theme by passing the value you already track:
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
const { theme } = useTheme(); // your app's theme state
|
|
481
|
+
<Login projectId="…" mode={theme === "dark" ? "dark" : "light"} />
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
To follow the OS setting instead:
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
import type { AuthMode } from "ab-ecosystem-sso";
|
|
488
|
+
|
|
489
|
+
const [mode, setMode] = useState<AuthMode>(
|
|
490
|
+
() => (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light")
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
useEffect(() => {
|
|
494
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
495
|
+
const onChange = (e: MediaQueryListEvent) => setMode(e.matches ? "dark" : "light");
|
|
496
|
+
mq.addEventListener("change", onChange);
|
|
497
|
+
return () => mq.removeEventListener("change", onChange);
|
|
498
|
+
}, []);
|
|
499
|
+
|
|
500
|
+
<Login projectId="…" mode={mode} />
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
> The package deliberately does **not** read `prefers-color-scheme` itself — an auth screen
|
|
504
|
+
> that silently disagrees with the host app's theme is worse than one that follows it, and only
|
|
505
|
+
> your app knows which theme it's in.
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
410
509
|
## Theming
|
|
411
510
|
|
|
412
511
|
The UI reads `--aas-*` CSS custom properties, so you can retheme without touching the components. Override them on `:root` (or any ancestor) after importing the stylesheet:
|
|
@@ -424,6 +523,28 @@ The UI reads `--aas-*` CSS custom properties, so you can retheme without touchin
|
|
|
424
523
|
}
|
|
425
524
|
```
|
|
426
525
|
|
|
526
|
+
Dark mode is the same variables redeclared on `[data-aas-theme='dark']`, so retheming dark is symmetrical — override there and light stays untouched:
|
|
527
|
+
|
|
528
|
+
```css
|
|
529
|
+
[data-aas-theme="dark"] {
|
|
530
|
+
--aas-background: oklch(0.16 0.01 265);
|
|
531
|
+
--aas-card: oklch(0.21 0.01 265);
|
|
532
|
+
--aas-primary: oklch(0.72 0.17 265);
|
|
533
|
+
--aas-primary-foreground: oklch(0.16 0.01 265);
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Beyond the shadcn-style tokens above, a few extra ones exist so dark mode is a pure variable
|
|
538
|
+
swap — override them to fine-tune either palette:
|
|
539
|
+
|
|
540
|
+
| Token | Used for |
|
|
541
|
+
| --- | --- |
|
|
542
|
+
| `--aas-sidebar` | Account Settings sidebar panel. |
|
|
543
|
+
| `--aas-hover` / `--aas-hover-strong` | Nav/icon-button hover and active fills. |
|
|
544
|
+
| `--aas-overlay` / `--aas-overlay-strong` | The blur overlays on loading cards and settings sections. |
|
|
545
|
+
| `--aas-success` / `--aas-success-bg` | "Verified" badges and credit amounts. |
|
|
546
|
+
| `--aas-switch-track`, `--aas-switch-knob`, `--aas-switch-knob-on` | The settings toggle switch. |
|
|
547
|
+
|
|
427
548
|
The styles are theme-aware and scoped to the components — they won't affect the rest of your app, and your app's global resets can't override them.
|
|
428
549
|
|
|
429
550
|
---
|
|
@@ -456,7 +577,7 @@ Everything is fully typed. Notable exports:
|
|
|
456
577
|
|
|
457
578
|
```ts
|
|
458
579
|
import type {
|
|
459
|
-
LoginProps, SignupProps, ForgotPasswordProps, VerifyEmailProps, AuthClassNames,
|
|
580
|
+
LoginProps, SignupProps, ForgotPasswordProps, VerifyEmailProps, AuthClassNames, AuthMode,
|
|
460
581
|
AccountSettingsProps, AccountSettingsSection, AccountSettingsData, ProfileFormValues,
|
|
461
582
|
OpenAccountSettingsOptions, AccountSettingsHandle,
|
|
462
583
|
AuthConfig, CaptchaConfig, CardLayoutConfig,
|