ab-ecosystem-sso 0.1.9 → 0.1.10

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 CHANGED
@@ -1,11 +1,29 @@
1
1
  # ab-ecosystem-sso
2
2
 
3
- Drop-in React auth & account UI for the partner-ecosystem **Auth-as-a-Service** backend — login, signup, forgot-password, two-factor, session validation, and a full Account Settings modal (profile, wallet, plans & add-ons, credit wallets), all wired to the `/api/v1/projects/:projectId/...` endpoints.
3
+ Drop-in React auth & account UI for the partner-ecosystem **Auth-as-a-Service** backend. Ships production-ready screens — login (with 2FA), signup, forgot-password, session validation, and a full **Account Settings** modal (profile, wallet, plans & add-ons, credit wallets) — all wired to the `/api/v1/projects/:projectId/...` endpoints, plus low-level helpers (`getUser`, `logout`, `useSession`) for building your own UI.
4
4
 
5
- - ⚛️ React 18 / 19 components, shipped as ESM + CJS with TypeScript types.
6
- - 🎨 Self-contained, theme-aware styles (scoped so they won't leak into your app).
7
- - 🔌 Works declaratively (`<AccountSettings />`) or imperatively (`openAccountSettings()`).
8
- - 🧩 Low-level helpers (`getUser`, `logout`, `useSession`) for building your own UI.
5
+ - ⚛️ React 18 / 19, shipped as ESM + CJS with full TypeScript types.
6
+ - 🎨 Self-contained, theme-aware CSS that **won't leak into or be overridden by** your app.
7
+ - 🔌 Use it declaratively (`<AccountSettings />`) or imperatively (`openAccountSettings()`).
8
+ - 🧩 Every network call also available as a plain function for custom UIs.
9
+
10
+ ---
11
+
12
+ ## Table of contents
13
+
14
+ - [Installation](#installation)
15
+ - [Configuration basics](#configuration-basics)
16
+ - [Auth screens](#auth-screens) — `Login` · `Signup` · `ForgotPassword`
17
+ - [Session management](#session-management) — `SessionProvider`, enforce subscription
18
+ - [Account Settings modal](#account-settings-modal) — `openAccountSettings` / `AccountSettings`
19
+ - [Logout](#logout) — `logout` · `LogoutButton` · `logoutApi`
20
+ - [Fetching the current user](#fetching-the-current-user) — `getUser`
21
+ - [Fetching user data](#fetching-user-data) — `getUserProfile` · `getUserWallet` · `getUserCreditWallet` · `getUserSubscription`
22
+ - [Two-factor setup](#two-factor-setup)
23
+ - [Hooks](#hooks)
24
+ - [Theming](#theming)
25
+ - [API reference](#api-reference)
26
+ - [TypeScript](#typescript)
9
27
 
10
28
  ---
11
29
 
@@ -15,35 +33,35 @@ Drop-in React auth & account UI for the partner-ecosystem **Auth-as-a-Service**
15
33
  npm install ab-ecosystem-sso
16
34
  ```
17
35
 
18
- `react` and `react-dom` are **peer dependencies** (v18 or v19) — make sure they're installed in your app.
36
+ `react` and `react-dom` (v18 or v19) are **peer dependencies** — install them in your app.
19
37
 
20
38
  ### Import the stylesheet once
21
39
 
22
- The components ship a single CSS file. Import it once at your app's entry point:
23
-
24
40
  ```ts
25
41
  import "ab-ecosystem-sso/styles.css";
26
42
  ```
27
43
 
28
- Styles are scoped (Tailwind `sso:` prefix + a `[data-aas]` root on the settings modal) so they won't collide with your app's CSS.
44
+ The CSS is scoped (a `[data-aas]` root + the `sso:` utility prefix) and shipped **unlayered**, so it never touches your app's elements and your app's resets (e.g. Tailwind's preflight) never override it. No extra `@layer` setup needed.
29
45
 
30
46
  ---
31
47
 
32
- ## Configuration
48
+ ## Configuration basics
33
49
 
34
- Every component/function takes:
50
+ Almost everything takes the same three inputs:
35
51
 
36
- | Prop | Description |
52
+ | Input | Description |
37
53
  | --- | --- |
38
- | `projectId` | Your project id, e.g. `436582-forms.abprojects.com`. Required. |
39
- | `token` | The auth JWT (from the login response). Required for authenticated calls. |
40
- | `apiBaseUrl` | API origin override, e.g. `https://partner-api.example.com`. Falls back to the `VITE_AUTH_API_BASE_URL` env var. |
54
+ | `projectId` | Your project id, e.g. `436582-forms.abprojects.com`. **Required.** |
55
+ | `token` | The auth JWT from the login response. Required for authenticated calls. |
56
+ | `apiBaseUrl` | API origin, e.g. `https://partner-api.example.com`. Falls back to the `VITE_AUTH_API_BASE_URL` env var. |
57
+
58
+ You own the token: persist it (e.g. `localStorage`) on `onSuccess`, pass it back into the authenticated components/functions, and clear it on logout.
41
59
 
42
60
  ---
43
61
 
44
62
  ## Auth screens
45
63
 
46
- ### Login
64
+ ### `Login`
47
65
 
48
66
  ```tsx
49
67
  import { Login } from "ab-ecosystem-sso";
@@ -56,9 +74,9 @@ function LoginPage() {
56
74
  apiBaseUrl="https://your-api"
57
75
  onSuccess={({ token, user }) => {
58
76
  localStorage.setItem("token", token);
59
- window.location.href = "/dashboard";
77
+ navigate("/dashboard");
60
78
  }}
61
- onError={(err) => console.error(err)}
79
+ onError={(err) => toast.error(err.message)}
62
80
  onForgotPassword={() => navigate("/forgot-password")}
63
81
  onSignupClick={() => navigate("/signup")}
64
82
  />
@@ -66,33 +84,35 @@ function LoginPage() {
66
84
  }
67
85
  ```
68
86
 
69
- `Login` fetches the project's `auth-config` (captcha, 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 required) and completes the flow before calling `onSuccess`.
87
+ `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.
70
88
 
71
- ### Signup & Forgot password
89
+ ### `Signup` and `ForgotPassword`
72
90
 
73
91
  ```tsx
74
92
  import { Signup, ForgotPassword } from "ab-ecosystem-sso";
75
93
 
76
- <Signup projectId="…" onSuccess={…} onLoginClick={() => navigate("/login")} />
94
+ <Signup projectId="…" onSuccess={handleAuth} onLoginClick={() => navigate("/login")} />
77
95
  <ForgotPassword projectId="…" onBackToLogin={() => navigate("/login")} />
78
96
  ```
79
97
 
80
- ### Common auth props
98
+ ### Shared auth props
81
99
 
82
100
  | Prop | Type | Notes |
83
101
  | --- | --- | --- |
84
- | `projectId` | `string` | Required. |
102
+ | `projectId` | `string` | **Required.** |
85
103
  | `apiBaseUrl` | `string` | Optional API origin. |
86
104
  | `config` | `AuthConfig` | Pre-fetched auth-config to skip the internal request. |
87
- | `onSuccess` | `({ token, user }) => void` | Called on successful auth. |
88
- | `onError` | `(error: Error) => void` | Called on failure. |
89
- | `classNames` | `AuthClassNames` | Per-element class overrides (`input`, `button`, `card`, …). |
105
+ | `onSuccess` | `({ token, user }) => void` | On successful auth. |
106
+ | `onError` | `(error: Error) => void` | On failure. |
107
+ | `classNames` | `AuthClassNames` | Per-element class overrides: `card`, `input`, `label`, `button`, `link`, `captcha`, `error`, … |
108
+
109
+ Per-screen extras: `Login` adds `onForgotPassword` / `onSignupClick`; `Signup` adds `onLoginClick`; `ForgotPassword` adds `onBackToLogin`.
90
110
 
91
111
  ---
92
112
 
93
- ## Session validation
113
+ ## Session management
94
114
 
95
- Wrap authenticated areas of your app in `SessionProvider`. It validates the token against `/me` and renders children only when the session is valid; otherwise it calls `onSessionInvalid` (navigate to login there).
115
+ Wrap authenticated areas in `SessionProvider`. It validates the token against `/me` and renders children only when the session is valid; otherwise it calls `onSessionInvalid` (redirect to login there).
96
116
 
97
117
  ```tsx
98
118
  import { SessionProvider } from "ab-ecosystem-sso";
@@ -107,9 +127,17 @@ import { SessionProvider } from "ab-ecosystem-sso";
107
127
  </SessionProvider>
108
128
  ```
109
129
 
130
+ | Prop | Type | Notes |
131
+ | --- | --- | --- |
132
+ | `projectId` | `string` | **Required.** |
133
+ | `token` | `string \| null` | Null ⇒ treated as signed-out. |
134
+ | `onSessionInvalid` | `() => void` | Called on invalid/expired session or logout. |
135
+ | `apiBaseUrl` | `string` | Optional. |
136
+ | `enforceActiveSubscription` | `boolean` | See below. |
137
+
110
138
  ### Enforce an active subscription
111
139
 
112
- Pass `enforceActiveSubscription` and, once authenticated, the provider checks the user's subscription. If none is active it opens the Account Settings modal on the **Plans** section — and blocks closing until a subscription exists (re-checking on each close attempt).
140
+ With `enforceActiveSubscription`, once authenticated the provider checks the user's subscription. If none is active it opens the Account Settings modal on the **Plans** section — and **blocks closing until a subscription exists** (it re-checks on every close attempt).
113
141
 
114
142
  ```tsx
115
143
  <SessionProvider … enforceActiveSubscription>
@@ -121,7 +149,7 @@ Pass `enforceActiveSubscription` and, once authenticated, the provider checks th
121
149
 
122
150
  ## Account Settings modal
123
151
 
124
- A full settings surface: **General** (profile), **Account** (email, change password, 2FA, sessions), **Wallet & Balance**, **Plans & Add-ons**, and **Credit Wallets**. It loads/saves via the live API when given `projectId` + `token`.
152
+ A complete settings surface with these sections: **General** (profile), **Account** (email, change password, 2FA, sessions), **Wallet & Balance** (balances + transaction history), **Plans & Add-ons** (subscribe, multi-subscribe add-ons, markdown plan details), and **Credit Wallets** (per-wallet balance, logs, top-up). It loads and saves via the live API whenever `projectId` + `token` are supplied (otherwise it renders sample data).
125
153
 
126
154
  ### Imperative (recommended)
127
155
 
@@ -134,14 +162,14 @@ import { openAccountSettings } from "ab-ecosystem-sso";
134
162
  projectId: "your-project-id",
135
163
  token,
136
164
  apiBaseUrl: "https://your-api",
137
- initialSection: "plans", // optional
165
+ initialSection: "plans", // optional
138
166
  onLogout: () => { localStorage.clear(); navigate("/login"); },
139
167
  })}>
140
168
  Settings
141
169
  </button>
142
170
  ```
143
171
 
144
- `openAccountSettings(options)` returns `{ close() }` to dismiss it programmatically.
172
+ `openAccountSettings(options)` returns a handle: `{ close() }` to dismiss it programmatically.
145
173
 
146
174
  ### Declarative
147
175
 
@@ -161,7 +189,25 @@ const [open, setOpen] = useState(false);
161
189
  />
162
190
  ```
163
191
 
164
- `initialSection` is one of `"general" | "account" | "billing" | "wallet" | "plans" | "credits"`.
192
+ ### Props
193
+
194
+ | Prop | Type | Notes |
195
+ | --- | --- | --- |
196
+ | `open` | `boolean` | **Required** (declarative). |
197
+ | `onOpenChange` | `(open: boolean) => void` | **Required** (declarative). |
198
+ | `projectId` | `string` | Enables live API mode (with `token`). |
199
+ | `token` | `string` | Bearer token. |
200
+ | `apiBaseUrl` | `string` | Optional API origin. |
201
+ | `initialSection` | `"general" \| "account" \| "wallet" \| "plans" \| "credits"` | First section shown. Default `"general"`. |
202
+ | `title` | `string` | Modal heading. Default `"Settings"`. |
203
+ | `data` | `Partial<AccountSettingsData>` | Override sample data (non-API mode / previews). |
204
+ | `onLogout` | `() => void` | Sign-out action (closes the modal, then runs this). |
205
+ | `onSaveProfile` | `(profile) => void` | Fires alongside the profile save. |
206
+ | `onChangePassword` | `(data) => void` | Fires alongside the password change. |
207
+ | `onChangePlan` | `(planId) => void` | Fires when a plan is subscribed. |
208
+ | `onUpdateWhatsApp`, `onToggleTwoFactor`, `onRevokeSession`, `onAddFunds` | callbacks | Observers for the respective actions. |
209
+
210
+ > 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.
165
211
 
166
212
  ---
167
213
 
@@ -172,7 +218,7 @@ Three options, from most convenient to most raw:
172
218
  ```tsx
173
219
  import { LogoutButton, logout, logoutApi } from "ab-ecosystem-sso";
174
220
 
175
- // 1) Ready-made button (no SessionProvider needed in standalone mode)
221
+ // 1) Ready-made button (works standalone, no SessionProvider needed)
176
222
  <LogoutButton
177
223
  projectId="your-project-id"
178
224
  token={token}
@@ -186,30 +232,63 @@ onClick={() => logout({
186
232
  token,
187
233
  apiBaseUrl: "https://your-api",
188
234
  onSuccess: () => { localStorage.clear(); navigate("/login"); },
235
+ onError: (e) => console.error(e),
189
236
  })}
190
237
 
191
238
  // 3) The raw endpoint call
192
239
  await logoutApi("your-project-id", token, "https://your-api");
193
240
  ```
194
241
 
195
- `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 />` (no props) uses the provider's logout instead.
242
+ `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.
196
243
 
197
244
  ---
198
245
 
199
- ## Fetch the current user
246
+ ## Fetching the current user
200
247
 
201
- Populate your own header/profile-dropdown from `/me`:
248
+ Populate your own header / profile-dropdown from `/me`:
202
249
 
203
250
  ```tsx
204
251
  import { getUser } from "ab-ecosystem-sso";
205
252
 
206
253
  const res = await getUser("your-project-id", token, "https://your-api");
207
- const name = res.data.profile.name.first; // { success, data: { id, username, profile, mainWallet, coinWallet } }
254
+ // res = { success, data: { id, username, profile: { name, email, address, … }, mainWallet, coinWallet } }
255
+ const firstName = res.data.profile.name.first;
208
256
  ```
209
257
 
258
+ `getUser` returns the raw response envelope and throws `AuthApiError` on a non-2xx response.
259
+
210
260
  ---
211
261
 
212
- ## Two-factor setup (standalone)
262
+ ## Fetching user data
263
+
264
+ When you want clean, ready-to-render data instead of the raw envelope, use these getters. Each **fetches and maps** in one call, and all share the same `(projectId, token, apiBaseUrl?)` signature.
265
+
266
+ ```ts
267
+ import {
268
+ getUserProfile,
269
+ getUserWallet,
270
+ getUserCreditWallet,
271
+ getUserSubscription,
272
+ } from "ab-ecosystem-sso";
273
+
274
+ const profile = await getUserProfile("your-project-id", token, "https://your-api");
275
+ const { mainWallet, coinWallet } = await getUserWallet("your-project-id", token, "https://your-api");
276
+ const creditWallets = await getUserCreditWallet("your-project-id", token, "https://your-api");
277
+ const subscription = await getUserSubscription("your-project-id", token, "https://your-api");
278
+ ```
279
+
280
+ | Function | Endpoint | Returns |
281
+ | --- | --- | --- |
282
+ | `getUserProfile` | `GET /me` | `MappedProfile` — flat fields (name, `email`/`username`, address, phone, `mainWallet`/`coinWallet`). |
283
+ | `getUserWallet` | `GET /me` | `UserWallets` — just `{ mainWallet, coinWallet }` balances. |
284
+ | `getUserCreditWallet` | `GET /credit-wallets` | `CreditWallet[]` — each `{ id, label, balance, balanceFormatted, creditUnit, fundSource, rechargeable }`. |
285
+ | `getUserSubscription` | `GET /subscription` | `MappedSubscription` — `planId` (empty if none), `status`, `renewsAt`, `subscribedIds`, `packageCounts`. |
286
+
287
+ > `getUserProfile` and `getUserWallet` both read `/me`; `getUserWallet` just narrows the same response to the wallet balances. All four throw `AccountApiError` on a non-2xx response.
288
+
289
+ ---
290
+
291
+ ## Two-factor setup
213
292
 
214
293
  For a settings page outside the modal:
215
294
 
@@ -222,6 +301,7 @@ import { TwoFactorSetup } from "ab-ecosystem-sso";
222
301
  apiBaseUrl="https://your-api"
223
302
  onEnabled={() => …}
224
303
  onDisabled={() => …}
304
+ onError={(err) => …}
225
305
  />
226
306
  ```
227
307
 
@@ -239,22 +319,40 @@ import { TwoFactorSetup } from "ab-ecosystem-sso";
239
319
 
240
320
  ## Theming
241
321
 
242
- The UI reads CSS custom properties (design tokens), so you can retheme without touching the components. Override the `--aas-*` variables on `:root` (or a parent) after importing the stylesheet:
322
+ 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:
243
323
 
244
324
  ```css
245
325
  :root {
246
- --aas-primary: #4f46e5;
247
- --aas-primary-foreground: #ffffff;
248
- --aas-background: #ffffff;
249
- --aas-foreground: #0f172a;
250
- --aas-muted: #f1f5f9;
251
- --aas-muted-foreground: #64748b;
252
- --aas-border: #e2e8f0;
253
- --aas-radius: 0.5rem;
326
+ --aas-primary: oklch(0.55 0.22 265);
327
+ --aas-primary-foreground: oklch(0.99 0 0);
328
+ --aas-background: oklch(1 0 0);
329
+ --aas-foreground: oklch(0.15 0 0);
330
+ --aas-muted: oklch(0.97 0 0);
331
+ --aas-muted-foreground: oklch(0.55 0 0);
332
+ --aas-border: oklch(0.92 0 0);
333
+ --aas-radius: 0.625rem;
254
334
  }
255
335
  ```
256
336
 
257
- Styles are dark-mode/theme aware and scoped to the components, so they won't affect the rest of your app.
337
+ 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.
338
+
339
+ ---
340
+
341
+ ## API reference
342
+
343
+ Beyond the components, these functions are exported for building custom UIs:
344
+
345
+ | Function | Endpoint |
346
+ | --- | --- |
347
+ | `getUser(projectId, token, apiBaseUrl?)` | `GET /me` (raw envelope) |
348
+ | `getUserProfile(projectId, token, apiBaseUrl?)` | `GET /me` → `MappedProfile` |
349
+ | `getUserWallet(projectId, token, apiBaseUrl?)` | `GET /me` → `UserWallets` |
350
+ | `getUserCreditWallet(projectId, token, apiBaseUrl?)` | `GET /credit-wallets` → `CreditWallet[]` |
351
+ | `getUserSubscription(projectId, token, apiBaseUrl?)` | `GET /subscription` → `MappedSubscription` |
352
+ | `logout(options)` | `POST /auth/logout` + callbacks |
353
+ | `logoutApi(projectId, token, apiBaseUrl?)` | `POST /auth/logout` |
354
+
355
+ > The Account Settings modal also drives a wider set of endpoints internally — profile (`/me`, `/profile`), password (`/account`), 2FA, WhatsApp, plans/subscription (`/plans`, `/subscribe`, `/subscription`), wallet (`/wallet/logs`, `/recharge-links`), and credit wallets (`/credit-wallets`, `/credit-wallets/:id/logs`, `/credit-wallets/:id/recharge`). If you need any of these as standalone helpers, open an issue and they can be exported.
258
356
 
259
357
  ---
260
358
 
@@ -265,11 +363,13 @@ Everything is fully typed. Notable exports:
265
363
  ```ts
266
364
  import type {
267
365
  LoginProps, SignupProps, ForgotPasswordProps, AuthClassNames,
268
- AccountSettingsProps, AccountSettingsSection,
366
+ AccountSettingsProps, AccountSettingsSection, AccountSettingsData, ProfileFormValues,
269
367
  OpenAccountSettingsOptions, AccountSettingsHandle,
270
368
  AuthConfig, CaptchaConfig, CardLayoutConfig,
271
- LoginRequest, AuthResponse, LogoutOptions,
369
+ LoginRequest, SignupRequest, ForgotPasswordRequest, AuthResponse, LogoutOptions,
370
+ MappedProfile, WalletInfo, UserWallets, CreditWallet, MappedSubscription,
272
371
  } from "ab-ecosystem-sso";
372
+
273
373
  import { AuthApiError } from "ab-ecosystem-sso";
274
374
  ```
275
375