@baliola/auth-sdk 0.5.0 → 0.7.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 CHANGED
@@ -2,6 +2,84 @@
2
2
 
3
3
  All notable changes to `@baliola/auth-sdk` are documented here.
4
4
 
5
+ ## 0.7.0
6
+
7
+ Self-service roles and a stricter login contract. baliola-auth added `POST /auth/roles/apply` and
8
+ `GET /auth/roles/status` (issue #61), dropped `flow` and `methods` from the send-login-code
9
+ response so it can no longer reveal whether an email is registered, and is making `clientId`
10
+ mandatory on every login and register body (issue #60) so that only the Console client can mint a
11
+ token carrying global roles.
12
+
13
+ ### New features
14
+
15
+ - feat(client): `auth.roles.apply({ roleName })` posts a self-service application for an
16
+ allowlisted global role and returns the account role row (`RoleApplicationStatus`);
17
+ `auth.roles.status()` returns one `RoleStatus` per self-applicable role, with `status: 'none'`
18
+ when the account never applied. Both need a session. Conflicts surface as `AuthError` with
19
+ `code` `role_already_active` or `application_pending`.
20
+ - feat(types): `ApplyRoleInput`, `RoleApplicationState`, `RoleApplicationStatus`, `RoleStatus`,
21
+ and `RolesNamespace` are exported from the root and the `./types` subpath.
22
+ - feat(errors): `ValidationError` (`code: 'validation_error'`, 400) with
23
+ `issues: ValidationIssue[]`, one `{ path, message }` per rejected request field.
24
+ baliola-auth now routes every request validation failure through the standard error
25
+ envelope under this code, so a login body without `clientId` or an `apply` with a role name
26
+ outside the allowlist is an `instanceof ValidationError`. Exported from the root and the
27
+ `./errors` subpath, together with the `ValidationIssue` type.
28
+ - Admin role management (`/admin/roles/*`: approve, revoke, list applications) is deliberately not
29
+ in this SDK. It is a client library for end-user apps; the Baliola Console keeps its own direct
30
+ client for `/admin/*`. This closes the open question on issue #61.
31
+
32
+ ### Breaking changes
33
+
34
+ - feat(client)!: `clientId` is now a required option of `createAuthClient`. Construction throws
35
+ `Error('createAuthClient: clientId is required')` when it is missing or empty, the same way
36
+ `baseUrl` is handled. Every login and register body always carries it, and
37
+ `auth.google.startUrl` no longer has its own `clientId` check. The "central" multi-project login
38
+ that omitted `clientId` is gone on the server side, so there is nothing to migrate to: pass the
39
+ client id of the project the app belongs to.
40
+ - chore(types)!: `SendLoginCodeResult` is narrowed to `{ otp: OtpInfo }`. The `flow` and `methods`
41
+ fields are removed because the server no longer sends them (baliola-auth commit `7f29126`);
42
+ whether the email is a login or a signup is only known after `verifyLoginCode`.
43
+ - Per `RELEASING.md` these are pre-1.0 breaks shipped as a minor with this explicit callout.
44
+
45
+ ## 0.6.0
46
+
47
+ Hosted Google login. baliola-auth now runs the Google OAuth round trip itself
48
+ (`GET /auth/google/start`, `GET /auth/google/callback`, `POST /auth/google/exchange`),
49
+ so an app can offer "Sign in with Google" without loading Google Identity Services. The
50
+ browser only ever sees a one-time code, never a token.
51
+
52
+ - feat(client): `auth.google.signInWithPopup(opts?)` opens the hosted login in a popup,
53
+ waits for the exit message, exchanges the one-time code, and stores the session.
54
+ `opts.returnTo` (default: current href without hash) and `opts.popup.width` /
55
+ `opts.popup.height` (default 500 x 640). Browser only.
56
+ - feat(client): `auth.google.completeRedirect(opts?)` finishes a hosted login started in
57
+ `redirect` mode on the `returnTo` page: resolves `null` when the URL carries no `code`
58
+ or `error`, verifies the nonce saved by `startUrl`, strips `code`, `error`, and `nonce`
59
+ from the URL before exchanging. Browser only.
60
+ - feat(client): `auth.google.startUrl({ mode, returnTo?, nonce })` builds the
61
+ `/auth/google/start` URL from the client's `clientId`, and `auth.google.exchange({ code })`
62
+ posts the one-time code and stores the session like every other login method. Both are
63
+ the manual path underneath the two methods above.
64
+ - feat(errors): `GoogleSignInCancelledError` (`code: 'oauth_cancelled'`,
65
+ `reason: 'popup_blocked' | 'popup_closed' | 'access_denied'`) and `GoogleSignInError`
66
+ (`code` is the exit or exchange failure: `invalid_google_code`, `email_unverified`,
67
+ `invalid_oauth_state`, `oauth_unexpected`, or the SDK-side `oauth_nonce_mismatch`).
68
+ `authErrorFromResponse` now maps server `invalid_google_code` and `email_unverified` to
69
+ `GoogleSignInError`; `status` and `code` are unchanged, so `instanceof AuthError` and
70
+ `err.code` branches keep working.
71
+ - feat(types): `GoogleExchangeInput`, `GoogleStartMode`, `GoogleStartOptions`,
72
+ `GoogleSignInWithPopupOptions`, `GoogleCompleteRedirectOptions`, and
73
+ `GoogleSignInCancelledReason` are exported from the root and the `./types` (or
74
+ `./errors`) subpath.
75
+ - feat(client)!: **Breaking.** `auth.google.login` accepts `{ idToken }` only.
76
+ `LoginWithGoogleInput` is narrowed from `{ idToken: string } | { code: string }` to
77
+ `{ idToken: string }`. The server no longer accepts `{ code }` on `/auth/google/login`
78
+ (it is a 400 now); apps that used the Google Identity Services code client should move
79
+ to `auth.google.signInWithPopup()`. `RELEASING.md` would call this removal a major
80
+ bump; the user chose 0.6.0 under the pre-1.0 rule that breaks ship as minors with an
81
+ explicit CHANGELOG callout, which this bullet is.
82
+
5
83
  ## 0.5.0
6
84
 
7
85
  - feat(types): `auth.getProfile()` now returns `account.hasPassword`, a boolean
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @baliola/auth-sdk
2
2
 
3
- Client SDK for the Baliola Auth service. Two clearly separated email flows (passwordless OTP, password + verification OTP) plus Google login, with typed methods, typed errors, auto-refresh, and cross-tab sync.
3
+ Client SDK for the Baliola Auth service. Two clearly separated email flows (passwordless OTP, password + verification OTP) plus Google login (hosted popup, hosted redirect, or Google Identity Services), with typed methods, typed errors, auto-refresh, and cross-tab sync.
4
4
 
5
5
  Guides:
6
6
 
@@ -31,7 +31,7 @@ import { createAuthClient, localStorageStore, OtpInvalidError } from '@baliola/a
31
31
 
32
32
  const auth = createAuthClient({
33
33
  baseUrl: AUTH_URL,
34
- clientId: 'your-project-client-id', // omit for a central multi-project login
34
+ clientId: 'your-project-client-id', // required; scopes every login to this project
35
35
  store: localStorageStore(), // omit for in-memory (server-side / tests)
36
36
  });
37
37
  await auth.loadSession(); // rehydrate from store at app boot
@@ -66,7 +66,7 @@ The SDK is namespaced by flow so that each method's purpose is unambiguous at th
66
66
 
67
67
  ```ts
68
68
  auth.emailOtp.sendLoginCode({ email, captchaToken });
69
- // → { flow: 'login'|'signup', methods: ('password'|'passwordless')[], otp: OtpInfo }
69
+ // → { otp: OtpInfo } (never says whether the email is registered)
70
70
 
71
71
  auth.emailOtp.verifyLoginCode({ email, code });
72
72
  // → AuthSession (also populates the session mirror)
@@ -98,15 +98,44 @@ auth.emailPassword.changePassword({ currentPassword, newPassword });
98
98
 
99
99
  ### `auth.google`
100
100
 
101
+ Three ways in. The hosted flow needs a `clientId` on the client and a `returnTo` whose origin is
102
+ one of the project's `allowedOrigins`. Google Identity Services is not loaded in your page.
103
+
101
104
  ```ts
102
- // idToken comes from the Google Identity Services button or One Tap.
103
- auth.google.login({ idToken }); // → AuthSession
105
+ import { GoogleSignInCancelledError, GoogleSignInError } from '@baliola/auth-sdk';
106
+
107
+ // 1. Hosted popup (recommended). Opens the auth server's Google login in a popup,
108
+ // exchanges the one-time code it posts back, and stores the session.
109
+ try {
110
+ const session = await auth.google.signInWithPopup();
111
+ // optional: { returnTo: '/auth/callback', popup: { width: 500, height: 640 } }
112
+ } catch (e) {
113
+ if (e instanceof GoogleSignInCancelledError) return; // e.reason: 'popup_blocked' | 'popup_closed' | 'access_denied'
114
+ if (e instanceof GoogleSignInError) return showError(e.code); // 'email_unverified', 'invalid_google_code', ...
115
+ throw e;
116
+ }
117
+
118
+ // 2. Hosted redirect. Send the tab to Google, then finish on the returnTo page.
119
+ location.assign(auth.google.startUrl({ mode: 'redirect', nonce: crypto.randomUUID() }));
120
+ // ...on the returnTo page, at boot:
121
+ const session = await auth.google.completeRedirect(); // null when the URL has no code or error
104
122
 
105
- // code comes from the OAuth popup code flow (google.accounts.oauth2.initCodeClient).
106
- // The server exchanges it for the ID token with its client secret.
107
- auth.google.login({ code }); // → AuthSession
123
+ // 3. Google Identity Services. idToken comes from the GIS button or One Tap.
124
+ auth.google.login({ idToken }); // AuthSession
108
125
  ```
109
126
 
127
+ Manual control: `startUrl({ mode: 'popup' | 'redirect', returnTo?, nonce })` builds
128
+ `GET {baseUrl}/auth/google/start`, and `exchange({ code })` turns the one-time code from the exit into
129
+ a stored session. Both are what `signInWithPopup` and `completeRedirect` use internally.
130
+
131
+ How the popup exit reaches you: the auth server's callback page runs
132
+ `window.opener.postMessage({ type: 'baliola-auth:google', nonce, code }, returnToOrigin)` on success,
133
+ or `{ type: 'baliola-auth:google', nonce, error }` on failure, then closes. `signInWithPopup` accepts
134
+ a message only when `event.origin` is the auth server origin, `type` matches, and `nonce` is the one
135
+ it generated. In `redirect` mode the exit is a 302 to `returnTo` with `code` and `nonce` (or `error`
136
+ and `nonce`) query params; `completeRedirect` checks `nonce` against the value `startUrl` saved in
137
+ `sessionStorage` and strips all three params from the URL before exchanging.
138
+
110
139
  ### Custodial wallet (smart-account signer)
111
140
 
112
141
  ```ts
@@ -114,7 +143,7 @@ import { createAuthClient } from '@baliola/auth-sdk';
114
143
  import { createRemoteSigner } from '@baliola/auth-sdk/wallet';
115
144
  import { toSmartAccount } from '@baliola/smart-account-sdk';
116
145
 
117
- const auth = createAuthClient({ baseUrl });
146
+ const auth = createAuthClient({ baseUrl, clientId });
118
147
  await auth.emailOtp.verifyLoginCode({ email, code }); // authenticated session
119
148
 
120
149
  const owner = await createRemoteSigner(auth); // viem LocalAccount, signs server-side
@@ -123,6 +152,22 @@ const account = await toSmartAccount({ owner, chain: 'macTestnet' });
123
152
 
124
153
  The private key never leaves baliola-auth; `owner.signMessage({ raw })` calls `POST /auth/wallet/sign`.
125
154
 
155
+ ### `auth.roles` (self-service role applications)
156
+
157
+ ```ts
158
+ const app = await auth.roles.apply({ roleName: 'module_deployer' }); // POST /auth/roles/apply
159
+ // app = { id, accountId, roleId, projectId, status: 'pending_approval', assignedBy, assignedAt, approvedAt, revokedAt }
160
+
161
+ const rows = await auth.roles.status(); // GET /auth/roles/status
162
+ // rows = [{ roleName, displayName, status: 'none'|'pending_approval'|'active'|'revoked', accountRoleId, assignedAt, approvedAt, revokedAt }]
163
+ ```
164
+
165
+ Both need a session. `apply` rejects with `AuthError` whose `code` is `role_already_active` or
166
+ `application_pending` (409) when there is nothing to do, and with a 400 when the role name is not
167
+ on the server's self-applicable allowlist (`ValidationError`, `issues[0].path === 'roleName'`). Approving, revoking, and listing applications are `/admin/*` endpoints and are
168
+ deliberately not part of this SDK; an admin surface such as the Baliola Console calls them with
169
+ its own client.
170
+
126
171
  ### Profile (identity read)
127
172
 
128
173
  ```ts
@@ -196,6 +241,8 @@ import {
196
241
  RateLimitedError,
197
242
  InvalidPasswordError,
198
243
  InvalidEmailError,
244
+ GoogleSignInCancelledError,
245
+ GoogleSignInError,
199
246
  } from '@baliola/auth-sdk';
200
247
 
201
248
  try {
@@ -213,19 +260,22 @@ try {
213
260
  | Error class | `code` | Status | Carries |
214
261
  | ------------------------------ | ---------------------------- | ------ | ------------------------------------------ |
215
262
  | `OtpInvalidError` | `otp_invalid` | 400 | `attemptsRemaining`, `canResendInSeconds` |
216
- | `OtpExpiredError` | `otp_expired` | 400 | |
263
+ | `OtpExpiredError` | `otp_expired` | 400 | none |
217
264
  | `MaxAttemptsError` | `max_attempts_exceeded` | 429 | `retryAfterSeconds` |
218
- | `NoPendingOtpError` | `no_pending_otp` | 400 | |
265
+ | `NoPendingOtpError` | `no_pending_otp` | 400 | none |
219
266
  | `ResendCooldownError` | `resend_cooldown` | 429 | `canResendInSeconds` |
220
- | `MaxResendsError` | `max_resends_exceeded` | 429 | |
221
- | `InvalidCredentialsError` | `invalid_credentials` | 401 | |
222
- | `NoPasswordSetError` | `no_password_set` | 400 | |
223
- | `AccountSuspendedError` | `account_suspended` | 403 | |
224
- | `EmailAlreadyHasPasswordError` | `email_already_has_password` | 409 | |
225
- | `CaptchaFailedError` | `captcha_failed` | 400 | |
267
+ | `MaxResendsError` | `max_resends_exceeded` | 429 | none |
268
+ | `InvalidCredentialsError` | `invalid_credentials` | 401 | none |
269
+ | `NoPasswordSetError` | `no_password_set` | 400 | none |
270
+ | `AccountSuspendedError` | `account_suspended` | 403 | none |
271
+ | `EmailAlreadyHasPasswordError` | `email_already_has_password` | 409 | none |
272
+ | `CaptchaFailedError` | `captcha_failed` | 400 | none |
226
273
  | `InvalidPasswordError` | `invalid_password` | 400 | `reason: 'too_short'\|'no_uppercase'\|...` |
227
- | `InvalidEmailError` | `invalid_email` | 400 | |
274
+ | `InvalidEmailError` | `invalid_email` | 400 | none |
228
275
  | `RateLimitedError` | (legacy 429) | 429 | `retryAfterSeconds`, `retryAfterHuman` |
276
+ | `GoogleSignInCancelledError` | `oauth_cancelled` | 0 | `reason: 'popup_blocked'\|'popup_closed'\|'access_denied'` |
277
+ | `GoogleSignInError` | `invalid_google_code`, `email_unverified`, `invalid_oauth_state`, `oauth_unexpected`, `oauth_nonce_mismatch` | 401 / 400 / 0 | none |
278
+ | `ValidationError` | `validation_error` | 400 | `issues: { path, message }[]` |
229
279
  | `AuthError` (base) | varies | varies | `status: 0` for network/timeout |
230
280
 
231
281
  ## Stores
@@ -249,7 +299,7 @@ Cross-tab sync (login / logout / refresh) is automatic via `BroadcastChannel`, r
249
299
  ```ts
250
300
  createAuthClient({
251
301
  baseUrl: AUTH_URL, // required, no trailing slash
252
- clientId: 'your-project-client-id', // optional; scopes the session to one project. Omit for a central login that carries every project the account has active roles in.
302
+ clientId: 'your-project-client-id', // required; sent on every login and register body
253
303
  store: memoryStore(), // default; or localStorageStore() / custom
254
304
  fetch: globalThis.fetch, // override for tests / interceptors
255
305
  timeoutMs: 15_000, // per-request, default 15s
@@ -258,7 +308,8 @@ createAuthClient({
258
308
  });
259
309
  ```
260
310
 
261
- `clientId` is constructor-only there is no per-call override. To switch projects, create a new client.
311
+ `clientId` is constructor-only, there is no per-call override. To switch projects, create a new client.
312
+ `createAuthClient` throws a plain `Error` when `clientId` is missing or empty, the same way it does for `baseUrl`.
262
313
 
263
314
  ## Decoding the JWT
264
315
 
@@ -272,7 +323,7 @@ const session = auth.getSession();
272
323
  if (session) {
273
324
  const claims = decodeJwt<AccessTokenPayload>(session.accessToken);
274
325
  // claims.accountId, claims.email
275
- // claims.projects: { id, name, clientId }[] // single entry for a scoped login; many for a central login
326
+ // claims.projects: { id, name, clientId }[] // the project the login was scoped to; the Console client also carries global roles
276
327
  // claims.roles?: string[]; claims.permissions?: string[]
277
328
  }
278
329
  ```
@@ -285,8 +336,6 @@ if (session) {
285
336
 
286
337
  ```json
287
338
  {
288
- "flow": "signup",
289
- "methods": ["passwordless"],
290
339
  "otp": {
291
340
  "expiresInSeconds": 300,
292
341
  "expiresAt": "2026-05-04T12:05:00.000Z",
@@ -309,7 +358,7 @@ if (session) {
309
358
  }
310
359
  ```
311
360
 
312
- **`auth.emailOtp.verifyLoginCode` / `auth.emailPassword.verifyRegistrationCode` / `auth.emailPassword.login` / `auth.google.login` success (`AuthSession`):**
361
+ **`auth.emailOtp.verifyLoginCode` / `auth.emailPassword.verifyRegistrationCode` / `auth.emailPassword.login` / `auth.google.login` / `auth.google.exchange` success (`AuthSession`):**
313
362
 
314
363
  ```json
315
364
  {
@@ -332,6 +381,38 @@ if (session) {
332
381
  }
333
382
  ```
334
383
 
384
+ **`auth.roles.apply` success (`RoleApplicationStatus`, HTTP 201):**
385
+
386
+ ```json
387
+ {
388
+ "id": "6f1c2d3e-0000-4000-8000-000000000001",
389
+ "accountId": "6f1c2d3e-0000-4000-8000-0000000000aa",
390
+ "roleId": "6f1c2d3e-0000-4000-8000-0000000000bb",
391
+ "projectId": null,
392
+ "status": "pending_approval",
393
+ "assignedBy": null,
394
+ "assignedAt": "2026-09-04T00:00:00.000Z",
395
+ "approvedAt": null,
396
+ "revokedAt": null
397
+ }
398
+ ```
399
+
400
+ **`auth.roles.status` success (`RoleStatus[]`):**
401
+
402
+ ```json
403
+ [
404
+ {
405
+ "roleName": "module_deployer",
406
+ "displayName": "Module Deployer",
407
+ "status": "none",
408
+ "accountRoleId": null,
409
+ "assignedAt": null,
410
+ "approvedAt": null,
411
+ "revokedAt": null
412
+ }
413
+ ]
414
+ ```
415
+
335
416
  **Verify-code failure (e.g. `OtpInvalidError`):**
336
417
 
337
418
  ```json
@@ -349,6 +430,19 @@ if (session) {
349
430
  }
350
431
  ```
351
432
 
433
+ **`auth.google.exchange` failure (`GoogleSignInError`, 401):**
434
+
435
+ ```json
436
+ {
437
+ "message": "Invalid or expired login code",
438
+ "data": null,
439
+ "error": {
440
+ "code": "invalid_google_code",
441
+ "details": { "name": "UnauthorizedError", "requestId": "..." }
442
+ }
443
+ }
444
+ ```
445
+
352
446
  ## License
353
447
 
354
448
  Proprietary — see [LICENSE](./LICENSE).
@@ -131,6 +131,54 @@ var InvalidEmailError = class extends AuthError {
131
131
  this.name = "InvalidEmailError";
132
132
  }
133
133
  };
134
+ /**
135
+ * The hosted Google sign-in ended without a session because of the user or
136
+ * the browser: the popup was blocked, the popup was closed early, or the
137
+ * user cancelled on Google's account chooser. `code` is always
138
+ * `oauth_cancelled`; branch on `reason` for the specific cause.
139
+ */
140
+ var GoogleSignInCancelledError = class extends AuthError {
141
+ reason;
142
+ constructor(init) {
143
+ super({
144
+ status: 0,
145
+ code: "oauth_cancelled",
146
+ serverMessage: `Google sign-in cancelled: ${init.reason}`,
147
+ ...init.cause !== void 0 && { cause: init.cause }
148
+ });
149
+ this.name = "GoogleSignInCancelledError";
150
+ this.reason = init.reason;
151
+ }
152
+ };
153
+ /**
154
+ * The hosted Google sign-in failed. `code` is the failure code the exit or
155
+ * the exchange returned: `invalid_oauth_state`, `invalid_google_code`,
156
+ * `email_unverified`, `oauth_unexpected`, or the SDK-side
157
+ * `oauth_nonce_mismatch`. `status` is the HTTP status when the exchange
158
+ * request failed, otherwise `0`.
159
+ */
160
+ var GoogleSignInError = class extends AuthError {
161
+ constructor(init) {
162
+ super({
163
+ ...init,
164
+ status: init.status ?? 0,
165
+ serverMessage: init.serverMessage ?? `Google sign-in failed: ${init.code}`
166
+ });
167
+ this.name = "GoogleSignInError";
168
+ }
169
+ };
170
+ /**
171
+ * Request validation failed. Thrown for `validation_error` (400) on any
172
+ * endpoint; `issues` names each rejected field.
173
+ */
174
+ var ValidationError = class extends AuthError {
175
+ issues;
176
+ constructor(init) {
177
+ super(init);
178
+ this.name = "ValidationError";
179
+ this.issues = init.issues;
180
+ }
181
+ };
134
182
  function num(d, key, fallback = 0) {
135
183
  const v = d?.[key];
136
184
  return typeof v === "number" ? v : fallback;
@@ -139,6 +187,21 @@ function str(d, key, fallback = "") {
139
187
  const v = d?.[key];
140
188
  return typeof v === "string" ? v : fallback;
141
189
  }
190
+ function issues(d) {
191
+ const raw = d?.["issues"];
192
+ if (!Array.isArray(raw)) return [];
193
+ const out = [];
194
+ for (const entry of raw) {
195
+ if (typeof entry !== "object" || entry === null) continue;
196
+ const record = entry;
197
+ if (typeof record["path"] !== "string" || typeof record["message"] !== "string") continue;
198
+ out.push({
199
+ path: record["path"],
200
+ message: record["message"]
201
+ });
202
+ }
203
+ return out;
204
+ }
142
205
  /**
143
206
  * Map a backend error response onto the right AuthError subclass.
144
207
  * Falls back to `AuthError` (with status + code preserved) for unknown codes.
@@ -189,6 +252,15 @@ async function authErrorFromResponse(response) {
189
252
  });
190
253
  }
191
254
  case "invalid_email": return new InvalidEmailError(init);
255
+ case "invalid_google_code":
256
+ case "email_unverified": return new GoogleSignInError({
257
+ ...init,
258
+ code
259
+ });
260
+ case "validation_error": return new ValidationError({
261
+ ...init,
262
+ issues: issues(details)
263
+ });
192
264
  default:
193
265
  if (response.status === 429) {
194
266
  const retryAfterSeconds = num(details, "retryAfter") || num(details, "retryAfterSeconds") || Number(response.headers.get("Retry-After") ?? 0);
@@ -212,4 +284,4 @@ function authErrorFromFetchFailure(cause, isTimeout = false) {
212
284
  });
213
285
  }
214
286
  //#endregion
215
- export { authErrorFromResponse as _, InvalidCredentialsError as a, MaxAttemptsError as c, NoPendingOtpError as d, OtpExpiredError as f, authErrorFromFetchFailure as g, ResendCooldownError as h, EmailAlreadyHasPasswordError as i, MaxResendsError as l, RateLimitedError as m, AuthError as n, InvalidEmailError as o, OtpInvalidError as p, CaptchaFailedError as r, InvalidPasswordError as s, AccountSuspendedError as t, NoPasswordSetError as u };
287
+ export { ResendCooldownError as _, GoogleSignInCancelledError as a, authErrorFromResponse as b, InvalidEmailError as c, MaxResendsError as d, NoPasswordSetError as f, RateLimitedError as g, OtpInvalidError as h, EmailAlreadyHasPasswordError as i, InvalidPasswordError as l, OtpExpiredError as m, AuthError as n, GoogleSignInError as o, NoPendingOtpError as p, CaptchaFailedError as r, InvalidCredentialsError as s, AccountSuspendedError as t, MaxAttemptsError as u, ValidationError as v, authErrorFromFetchFailure as y };
@@ -92,6 +92,47 @@ declare class InvalidPasswordError extends AuthError {
92
92
  declare class InvalidEmailError extends AuthError {
93
93
  constructor(init: AuthErrorInit);
94
94
  }
95
+ type GoogleSignInCancelledReason = 'popup_blocked' | 'popup_closed' | 'access_denied';
96
+ /**
97
+ * The hosted Google sign-in ended without a session because of the user or
98
+ * the browser: the popup was blocked, the popup was closed early, or the
99
+ * user cancelled on Google's account chooser. `code` is always
100
+ * `oauth_cancelled`; branch on `reason` for the specific cause.
101
+ */
102
+ declare class GoogleSignInCancelledError extends AuthError {
103
+ readonly reason: GoogleSignInCancelledReason;
104
+ constructor(init: {
105
+ reason: GoogleSignInCancelledReason;
106
+ cause?: unknown;
107
+ });
108
+ }
109
+ /**
110
+ * The hosted Google sign-in failed. `code` is the failure code the exit or
111
+ * the exchange returned: `invalid_oauth_state`, `invalid_google_code`,
112
+ * `email_unverified`, `oauth_unexpected`, or the SDK-side
113
+ * `oauth_nonce_mismatch`. `status` is the HTTP status when the exchange
114
+ * request failed, otherwise `0`.
115
+ */
116
+ declare class GoogleSignInError extends AuthError {
117
+ constructor(init: {
118
+ code: string;
119
+ } & Partial<AuthErrorInit>);
120
+ }
121
+ /** One failing request field, as reported by the server's validation hook. */
122
+ type ValidationIssue = {
123
+ /** Dot-joined path of the field, for example `clientId` or `otp`. */path: string;
124
+ message: string;
125
+ };
126
+ /**
127
+ * Request validation failed. Thrown for `validation_error` (400) on any
128
+ * endpoint; `issues` names each rejected field.
129
+ */
130
+ declare class ValidationError extends AuthError {
131
+ readonly issues: ValidationIssue[];
132
+ constructor(init: AuthErrorInit & {
133
+ issues: ValidationIssue[];
134
+ });
135
+ }
95
136
  /**
96
137
  * Map a backend error response onto the right AuthError subclass.
97
138
  * Falls back to `AuthError` (with status + code preserved) for unknown codes.
@@ -100,4 +141,4 @@ declare function authErrorFromResponse(response: Response): Promise<AuthError>;
100
141
  /** Network/timeout failures land here. */
101
142
  declare function authErrorFromFetchFailure(cause: unknown, isTimeout?: boolean): AuthError;
102
143
  //#endregion
103
- export { authErrorFromFetchFailure as _, EmailAlreadyHasPasswordError as a, InvalidPasswordError as c, NoPasswordSetError as d, NoPendingOtpError as f, ResendCooldownError as g, RateLimitedError as h, CaptchaFailedError as i, MaxAttemptsError as l, OtpInvalidError as m, AuthError as n, InvalidCredentialsError as o, OtpExpiredError as p, AuthErrorInit as r, InvalidEmailError as s, AccountSuspendedError as t, MaxResendsError as u, authErrorFromResponse as v };
144
+ export { authErrorFromResponse as C, authErrorFromFetchFailure as S, OtpInvalidError as _, EmailAlreadyHasPasswordError as a, ValidationError as b, GoogleSignInError as c, InvalidPasswordError as d, MaxAttemptsError as f, OtpExpiredError as g, NoPendingOtpError as h, CaptchaFailedError as i, InvalidCredentialsError as l, NoPasswordSetError as m, AuthError as n, GoogleSignInCancelledError as o, MaxResendsError as p, AuthErrorInit as r, GoogleSignInCancelledReason as s, AccountSuspendedError as t, InvalidEmailError as u, RateLimitedError as v, ValidationIssue as x, ResendCooldownError as y };
@@ -1,2 +1,2 @@
1
- import { a as GoogleNamespace, c as WalletNamespace, i as EmailPasswordNamespace, l as createAuthClient, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient, u as AuthFetchInit } from "../index-URk8yf8l.js";
2
- export { AuthClient, AuthFetchInit, CreateAuthClientOptions, EmailOtpNamespace, EmailPasswordNamespace, GoogleNamespace, ProactiveRefreshConfig, SubscribeOptions, WalletNamespace, createAuthClient };
1
+ import { a as GoogleNamespace, c as SubscribeOptions, d as AuthFetchInit, i as EmailPasswordNamespace, l as WalletNamespace, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as RolesNamespace, t as AuthClient, u as createAuthClient } from "../index-DwFEdFaS.js";
2
+ export { AuthClient, AuthFetchInit, CreateAuthClientOptions, EmailOtpNamespace, EmailPasswordNamespace, GoogleNamespace, ProactiveRefreshConfig, RolesNamespace, SubscribeOptions, WalletNamespace, createAuthClient };
@@ -1,2 +1,2 @@
1
- import { t as createAuthClient } from "../client-DxhQkQcE.js";
1
+ import { t as createAuthClient } from "../client-Dlhhdp-T.js";
2
2
  export { createAuthClient };
@@ -1,5 +1,142 @@
1
- import { _ as authErrorFromResponse, g as authErrorFromFetchFailure, n as AuthError } from "./authError-CYUAl2Jt.js";
1
+ import { a as GoogleSignInCancelledError, b as authErrorFromResponse, n as AuthError, o as GoogleSignInError, y as authErrorFromFetchFailure } from "./authError-BPxMHQK2.js";
2
2
  import { n as memoryStore } from "./sessionStore-DD6lON9W.js";
3
+ //#region src/client/googleHosted.ts
4
+ const NONCE_STORAGE_KEY = "baliola-auth:google:nonce";
5
+ const MESSAGE_TYPE = "baliola-auth:google";
6
+ const POPUP_WINDOW_NAME = "baliola-auth-google";
7
+ const DEFAULT_POPUP_WIDTH = 500;
8
+ const DEFAULT_POPUP_HEIGHT = 640;
9
+ const POPUP_POLL_INTERVAL_MS = 500;
10
+ function browserWindow() {
11
+ return typeof globalThis.window === "undefined" ? null : globalThis.window;
12
+ }
13
+ function requireBrowserWindow(method) {
14
+ const win = browserWindow();
15
+ if (!win) throw new Error(`google.${method}: only available in a browser (window is undefined)`);
16
+ return win;
17
+ }
18
+ function parseExitMessage(data) {
19
+ if (typeof data !== "object" || data === null) return null;
20
+ const record = data;
21
+ if (record["type"] !== MESSAGE_TYPE) return null;
22
+ const nonce = record["nonce"];
23
+ return {
24
+ nonce: typeof nonce === "string" ? nonce : null,
25
+ ...typeof record["code"] === "string" && { code: record["code"] },
26
+ ...typeof record["error"] === "string" && { error: record["error"] }
27
+ };
28
+ }
29
+ function exitError(code) {
30
+ if (code === "access_denied") return new GoogleSignInCancelledError({ reason: "access_denied" });
31
+ return new GoogleSignInError({ code });
32
+ }
33
+ function createGoogleHosted(ctx) {
34
+ const authOrigin = new URL(ctx.baseUrl).origin;
35
+ function resolveReturnTo(returnTo) {
36
+ if (returnTo !== void 0 && /^https?:\/\//i.test(returnTo)) return returnTo;
37
+ const win = browserWindow();
38
+ if (!win) throw new Error("google.startUrl: returnTo must be an absolute URL outside a browser (window is undefined)");
39
+ if (returnTo === void 0) {
40
+ const current = new URL(win.location.href);
41
+ current.hash = "";
42
+ return current.toString();
43
+ }
44
+ return new URL(returnTo, win.location.href).toString();
45
+ }
46
+ function saveRedirectNonce(nonce) {
47
+ if (typeof globalThis.sessionStorage === "undefined") throw new Error("google.startUrl: sessionStorage is required for redirect mode");
48
+ globalThis.sessionStorage.setItem(NONCE_STORAGE_KEY, nonce);
49
+ }
50
+ function startUrl(opts) {
51
+ const url = new URL(`${ctx.baseUrl}/auth/google/start`);
52
+ url.searchParams.set("clientId", ctx.clientId);
53
+ url.searchParams.set("returnTo", resolveReturnTo(opts.returnTo));
54
+ url.searchParams.set("mode", opts.mode);
55
+ url.searchParams.set("nonce", opts.nonce);
56
+ if (opts.mode === "redirect") saveRedirectNonce(opts.nonce);
57
+ return url.toString();
58
+ }
59
+ function signInWithPopup(opts = {}) {
60
+ return new Promise((resolve, reject) => {
61
+ const win = requireBrowserWindow("signInWithPopup");
62
+ const nonce = crypto.randomUUID();
63
+ const url = startUrl({
64
+ mode: "popup",
65
+ nonce,
66
+ ...opts.returnTo !== void 0 && { returnTo: opts.returnTo }
67
+ });
68
+ const width = opts.popup?.width ?? DEFAULT_POPUP_WIDTH;
69
+ const height = opts.popup?.height ?? DEFAULT_POPUP_HEIGHT;
70
+ const popup = win.open(url, POPUP_WINDOW_NAME, `popup,width=${width},height=${height}`);
71
+ if (!popup) {
72
+ reject(new GoogleSignInCancelledError({ reason: "popup_blocked" }));
73
+ return;
74
+ }
75
+ let settled = false;
76
+ let closeTimer = null;
77
+ const cleanup = () => {
78
+ settled = true;
79
+ win.removeEventListener("message", onMessage);
80
+ clearInterval(pollTimer);
81
+ if (closeTimer !== null) clearTimeout(closeTimer);
82
+ };
83
+ const onMessage = (event) => {
84
+ if (settled || event.origin !== authOrigin) return;
85
+ const message = parseExitMessage(event.data);
86
+ if (!message || message.nonce !== nonce) return;
87
+ cleanup();
88
+ if (message.code !== void 0) {
89
+ ctx.exchange({ code: message.code }).then(resolve, reject);
90
+ return;
91
+ }
92
+ reject(exitError(message.error ?? "oauth_unexpected"));
93
+ };
94
+ const pollTimer = setInterval(() => {
95
+ if (settled || !popup.closed || closeTimer !== null) return;
96
+ closeTimer = setTimeout(() => {
97
+ if (settled) return;
98
+ cleanup();
99
+ reject(new GoogleSignInCancelledError({ reason: "popup_closed" }));
100
+ }, POPUP_POLL_INTERVAL_MS);
101
+ }, POPUP_POLL_INTERVAL_MS);
102
+ win.addEventListener("message", onMessage);
103
+ });
104
+ }
105
+ function takeRedirectNonce() {
106
+ if (typeof globalThis.sessionStorage === "undefined") return null;
107
+ const saved = globalThis.sessionStorage.getItem(NONCE_STORAGE_KEY);
108
+ globalThis.sessionStorage.removeItem(NONCE_STORAGE_KEY);
109
+ return saved;
110
+ }
111
+ function stripExitParams(url) {
112
+ for (const key of [
113
+ "code",
114
+ "error",
115
+ "nonce"
116
+ ]) url.searchParams.delete(key);
117
+ if (typeof globalThis.history === "undefined") return;
118
+ globalThis.history.replaceState(globalThis.history.state, "", url.toString());
119
+ }
120
+ async function completeRedirect(opts = {}) {
121
+ const win = requireBrowserWindow("completeRedirect");
122
+ const url = new URL(opts.url ?? win.location.href);
123
+ const code = url.searchParams.get("code");
124
+ const error = url.searchParams.get("error");
125
+ if (code === null && error === null) return null;
126
+ const nonce = url.searchParams.get("nonce");
127
+ const saved = takeRedirectNonce();
128
+ stripExitParams(url);
129
+ if (saved === null || nonce !== saved) throw new GoogleSignInError({ code: "oauth_nonce_mismatch" });
130
+ if (code === null) throw exitError(error ?? "oauth_unexpected");
131
+ return ctx.exchange({ code });
132
+ }
133
+ return {
134
+ startUrl,
135
+ signInWithPopup,
136
+ completeRedirect
137
+ };
138
+ }
139
+ //#endregion
3
140
  //#region src/client/methods.ts
4
141
  function toAuthSession(data) {
5
142
  const issuedAt = Date.now();
@@ -16,13 +153,10 @@ function toAuthSession(data) {
16
153
  };
17
154
  }
18
155
  function createMethods(ctx) {
19
- const withClientId = (body) => {
20
- if (ctx.clientId === void 0) return body;
21
- return {
22
- ...body,
23
- clientId: ctx.clientId
24
- };
25
- };
156
+ const withClientId = (body) => ({
157
+ ...body,
158
+ clientId: ctx.clientId
159
+ });
26
160
  return {
27
161
  /** emailOtp.* (passwordless) */
28
162
  async sendLoginCode(input) {
@@ -130,7 +264,15 @@ function createMethods(ctx) {
130
264
  path: "/auth/google/login",
131
265
  method: "POST",
132
266
  authMode: "none",
133
- body: withClientId("idToken" in input ? { idToken: input.idToken } : { code: input.code })
267
+ body: withClientId({ idToken: input.idToken })
268
+ }));
269
+ },
270
+ async googleExchange(input) {
271
+ return toAuthSession(await ctx.transport.request({
272
+ path: "/auth/google/exchange",
273
+ method: "POST",
274
+ authMode: "none",
275
+ body: { code: input.code }
134
276
  }));
135
277
  },
136
278
  /** wallet.* */
@@ -149,6 +291,22 @@ function createMethods(ctx) {
149
291
  body: { hash }
150
292
  });
151
293
  },
294
+ /** roles.* */
295
+ async applyRole(input) {
296
+ return ctx.transport.request({
297
+ path: "/auth/roles/apply",
298
+ method: "POST",
299
+ authMode: "bearer+session",
300
+ body: { roleName: input.roleName }
301
+ });
302
+ },
303
+ async getRoleStatus() {
304
+ return ctx.transport.request({
305
+ path: "/auth/roles/status",
306
+ method: "GET",
307
+ authMode: "bearer+session"
308
+ });
309
+ },
152
310
  /** profile */
153
311
  async getProfile() {
154
312
  return ctx.transport.request({
@@ -381,7 +539,9 @@ const DEFAULT_PROACTIVE_LEAD_TIME_MS = 6e4;
381
539
  const BROADCAST_CHANNEL_NAME = "baliola.auth.session.v1";
382
540
  function createAuthClient(options) {
383
541
  if (!options.baseUrl) throw new Error("createAuthClient: baseUrl is required");
542
+ if (!options.clientId) throw new Error("createAuthClient: clientId is required");
384
543
  const baseUrl = options.baseUrl.replace(/\/+$/, "");
544
+ const clientId = options.clientId;
385
545
  const store = options.store ?? memoryStore();
386
546
  const fetchImpl = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
387
547
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -439,39 +599,61 @@ function createAuthClient(options) {
439
599
  });
440
600
  const methods = createMethods({
441
601
  transport,
442
- ...options.clientId !== void 0 && { clientId: options.clientId }
602
+ clientId
443
603
  });
444
- return {
445
- emailOtp: {
446
- sendLoginCode: (input) => methods.sendLoginCode(input),
447
- async verifyLoginCode(input) {
448
- const session = await methods.verifyLoginCode(input);
449
- await setSession(session);
450
- return session;
451
- },
452
- resendLoginCode: (input) => methods.resendLoginCode(input)
604
+ const emailOtp = {
605
+ sendLoginCode: (input) => methods.sendLoginCode(input),
606
+ async verifyLoginCode(input) {
607
+ const session = await methods.verifyLoginCode(input);
608
+ await setSession(session);
609
+ return session;
453
610
  },
454
- emailPassword: {
455
- register: (input) => methods.register(input),
456
- async verifyRegistrationCode(input) {
457
- const session = await methods.verifyRegistrationCode(input);
458
- await setSession(session);
459
- return session;
460
- },
461
- resendRegistrationCode: (input) => methods.resendRegistrationCode(input),
611
+ resendLoginCode: (input) => methods.resendLoginCode(input)
612
+ };
613
+ const emailPassword = {
614
+ register: (input) => methods.register(input),
615
+ async verifyRegistrationCode(input) {
616
+ const session = await methods.verifyRegistrationCode(input);
617
+ await setSession(session);
618
+ return session;
619
+ },
620
+ resendRegistrationCode: (input) => methods.resendRegistrationCode(input),
621
+ async login(input) {
622
+ const session = await methods.loginWithPassword(input);
623
+ await setSession(session);
624
+ return session;
625
+ },
626
+ setPassword: (input) => methods.setPassword(input),
627
+ changePassword: (input) => methods.changePassword(input)
628
+ };
629
+ async function googleExchange(input) {
630
+ const session = await methods.googleExchange(input);
631
+ await setSession(session);
632
+ return session;
633
+ }
634
+ const googleHosted = createGoogleHosted({
635
+ baseUrl,
636
+ clientId,
637
+ exchange: googleExchange
638
+ });
639
+ return {
640
+ emailOtp,
641
+ emailPassword,
642
+ google: {
462
643
  async login(input) {
463
- const session = await methods.loginWithPassword(input);
644
+ const session = await methods.loginWithGoogle(input);
464
645
  await setSession(session);
465
646
  return session;
466
647
  },
467
- setPassword: (input) => methods.setPassword(input),
468
- changePassword: (input) => methods.changePassword(input)
648
+ startUrl: (opts) => googleHosted.startUrl(opts),
649
+ exchange: googleExchange,
650
+ signInWithPopup: (opts) => googleHosted.signInWithPopup(opts),
651
+ completeRedirect: (opts) => googleHosted.completeRedirect(opts)
652
+ },
653
+ roles: {
654
+ apply: (input) => methods.applyRole(input),
655
+ status: () => methods.getRoleStatus()
469
656
  },
470
- google: { async login(input) {
471
- const session = await methods.loginWithGoogle(input);
472
- await setSession(session);
473
- return session;
474
- } },
475
657
  wallet: {
476
658
  getAddress: async () => (await methods.getWalletAddress()).address,
477
659
  signHash: async (hash) => (await methods.signWalletHash(hash)).signature
@@ -1,2 +1,2 @@
1
- import { _ as authErrorFromFetchFailure, a as EmailAlreadyHasPasswordError, c as InvalidPasswordError, d as NoPasswordSetError, f as NoPendingOtpError, g as ResendCooldownError, h as RateLimitedError, i as CaptchaFailedError, l as MaxAttemptsError, m as OtpInvalidError, n as AuthError, o as InvalidCredentialsError, p as OtpExpiredError, r as AuthErrorInit, s as InvalidEmailError, t as AccountSuspendedError, u as MaxResendsError, v as authErrorFromResponse } from "../authError-DgPEjTfW.js";
2
- export { AccountSuspendedError, AuthError, type AuthErrorInit, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse };
1
+ import { C as authErrorFromResponse, S as authErrorFromFetchFailure, _ as OtpInvalidError, a as EmailAlreadyHasPasswordError, b as ValidationError, c as GoogleSignInError, d as InvalidPasswordError, f as MaxAttemptsError, g as OtpExpiredError, h as NoPendingOtpError, i as CaptchaFailedError, l as InvalidCredentialsError, m as NoPasswordSetError, n as AuthError, o as GoogleSignInCancelledError, p as MaxResendsError, r as AuthErrorInit, s as GoogleSignInCancelledReason, t as AccountSuspendedError, u as InvalidEmailError, v as RateLimitedError, x as ValidationIssue, y as ResendCooldownError } from "../authError-E11ZMcID.js";
2
+ export { AccountSuspendedError, AuthError, type AuthErrorInit, CaptchaFailedError, EmailAlreadyHasPasswordError, GoogleSignInCancelledError, type GoogleSignInCancelledReason, GoogleSignInError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, ValidationError, type ValidationIssue, authErrorFromFetchFailure, authErrorFromResponse };
@@ -1,2 +1,2 @@
1
- import { _ as authErrorFromResponse, a as InvalidCredentialsError, c as MaxAttemptsError, d as NoPendingOtpError, f as OtpExpiredError, g as authErrorFromFetchFailure, h as ResendCooldownError, i as EmailAlreadyHasPasswordError, l as MaxResendsError, m as RateLimitedError, n as AuthError, o as InvalidEmailError, p as OtpInvalidError, r as CaptchaFailedError, s as InvalidPasswordError, t as AccountSuspendedError, u as NoPasswordSetError } from "../authError-CYUAl2Jt.js";
2
- export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse };
1
+ import { _ as ResendCooldownError, a as GoogleSignInCancelledError, b as authErrorFromResponse, c as InvalidEmailError, d as MaxResendsError, f as NoPasswordSetError, g as RateLimitedError, h as OtpInvalidError, i as EmailAlreadyHasPasswordError, l as InvalidPasswordError, m as OtpExpiredError, n as AuthError, o as GoogleSignInError, p as NoPendingOtpError, r as CaptchaFailedError, s as InvalidCredentialsError, t as AccountSuspendedError, u as MaxAttemptsError, v as ValidationError, y as authErrorFromFetchFailure } from "../authError-BPxMHQK2.js";
2
+ export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, GoogleSignInCancelledError, GoogleSignInError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, ValidationError, authErrorFromFetchFailure, authErrorFromResponse };
@@ -1,6 +1,6 @@
1
- import { c as ResendCodeResult, l as SendLoginCodeResult, n as AuthSession, r as ErrorHandler, s as RegisterResult, u as SessionChangeHandler } from "./session-Cs_P7ojF.js";
2
- import { n as SessionStore } from "./sessionStore-BDdEpbL8.js";
3
- import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, i as LoginWithPasswordInput, l as SetPasswordInput, m as ProfileResult, n as ChangePasswordInput, o as ResendLoginCodeInput, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, u as VerifyLoginCodeInput } from "./requests-jRLgNXyf.js";
1
+ import { c as ResendCodeResult, l as SendLoginCodeResult, n as AuthSession, r as ErrorHandler, s as RegisterResult, u as SessionChangeHandler } from "./session-flM0yXx2.js";
2
+ import { n as SessionStore } from "./sessionStore-D2kXjvNM.js";
3
+ import { C as ProfileResult, _ as SendLoginCodeInput, b as VerifyRegistrationCodeInput, c as GoogleExchangeInput, d as GoogleStartOptions, f as LoginWithGoogleInput, g as ResendRegistrationCodeInput, h as ResendLoginCodeInput, i as RoleStatus, l as GoogleSignInWithPopupOptions, m as RegisterInput, o as ChangePasswordInput, p as LoginWithPasswordInput, r as RoleApplicationStatus, s as GoogleCompleteRedirectOptions, t as ApplyRoleInput, v as SetPasswordInput, y as VerifyLoginCodeInput } from "./roles-BZecOymZ.js";
4
4
 
5
5
  //#region src/client/transport.d.ts
6
6
  type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
@@ -31,8 +31,13 @@ type ProactiveRefreshConfig = false | {
31
31
  /** Refresh proactively if `expiresAt - now < leadTimeMs`. Defaults to 60_000 (60s). */leadTimeMs?: number;
32
32
  };
33
33
  type CreateAuthClientOptions = {
34
- /** Base URL of the Baliola Auth server (no trailing slash). */baseUrl: string; /** Optional clientId, applied to every auth request that accepts one. */
35
- clientId?: string;
34
+ /** Base URL of the Baliola Auth server (no trailing slash). */baseUrl: string;
35
+ /**
36
+ * The project's client id. Sent on every login and register body and used
37
+ * to build the hosted Google login URL. baliola-auth rejects login and
38
+ * register requests without one.
39
+ */
40
+ clientId: string;
36
41
  /**
37
42
  * Pluggable session store. Defaults to `memoryStore()`. Use
38
43
  * `localStorageStore()` in browsers for persistence across reloads.
@@ -65,10 +70,12 @@ type EmailOtpNamespace = {
65
70
  * Send a 6-digit one-time code to the email. Used for passwordless
66
71
  * login (existing accounts) or signup (creates a new account on verify).
67
72
  *
73
+ * The response never says whether the email is registered; the login
74
+ * versus signup branch is resolved by `verifyLoginCode`.
75
+ *
68
76
  * @example
69
77
  * const r = await auth.emailOtp.sendLoginCode({ email, captchaToken })
70
- * // r = { flow: 'login'|'signup', methods: ['password','passwordless'],
71
- * // otp: { expiresInSeconds, expiresAt, canResendInSeconds, resendsRemaining } }
78
+ * // r = { otp: { expiresInSeconds, expiresAt, canResendInSeconds, resendsRemaining } }
72
79
  *
73
80
  * @throws CaptchaFailedError, RateLimitedError, InvalidEmailError, AuthError.
74
81
  */
@@ -118,19 +125,92 @@ type EmailPasswordNamespace = {
118
125
  /** Google flow. */
119
126
  type GoogleNamespace = {
120
127
  /**
121
- * Sign in with Google. Pass `{ idToken }` from the Google Identity Services
122
- * button or One Tap, or `{ code }` from the OAuth popup code flow
123
- * (`google.accounts.oauth2.initCodeClient`), which the server exchanges
124
- * for the ID token using its client secret.
128
+ * Sign in with a Google ID token from the Google Identity Services button
129
+ * or One Tap. Apps that do not load Google Identity Services should use
130
+ * `signInWithPopup` instead.
125
131
  *
126
- * @throws AuthError with code `invalid_google_code` (401) when the exchange
127
- * fails, `google_code_unsupported` (400) when the server has no
128
- * client secret configured, AccountSuspendedError, AuthError.
129
- * @example
130
- * await auth.google.login({ idToken });
131
- * await auth.google.login({ code });
132
+ * @throws AuthError (401) when the ID token fails verification,
133
+ * GoogleSignInError with code `email_unverified`,
134
+ * AccountSuspendedError, AuthError.
132
135
  */
133
136
  login(input: LoginWithGoogleInput): Promise<AuthSession>;
137
+ /**
138
+ * Build the `GET /auth/google/start` URL for the hosted login. Use it to
139
+ * drive the redirect flow yourself (`location.assign(url)`), or a custom
140
+ * popup. In `redirect` mode the nonce is saved in `sessionStorage` for
141
+ * `completeRedirect` to verify.
142
+ *
143
+ * @throws Error when `returnTo` is relative outside a browser, or when
144
+ * `sessionStorage` is missing in `redirect` mode.
145
+ */
146
+ startUrl(opts: GoogleStartOptions & {
147
+ nonce: string;
148
+ }): string;
149
+ /**
150
+ * Exchange the one-time `code` from a hosted login exit for a session.
151
+ * `signInWithPopup` and `completeRedirect` call this for you; call it
152
+ * yourself only when driving `startUrl` manually.
153
+ *
154
+ * @throws GoogleSignInError with code `invalid_google_code` (401) when the
155
+ * code is unknown, expired (60 s), or already used;
156
+ * AccountSuspendedError, AuthError.
157
+ */
158
+ exchange(input: GoogleExchangeInput): Promise<AuthSession>;
159
+ /**
160
+ * Sign in with Google through the auth-hosted flow in a popup. Opens
161
+ * `startUrl` in a popup window, waits for the exit message, exchanges the
162
+ * one-time code, and stores the session. Browser only.
163
+ *
164
+ * @throws GoogleSignInCancelledError (`reason`: `popup_blocked`,
165
+ * `popup_closed`, or `access_denied`), GoogleSignInError with the
166
+ * exit or exchange code, Error outside a browser.
167
+ * @example
168
+ * try {
169
+ * const session = await auth.google.signInWithPopup();
170
+ * } catch (e) {
171
+ * if (e instanceof GoogleSignInCancelledError) return; // user backed out
172
+ * throw e;
173
+ * }
174
+ */
175
+ signInWithPopup(opts?: GoogleSignInWithPopupOptions): Promise<AuthSession>;
176
+ /**
177
+ * Finish a hosted Google login started in `redirect` mode. Call it on the
178
+ * `returnTo` page. Resolves `null` when the URL carries no `code` or
179
+ * `error`, otherwise verifies the nonce saved by `startUrl`, strips
180
+ * `code`, `error`, and `nonce` from the URL, and exchanges the code.
181
+ * Browser only.
182
+ *
183
+ * @throws GoogleSignInError (`oauth_nonce_mismatch`, or the exit or
184
+ * exchange code), GoogleSignInCancelledError (`access_denied`),
185
+ * Error outside a browser.
186
+ * @example
187
+ * const session = await auth.google.completeRedirect();
188
+ * if (session) router.replace('/dashboard');
189
+ */
190
+ completeRedirect(opts?: GoogleCompleteRedirectOptions): Promise<AuthSession | null>;
191
+ };
192
+ /** Self-service role applications (`/auth/roles/*`). */
193
+ type RolesNamespace = {
194
+ /**
195
+ * Apply for a self-applicable global role. The application lands at
196
+ * `pending_approval` until an admin approves it.
197
+ *
198
+ * @throws AuthError with `code` `role_already_active` or
199
+ * `application_pending` (409), `role_not_self_applicable` (403),
200
+ * a 400 for a role name outside the allowlist, a 401 for a missing
201
+ * or expired session, and other API errors.
202
+ * @example
203
+ * const app = await auth.roles.apply({ roleName: 'module_deployer' });
204
+ * // app.status === 'pending_approval'
205
+ */
206
+ apply(input: ApplyRoleInput): Promise<RoleApplicationStatus>;
207
+ /**
208
+ * Application status for every self-applicable role. Always one row per
209
+ * role; `status` is `none` when the account has never applied.
210
+ *
211
+ * @throws AuthError on 401 (missing or expired session) and other API errors.
212
+ */
213
+ status(): Promise<RoleStatus[]>;
134
214
  };
135
215
  /** Custodial wallet (Console tier). */
136
216
  type WalletNamespace = {
@@ -141,6 +221,7 @@ type AuthClient = {
141
221
  emailOtp: EmailOtpNamespace;
142
222
  emailPassword: EmailPasswordNamespace;
143
223
  google: GoogleNamespace;
224
+ roles: RolesNamespace;
144
225
  wallet: WalletNamespace;
145
226
  /**
146
227
  * Fetch the token holder's identity: account (id, email, live status,
@@ -167,4 +248,4 @@ type AuthClient = {
167
248
  };
168
249
  declare function createAuthClient(options: CreateAuthClientOptions): AuthClient;
169
250
  //#endregion
170
- export { GoogleNamespace as a, WalletNamespace as c, EmailPasswordNamespace as i, createAuthClient as l, CreateAuthClientOptions as n, ProactiveRefreshConfig as o, EmailOtpNamespace as r, SubscribeOptions as s, AuthClient as t, AuthFetchInit as u };
251
+ export { GoogleNamespace as a, SubscribeOptions as c, AuthFetchInit as d, EmailPasswordNamespace as i, WalletNamespace as l, CreateAuthClientOptions as n, ProactiveRefreshConfig as o, EmailOtpNamespace as r, RolesNamespace as s, AuthClient as t, createAuthClient as u };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { a as OtpInfo, c as ResendCodeResult, i as ErrorSource, l as SendLoginCodeResult, n as AuthSession, o as Project, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "./session-Cs_P7ojF.js";
2
- import { a as GoogleNamespace, c as WalletNamespace, i as EmailPasswordNamespace, l as createAuthClient, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient, u as AuthFetchInit } from "./index-URk8yf8l.js";
3
- import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "./sessionStore-BDdEpbL8.js";
4
- import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, f as Profile, i as LoginWithPasswordInput, l as SetPasswordInput, m as ProfileResult, n as ChangePasswordInput, o as ResendLoginCodeInput, p as ProfileAccount, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, t as CaptchaArgs, u as VerifyLoginCodeInput } from "./requests-jRLgNXyf.js";
5
- import { _ as authErrorFromFetchFailure, a as EmailAlreadyHasPasswordError, c as InvalidPasswordError, d as NoPasswordSetError, f as NoPendingOtpError, g as ResendCooldownError, h as RateLimitedError, i as CaptchaFailedError, l as MaxAttemptsError, m as OtpInvalidError, n as AuthError, o as InvalidCredentialsError, p as OtpExpiredError, r as AuthErrorInit, s as InvalidEmailError, t as AccountSuspendedError, u as MaxResendsError, v as authErrorFromResponse } from "./authError-DgPEjTfW.js";
1
+ import { a as OtpInfo, c as ResendCodeResult, i as ErrorSource, l as SendLoginCodeResult, n as AuthSession, o as Project, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "./session-flM0yXx2.js";
2
+ import { a as GoogleNamespace, c as SubscribeOptions, d as AuthFetchInit, i as EmailPasswordNamespace, l as WalletNamespace, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as RolesNamespace, t as AuthClient, u as createAuthClient } from "./index-DwFEdFaS.js";
3
+ import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "./sessionStore-D2kXjvNM.js";
4
+ import { C as ProfileResult, S as ProfileAccount, _ as SendLoginCodeInput, a as CaptchaArgs, b as VerifyRegistrationCodeInput, c as GoogleExchangeInput, d as GoogleStartOptions, f as LoginWithGoogleInput, g as ResendRegistrationCodeInput, h as ResendLoginCodeInput, i as RoleStatus, l as GoogleSignInWithPopupOptions, m as RegisterInput, n as RoleApplicationState, o as ChangePasswordInput, p as LoginWithPasswordInput, r as RoleApplicationStatus, s as GoogleCompleteRedirectOptions, t as ApplyRoleInput, u as GoogleStartMode, v as SetPasswordInput, x as Profile, y as VerifyLoginCodeInput } from "./roles-BZecOymZ.js";
5
+ import { C as authErrorFromResponse, S as authErrorFromFetchFailure, _ as OtpInvalidError, a as EmailAlreadyHasPasswordError, b as ValidationError, c as GoogleSignInError, d as InvalidPasswordError, f as MaxAttemptsError, g as OtpExpiredError, h as NoPendingOtpError, i as CaptchaFailedError, l as InvalidCredentialsError, m as NoPasswordSetError, n as AuthError, o as GoogleSignInCancelledError, p as MaxResendsError, r as AuthErrorInit, s as GoogleSignInCancelledReason, t as AccountSuspendedError, u as InvalidEmailError, v as RateLimitedError, x as ValidationIssue, y as ResendCooldownError } from "./authError-E11ZMcID.js";
6
6
  import { n as AccessTokenPayloadProject, t as AccessTokenPayload } from "./tokens-BXrPLi5B.js";
7
7
 
8
8
  //#region src/types/wallet.d.ts
@@ -16,4 +16,4 @@ type SignHashResult = {
16
16
  address: string;
17
17
  };
18
18
  //#endregion
19
- export { type AccessTokenPayload, type AccessTokenPayloadProject, type Account, AccountSuspendedError, type AuthClient, AuthError, type AuthErrorInit, type AuthFetchInit, type AuthSession, type CaptchaArgs, CaptchaFailedError, type ChangePasswordInput, type CreateAuthClientOptions, EmailAlreadyHasPasswordError, type EmailOtpNamespace, type EmailPasswordNamespace, type ErrorHandler, type ErrorSource, type GoogleNamespace, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, type LocalStorageStoreOptions, type LoginWithGoogleInput, type LoginWithPasswordInput, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, type OtpInfo, OtpInvalidError, type ProactiveRefreshConfig, type Profile, type ProfileAccount, type ProfileResult, type Project, RateLimitedError, type RegisterInput, type RegisterResult, type ResendCodeResult, ResendCooldownError, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SessionStore, type SetPasswordInput, type SignHashResult, type SubscribeOptions, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput, type WalletAddressResult, type WalletNamespace, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
19
+ export { type AccessTokenPayload, type AccessTokenPayloadProject, type Account, AccountSuspendedError, type ApplyRoleInput, type AuthClient, AuthError, type AuthErrorInit, type AuthFetchInit, type AuthSession, type CaptchaArgs, CaptchaFailedError, type ChangePasswordInput, type CreateAuthClientOptions, EmailAlreadyHasPasswordError, type EmailOtpNamespace, type EmailPasswordNamespace, type ErrorHandler, type ErrorSource, type GoogleCompleteRedirectOptions, type GoogleExchangeInput, type GoogleNamespace, GoogleSignInCancelledError, type GoogleSignInCancelledReason, GoogleSignInError, type GoogleSignInWithPopupOptions, type GoogleStartMode, type GoogleStartOptions, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, type LocalStorageStoreOptions, type LoginWithGoogleInput, type LoginWithPasswordInput, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, type OtpInfo, OtpInvalidError, type ProactiveRefreshConfig, type Profile, type ProfileAccount, type ProfileResult, type Project, RateLimitedError, type RegisterInput, type RegisterResult, type ResendCodeResult, ResendCooldownError, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type RoleApplicationState, type RoleApplicationStatus, type RoleStatus, type RolesNamespace, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SessionStore, type SetPasswordInput, type SignHashResult, type SubscribeOptions, ValidationError, type ValidationIssue, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput, type WalletAddressResult, type WalletNamespace, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createAuthClient } from "./client-DxhQkQcE.js";
2
- import { _ as authErrorFromResponse, a as InvalidCredentialsError, c as MaxAttemptsError, d as NoPendingOtpError, f as OtpExpiredError, g as authErrorFromFetchFailure, h as ResendCooldownError, i as EmailAlreadyHasPasswordError, l as MaxResendsError, m as RateLimitedError, n as AuthError, o as InvalidEmailError, p as OtpInvalidError, r as CaptchaFailedError, s as InvalidPasswordError, t as AccountSuspendedError, u as NoPasswordSetError } from "./authError-CYUAl2Jt.js";
1
+ import { _ as ResendCooldownError, a as GoogleSignInCancelledError, b as authErrorFromResponse, c as InvalidEmailError, d as MaxResendsError, f as NoPasswordSetError, g as RateLimitedError, h as OtpInvalidError, i as EmailAlreadyHasPasswordError, l as InvalidPasswordError, m as OtpExpiredError, n as AuthError, o as GoogleSignInError, p as NoPendingOtpError, r as CaptchaFailedError, s as InvalidCredentialsError, t as AccountSuspendedError, u as MaxAttemptsError, v as ValidationError, y as authErrorFromFetchFailure } from "./authError-BPxMHQK2.js";
2
+ import { t as createAuthClient } from "./client-Dlhhdp-T.js";
3
3
  import { n as memoryStore, t as localStorageStore } from "./sessionStore-DD6lON9W.js";
4
- export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
4
+ export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, GoogleSignInCancelledError, GoogleSignInError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, ValidationError, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
@@ -0,0 +1,123 @@
1
+ import { t as Account } from "./session-flM0yXx2.js";
2
+
3
+ //#region src/types/profile.d.ts
4
+ /** The account's profile record; every field is user-editable and nullable. */
5
+ type Profile = {
6
+ displayName: string | null;
7
+ avatarUrl: string | null;
8
+ primaryEmail: string | null;
9
+ primaryPhone: string | null;
10
+ };
11
+ /** The account as returned by `auth.getProfile`, with sign-in capability flags. */
12
+ type ProfileAccount = Account & {
13
+ /**
14
+ * `true` when the account can sign in with a password; `false` for
15
+ * Google-only or code-only accounts, which should call
16
+ * `auth.emailPassword.setPassword` rather than `changePassword`.
17
+ */
18
+ hasPassword: boolean;
19
+ };
20
+ /** Result of `auth.getProfile`, the identity read for the token holder. */
21
+ type ProfileResult = {
22
+ /** `status` is the live account status, not a token-time snapshot. */account: ProfileAccount; /** `null` when the account has no profile row yet. */
23
+ profile: Profile | null;
24
+ };
25
+ //#endregion
26
+ //#region src/types/requests.d.ts
27
+ /** Optional captcha argument shared by every public unauthenticated entrypoint. */
28
+ type CaptchaArgs = {
29
+ /** Cloudflare Turnstile token. Required when captcha is enabled on the server. */captchaToken?: string;
30
+ };
31
+ type SendLoginCodeInput = {
32
+ email: string;
33
+ } & CaptchaArgs;
34
+ type VerifyLoginCodeInput = {
35
+ email: string;
36
+ code: string;
37
+ };
38
+ type ResendLoginCodeInput = {
39
+ email: string;
40
+ } & CaptchaArgs;
41
+ type RegisterInput = {
42
+ email: string;
43
+ password: string;
44
+ } & CaptchaArgs;
45
+ type VerifyRegistrationCodeInput = {
46
+ email: string;
47
+ code: string;
48
+ };
49
+ type ResendRegistrationCodeInput = {
50
+ email: string;
51
+ } & CaptchaArgs;
52
+ type LoginWithPasswordInput = {
53
+ email: string;
54
+ password: string;
55
+ } & CaptchaArgs;
56
+ type SetPasswordInput = {
57
+ password: string;
58
+ };
59
+ type ChangePasswordInput = {
60
+ currentPassword: string;
61
+ newPassword: string;
62
+ };
63
+ /** `idToken` comes from Google Identity Services (`renderButton` / One Tap). */
64
+ type LoginWithGoogleInput = {
65
+ idToken: string;
66
+ };
67
+ /** The one-time code handed to the app by the hosted Google login exit. */
68
+ type GoogleExchangeInput = {
69
+ code: string;
70
+ };
71
+ type GoogleStartMode = 'popup' | 'redirect';
72
+ type GoogleStartOptions = {
73
+ /**
74
+ * Where the browser lands after Google. Absolute URL or a path resolved
75
+ * against `window.location`. Its origin must be one of the project's
76
+ * `allowedOrigins`. Default: the current href without its hash.
77
+ */
78
+ returnTo?: string; /** `popup` posts the result to `window.opener`; `redirect` 302s back to `returnTo`. */
79
+ mode: GoogleStartMode; /** Opaque value echoed back on the exit. Supplied by `signInWithPopup`; callers normally omit it. */
80
+ nonce?: string;
81
+ };
82
+ type GoogleSignInWithPopupOptions = {
83
+ /** See `GoogleStartOptions.returnTo`. Default: the current href without its hash. */returnTo?: string;
84
+ popup?: {
85
+ /** Popup width in CSS pixels. Default: 500. */width?: number; /** Popup height in CSS pixels. Default: 640. */
86
+ height?: number;
87
+ };
88
+ };
89
+ type GoogleCompleteRedirectOptions = {
90
+ /** URL to read `code`, `error`, and `nonce` from. Default: `window.location.href`. */url?: string;
91
+ };
92
+ //#endregion
93
+ //#region src/types/roles.d.ts
94
+ /** Lifecycle of a role application, as stored on the server. */
95
+ type RoleApplicationState = 'pending_approval' | 'active' | 'revoked';
96
+ /** Input of `auth.roles.apply`. */
97
+ type ApplyRoleInput = {
98
+ /** Role to apply for. Only names on the server's self-applicable allowlist are accepted. */roleName: string;
99
+ };
100
+ /** Result of `auth.roles.apply`: the account role row the server created. */
101
+ type RoleApplicationStatus = {
102
+ id: string;
103
+ accountId: string;
104
+ roleId: string; /** `null` for a global role. */
105
+ projectId: string | null;
106
+ status: RoleApplicationState; /** Account id that created the row: an admin, or the applicant for a self-service application. */
107
+ assignedBy: string | null;
108
+ assignedAt: string;
109
+ approvedAt: string | null;
110
+ revokedAt: string | null;
111
+ };
112
+ /** One row of `auth.roles.status`, from the caller's point of view. */
113
+ type RoleStatus = {
114
+ roleName: string;
115
+ displayName: string | null; /** `none` when the account has never applied for this role. */
116
+ status: RoleApplicationState | 'none'; /** `null` until the account applies. */
117
+ accountRoleId: string | null;
118
+ assignedAt: string | null;
119
+ approvedAt: string | null;
120
+ revokedAt: string | null;
121
+ };
122
+ //#endregion
123
+ export { ProfileResult as C, ProfileAccount as S, SendLoginCodeInput as _, CaptchaArgs as a, VerifyRegistrationCodeInput as b, GoogleExchangeInput as c, GoogleStartOptions as d, LoginWithGoogleInput as f, ResendRegistrationCodeInput as g, ResendLoginCodeInput as h, RoleStatus as i, GoogleSignInWithPopupOptions as l, RegisterInput as m, RoleApplicationState as n, ChangePasswordInput as o, LoginWithPasswordInput as p, RoleApplicationStatus as r, GoogleCompleteRedirectOptions as s, ApplyRoleInput as t, GoogleStartMode as u, SetPasswordInput as v, Profile as x, VerifyLoginCodeInput as y };
@@ -28,11 +28,13 @@ type OtpInfo = {
28
28
  canResendInSeconds: number; /** Resend attempts left for this OTP record. */
29
29
  resendsRemaining: number;
30
30
  };
31
- /** Result of `auth.emailOtp.sendLoginCode`. */
31
+ /**
32
+ * Result of `auth.emailOtp.sendLoginCode`. Carries only OTP timing: the
33
+ * server does not reveal whether the email is registered, so login versus
34
+ * signup is only known after `verifyLoginCode`.
35
+ */
32
36
  type SendLoginCodeResult = {
33
- /** Whether the BE recognizes the email as an existing account or a new signup. */flow: 'login' | 'signup'; /** Available login methods for this email. */
34
- methods: ('password' | 'passwordless')[]; /** OTP timing metadata. */
35
- otp: OtpInfo;
37
+ /** OTP timing metadata. */otp: OtpInfo;
36
38
  };
37
39
  /** Result of any resend method (`emailOtp.resendLoginCode`, `emailPassword.resendRegistrationCode`). */
38
40
  type ResendCodeResult = {
@@ -1,4 +1,4 @@
1
- import { n as AuthSession } from "./session-Cs_P7ojF.js";
1
+ import { n as AuthSession } from "./session-flM0yXx2.js";
2
2
 
3
3
  //#region src/store/sessionStore.d.ts
4
4
  /**
@@ -1,2 +1,2 @@
1
- import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "../sessionStore-BDdEpbL8.js";
1
+ import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "../sessionStore-D2kXjvNM.js";
2
2
  export { type LocalStorageStoreOptions, type SessionStore, localStorageStore, memoryStore };
@@ -1,4 +1,4 @@
1
- import { a as OtpInfo, c as ResendCodeResult, i as ErrorSource, l as SendLoginCodeResult, n as AuthSession, o as Project, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "../session-Cs_P7ojF.js";
2
- import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, f as Profile, i as LoginWithPasswordInput, l as SetPasswordInput, m as ProfileResult, n as ChangePasswordInput, o as ResendLoginCodeInput, p as ProfileAccount, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, t as CaptchaArgs, u as VerifyLoginCodeInput } from "../requests-jRLgNXyf.js";
1
+ import { a as OtpInfo, c as ResendCodeResult, i as ErrorSource, l as SendLoginCodeResult, n as AuthSession, o as Project, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "../session-flM0yXx2.js";
2
+ import { C as ProfileResult, S as ProfileAccount, _ as SendLoginCodeInput, a as CaptchaArgs, b as VerifyRegistrationCodeInput, c as GoogleExchangeInput, d as GoogleStartOptions, f as LoginWithGoogleInput, g as ResendRegistrationCodeInput, h as ResendLoginCodeInput, i as RoleStatus, l as GoogleSignInWithPopupOptions, m as RegisterInput, n as RoleApplicationState, o as ChangePasswordInput, p as LoginWithPasswordInput, r as RoleApplicationStatus, s as GoogleCompleteRedirectOptions, t as ApplyRoleInput, u as GoogleStartMode, v as SetPasswordInput, x as Profile, y as VerifyLoginCodeInput } from "../roles-BZecOymZ.js";
3
3
  import { n as AccessTokenPayloadProject, t as AccessTokenPayload } from "../tokens-BXrPLi5B.js";
4
- export { type AccessTokenPayload, type AccessTokenPayloadProject, type Account, type AuthSession, type CaptchaArgs, type ChangePasswordInput, type ErrorHandler, type ErrorSource, type LoginWithGoogleInput, type LoginWithPasswordInput, type OtpInfo, type Profile, type ProfileAccount, type ProfileResult, type Project, type RegisterInput, type RegisterResult, type ResendCodeResult, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SetPasswordInput, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput };
4
+ export { type AccessTokenPayload, type AccessTokenPayloadProject, type Account, type ApplyRoleInput, type AuthSession, type CaptchaArgs, type ChangePasswordInput, type ErrorHandler, type ErrorSource, type GoogleCompleteRedirectOptions, type GoogleExchangeInput, type GoogleSignInWithPopupOptions, type GoogleStartMode, type GoogleStartOptions, type LoginWithGoogleInput, type LoginWithPasswordInput, type OtpInfo, type Profile, type ProfileAccount, type ProfileResult, type Project, type RegisterInput, type RegisterResult, type ResendCodeResult, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type RoleApplicationState, type RoleApplicationStatus, type RoleStatus, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SetPasswordInput, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput };
@@ -1,4 +1,4 @@
1
- import { t as AuthClient } from "../index-URk8yf8l.js";
1
+ import { t as AuthClient } from "../index-DwFEdFaS.js";
2
2
  import { LocalAccount } from "viem";
3
3
 
4
4
  //#region src/wallet/index.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baliola/auth-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Client SDK for Baliola Auth",
5
5
  "keywords": [
6
6
  "auth",
@@ -1,75 +0,0 @@
1
- import { t as Account } from "./session-Cs_P7ojF.js";
2
-
3
- //#region src/types/profile.d.ts
4
- /** The account's profile record; every field is user-editable and nullable. */
5
- type Profile = {
6
- displayName: string | null;
7
- avatarUrl: string | null;
8
- primaryEmail: string | null;
9
- primaryPhone: string | null;
10
- };
11
- /** The account as returned by `auth.getProfile`, with sign-in capability flags. */
12
- type ProfileAccount = Account & {
13
- /**
14
- * `true` when the account can sign in with a password; `false` for
15
- * Google-only or code-only accounts, which should call
16
- * `auth.emailPassword.setPassword` rather than `changePassword`.
17
- */
18
- hasPassword: boolean;
19
- };
20
- /** Result of `auth.getProfile`, the identity read for the token holder. */
21
- type ProfileResult = {
22
- /** `status` is the live account status, not a token-time snapshot. */account: ProfileAccount; /** `null` when the account has no profile row yet. */
23
- profile: Profile | null;
24
- };
25
- //#endregion
26
- //#region src/types/requests.d.ts
27
- /** Optional captcha argument shared by every public unauthenticated entrypoint. */
28
- type CaptchaArgs = {
29
- /** Cloudflare Turnstile token. Required when captcha is enabled on the server. */captchaToken?: string;
30
- };
31
- type SendLoginCodeInput = {
32
- email: string;
33
- } & CaptchaArgs;
34
- type VerifyLoginCodeInput = {
35
- email: string;
36
- code: string;
37
- };
38
- type ResendLoginCodeInput = {
39
- email: string;
40
- } & CaptchaArgs;
41
- type RegisterInput = {
42
- email: string;
43
- password: string;
44
- } & CaptchaArgs;
45
- type VerifyRegistrationCodeInput = {
46
- email: string;
47
- code: string;
48
- };
49
- type ResendRegistrationCodeInput = {
50
- email: string;
51
- } & CaptchaArgs;
52
- type LoginWithPasswordInput = {
53
- email: string;
54
- password: string;
55
- } & CaptchaArgs;
56
- type SetPasswordInput = {
57
- password: string;
58
- };
59
- type ChangePasswordInput = {
60
- currentPassword: string;
61
- newPassword: string;
62
- };
63
- /**
64
- * Exactly one of the two shapes. `idToken` comes from Google Identity Services
65
- * (`renderButton` / One Tap). `code` is the authorization code from
66
- * `google.accounts.oauth2.initCodeClient` in popup mode; the server exchanges
67
- * it for the ID token, so the client secret never reaches the browser.
68
- */
69
- type LoginWithGoogleInput = {
70
- idToken: string;
71
- } | {
72
- code: string;
73
- };
74
- //#endregion
75
- export { RegisterInput as a, SendLoginCodeInput as c, VerifyRegistrationCodeInput as d, Profile as f, LoginWithPasswordInput as i, SetPasswordInput as l, ProfileResult as m, ChangePasswordInput as n, ResendLoginCodeInput as o, ProfileAccount as p, LoginWithGoogleInput as r, ResendRegistrationCodeInput as s, CaptchaArgs as t, VerifyLoginCodeInput as u };