@baliola/auth-sdk 0.2.0 → 0.4.1

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,14 +2,42 @@
2
2
 
3
3
  All notable changes to `@baliola/auth-sdk` are documented here.
4
4
 
5
+ ## 0.4.1
6
+
7
+ - chore(sdk): internal cleanup, no public API change. Removed dead code (the
8
+ unused `bearer-only-passthrough` auth-header mode, the throw-only
9
+ `refreshResponseShape` type helper, and the redundant `stripUndefined` body
10
+ helper, since the transport already drops `undefined` keys via
11
+ `JSON.stringify`), narrowed the internal request method union to the verbs
12
+ actually used (GET / POST), and deduplicated internal wire types against the
13
+ public `RegisterResult` / `ResendCodeResult`.
14
+ - chore(packaging): `viem` is now declared an **optional** peer dependency
15
+ (`peerDependenciesMeta`). It is only needed by the `./wallet` subpath, so
16
+ consumers who never import a wallet no longer have it auto-installed by npm 7+
17
+ and no longer see a missing-peer warning. Wallet users must install `viem`
18
+ themselves, which the wallet setup already required.
19
+ - Repo: this package moved out of the `baliola-auth` backend monorepo into its
20
+ own repository at `baliola/baliola-auth-sdk`. Package name, npm scope, and
21
+ every import path are unchanged.
22
+
23
+ ## 0.4.0
24
+
25
+ - feat(sdk): add `auth.getProfile()` → `GET /profile`. Returns `{ account, profile }` where `account` carries the **live** account status (read from the database, not the token) and `profile` is the account's profile record (`displayName`, `avatarUrl`, `primaryEmail`, `primaryPhone`) or `null` when none exists yet. New exported types: `Profile`, `ProfileResult`.
26
+ - Server-side context: baliola-auth is re-scoped to a pure identity provider. The `/modules`, `/api-keys/*`, and `/admin/api-keys/*` endpoints are removed (metering/billing move to the Baliola Console backend), and the `GET /profile` response no longer carries `moduleAccess`. The SDK never called any of those endpoints, so there are no SDK-side removals.
27
+
28
+ ## 0.3.0
29
+
30
+ - feat(sdk): add the custodial wallet namespace: `auth.wallet.getAddress()` (`GET /auth/wallet`, provisioned on first read) and `auth.wallet.signHash(hash)` (`POST /auth/wallet/sign`).
31
+ - feat(sdk): add `createRemoteSigner` under the `./wallet` subpath, a viem-compatible account whose `signMessage({ raw })` delegates to `POST /auth/wallet/sign`, so the custodial private key never leaves baliola-auth.
32
+
5
33
  ## 0.2.0
6
34
 
7
35
  Distribution change and clean-ups; no method-level API changes.
8
36
 
9
- - chore(release): publish to **public npm** (`https://registry.npmjs.org`) instead of GitHub Packages. Installing no longer requires a Personal Access Token, an `.npmrc` registry override, or SSO authorization plain `bun add @baliola/auth-sdk` works in every environment.
37
+ - chore(release): publish to **public npm** (`https://registry.npmjs.org`) instead of GitHub Packages. Installing no longer requires a Personal Access Token, an `.npmrc` registry override, or SSO authorization. Plain `bun add @baliola/auth-sdk` works in every environment.
10
38
  - docs: rewrite `README.md`, `docs/install.md`, `docs/ciSetup.md`, and `docs/troubleshooting.md` to reflect public-npm install. CI snippets no longer need build secrets.
11
39
  - Consumers previously installing from GitHub Packages should remove any `@baliola:registry=https://npm.pkg.github.com` entries from `~/.npmrc` and project-local `.npmrc` files before upgrading.
12
- - fix(types): realign `AccessTokenPayload` with the server's current JWT shape. The flat `projectId?: string | null` / `projectName?: string | null` pair is replaced by `projects: AccessTokenPayloadProject[]` (each entry carries `{ id, name, clientId }`). Type-only change affects consumers that decode the JWT manually and reference this type. A scoped login (`clientId` supplied) yields a single-entry array; a central login yields every project the account has active roles in.
40
+ - fix(types): realign `AccessTokenPayload` with the server's current JWT shape. The flat `projectId?: string | null` / `projectName?: string | null` pair is replaced by `projects: AccessTokenPayloadProject[]` (each entry carries `{ id, name, clientId }`). Type-only change, affecting consumers that decode the JWT manually and reference this type. A scoped login (`clientId` supplied) yields a single-entry array; a central login yields every project the account has active roles in.
13
41
  - chore!: remove the `./internal` subpath export and the `internal/` source folder (`createValidateCache`, `newIdempotencyKey`, `consumeHeaders`). Those helpers targeted the retired `/internal/api-keys/*` endpoints and the static "internal token" concept. Service-to-service callers now authenticate with an admin-tier API key (`baliola_admin_…`) against `/admin/api-keys/*`; cache + idempotency helpers for that surface will ship in a dedicated admin SDK.
14
42
 
15
43
  ## 0.1.0
@@ -18,7 +46,7 @@ Breaking redesign of the auth API and SDK surface to eliminate dev confusion bet
18
46
 
19
47
  ### Breaking changes
20
48
 
21
- - feat(sdk)!: replace the flat method surface with namespaced flows `auth.emailOtp.*`, `auth.emailPassword.*`, `auth.google.*`. The namespaces match the DB provider names (`email_otp`, `email_password`).
49
+ - feat(sdk)!: replace the flat method surface with namespaced flows: `auth.emailOtp.*`, `auth.emailPassword.*`, `auth.google.*`. The namespaces match the DB provider names (`email_otp`, `email_password`).
22
50
  - feat(sdk)!: rename verb-overloaded methods so each one's purpose is unambiguous at the call site:
23
51
  - `auth.requestEmailOtp` → `auth.emailOtp.sendLoginCode`
24
52
  - `auth.verifyEmailOtp` → `auth.emailOtp.verifyLoginCode`
@@ -29,14 +57,14 @@ Breaking redesign of the auth API and SDK surface to eliminate dev confusion bet
29
57
  - `auth.changePassword` → `auth.emailPassword.changePassword`
30
58
  - `auth.loginWithGoogle` → `auth.google.login`
31
59
  - feat(sdk)!: verify-code methods take `{ email, code }` instead of `{ email, otp }`.
32
- - feat(sdk)!: `auth.emailPassword.register` no longer branches on `'otp_sent' | 'linked'` it always sends a verification OTP and returns `{ otp: OtpInfo }`. The single-step "linked" path is removed.
33
- - feat(sdk)!: send/resend/register success responses replace `expiresInMinutes` with a structured `OtpInfo` payload `expiresInSeconds`, `expiresAt`, `canResendInSeconds`, `resendsRemaining`.
60
+ - feat(sdk)!: `auth.emailPassword.register` no longer branches on `'otp_sent' | 'linked'`. It always sends a verification OTP and returns `{ otp: OtpInfo }`. The single-step "linked" path is removed.
61
+ - feat(sdk)!: send/resend/register success responses replace `expiresInMinutes` with a structured `OtpInfo` payload: `expiresInSeconds`, `expiresAt`, `canResendInSeconds`, `resendsRemaining`.
34
62
  - feat(sdk)!: backend endpoint paths renamed to match the namespaces: `POST /auth/email-otp/send-login-code`, `/auth/email-otp/verify-login-code`, `/auth/email-otp/resend-login-code`, `/auth/email-password/register`, `/auth/email-password/verify-registration-code`, `/auth/email-password/resend-registration-code`, etc.
35
63
 
36
64
  ### New features
37
65
 
38
- - feat(sdk): add typed error subclasses under `AuthError` `OtpInvalidError` (with `attemptsRemaining`, `canResendInSeconds`), `OtpExpiredError`, `MaxAttemptsError` (with `retryAfterSeconds`), `NoPendingOtpError`, `ResendCooldownError` (with `canResendInSeconds`), `MaxResendsError`, `InvalidCredentialsError`, `NoPasswordSetError`, `AccountSuspendedError`, `EmailAlreadyHasPasswordError`, `CaptchaFailedError`, `RateLimitedError` (with `retryAfterSeconds`, `retryAfterHuman`), `InvalidPasswordError` (with `reason`), `InvalidEmailError`. Branch on `instanceof` for TS narrowing, or `err.code` for stable string matching. Backend responses now carry the structured context fields via `error.details` so the SDK lifts them into the typed properties.
39
- - feat(sdk): add dedicated resend methods `auth.emailOtp.resendLoginCode` and `auth.emailPassword.resendRegistrationCode`. Each enforces its own resend cooldown (`OTP_RESEND_COOLDOWN_SECONDS`, default 60s) and per-OTP cap (`OTP_MAX_RESENDS`, default 3) on top of the per-OTP attempts limit.
66
+ - feat(sdk): add typed error subclasses under `AuthError`: `OtpInvalidError` (with `attemptsRemaining`, `canResendInSeconds`), `OtpExpiredError`, `MaxAttemptsError` (with `retryAfterSeconds`), `NoPendingOtpError`, `ResendCooldownError` (with `canResendInSeconds`), `MaxResendsError`, `InvalidCredentialsError`, `NoPasswordSetError`, `AccountSuspendedError`, `EmailAlreadyHasPasswordError`, `CaptchaFailedError`, `RateLimitedError` (with `retryAfterSeconds`, `retryAfterHuman`), `InvalidPasswordError` (with `reason`), `InvalidEmailError`. Branch on `instanceof` for TS narrowing, or `err.code` for stable string matching. Backend responses now carry the structured context fields via `error.details` so the SDK lifts them into the typed properties.
67
+ - feat(sdk): add dedicated resend methods: `auth.emailOtp.resendLoginCode` and `auth.emailPassword.resendRegistrationCode`. Each enforces its own resend cooldown (`OTP_RESEND_COOLDOWN_SECONDS`, default 60s) and per-OTP cap (`OTP_MAX_RESENDS`, default 3) on top of the per-OTP attempts limit.
40
68
  - feat(sdk): add `captchaToken` option on every public unauthenticated entrypoint (`sendLoginCode`, `resendLoginCode`, `register`, `resendRegistrationCode`, `login`). Backend verifies via Cloudflare Turnstile when `TURNSTILE_SECRET_KEY` is set; dev bypass when unset; server-to-server clients can be allowlisted via `CAPTCHA_BYPASS_CLIENT_IDS`.
41
69
  - feat(sdk): `auth.emailOtp.sendLoginCode` response includes `flow: 'login' | 'signup'` and `methods: ('password' | 'passwordless')[]` so the UI can adapt to the account state without an extra round-trip.
42
70
 
@@ -47,30 +75,29 @@ Breaking redesign of the auth API and SDK surface to eliminate dev confusion bet
47
75
 
48
76
  ## 0.0.5
49
77
 
50
- - feat(sdk): add cross-tab session sync via `BroadcastChannel` when one tab logs in, logs out, or refreshes the access token, every other tab on the same origin updates its in-memory mirror in real time.
78
+ - feat(sdk): add cross-tab session sync via `BroadcastChannel`. When one tab logs in, logs out, or refreshes the access token, every other tab on the same origin updates its in-memory mirror in real time.
51
79
  - feat(sdk): add `auth.fetch({ requireAuth: true })` per-call option that throws `AuthError(401)` before sending when no session is loaded.
52
80
  - feat(sdk): add `auth.fetch({ disableRefresh: true })` per-call option that skips both proactive and reactive refresh, letting a 401 propagate to the caller.
53
81
 
54
82
  ## 0.0.4
55
83
 
56
- - feat(sdk): add `changePassword({ currentPassword, newPassword })` paired with `POST /auth/email-password/change-password` server route verifies the current password, rotates the bcrypt hash, revokes every other session for the account (caller stays signed in), and sends a confirmation email out-of-band.
84
+ - feat(sdk): add `changePassword({ currentPassword, newPassword })` paired with `POST /auth/email-password/change-password` server route. It verifies the current password, rotates the bcrypt hash, revokes every other session for the account (caller stays signed in), and sends a confirmation email out-of-band.
57
85
  - chore(auth): relax password policy to require only 8+ characters and at least one uppercase letter (number and special-character requirements dropped).
58
86
 
59
87
  ## 0.0.3
60
88
 
61
- - chore(sdk): bump version to 0.0.3
62
- - feat(sdk): expose `Project.allowedOrigins` on `AuthSession`
89
+ - feat(sdk): expose `Project.allowedOrigins` on `AuthSession`, so a consumer can read the origins a project accepts without a second call.
63
90
 
64
91
  ## 0.0.2
65
92
 
66
- - chore(sdk): bump version to 0.0.2
67
- - refactor(sdk): drop pre-release version labels from public JSDoc
68
- - docs(sdk): inline install in README and move roadmap to ROADMAP.md
69
- - docs(sdk): add user install, troubleshooting, and CI setup guides
70
- - docs(sdk): add README with usage and configuration reference
71
- - test(sdk): add unit tests for client, transport, and store
72
- - feat(sdk): implement auth client with auto-refresh, session mirror, and consumer fetch helper
93
+ The first usable release. 0.0.1 published the package shell; this one puts a working client inside it.
94
+
95
+ - feat(sdk): implement the auth client, with proactive and reactive access-token refresh, the in-memory session mirror, and the `auth.fetch` helper that attaches the access token for consumers.
96
+ - refactor(sdk): drop pre-release version labels from public JSDoc, so published docs no longer advertise a version that moves every release.
97
+ - docs(sdk): add the README API reference plus the install, troubleshooting, and CI setup guides, and move the roadmap out to `ROADMAP.md`.
73
98
 
74
99
  ## 0.0.1
75
100
 
76
- - feat(sdk): scaffold `@baliola/auth-sdk` package config
101
+ - feat(sdk): scaffold the `@baliola/auth-sdk` package config. Package shell and build only, no client implementation yet.
102
+
103
+ These 0.0.x releases went to GitHub Packages, not public npm. 0.2.0 moved distribution to public npm, and nothing below 0.2.0 is installable from the public registry.
package/README.md CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
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.
4
4
 
5
- For deeper detail see [docs/concepts.md](./docs/concepts.md).
5
+ Guides:
6
+
7
+ - [docs/concepts.md](./docs/concepts.md): flows, session mirror, refresh, cross-tab sync, errors.
8
+ - [docs/install.md](./docs/install.md): install and first call.
9
+ - [docs/ciSetup.md](./docs/ciSetup.md): installing this SDK in your own pipeline.
10
+ - [docs/troubleshooting.md](./docs/troubleshooting.md): install and build symptoms.
6
11
 
7
12
  ## Install
8
13
 
@@ -18,11 +23,14 @@ Published to public npm under the `@baliola` scope — no registry config or aut
18
23
 
19
24
  ## Quick start
20
25
 
26
+ `AUTH_URL` below is the base URL of the Baliola Auth deployment you are targeting. It differs per
27
+ environment. Baliola developers: the per-environment values are in the internal integration docs.
28
+
21
29
  ```ts
22
30
  import { createAuthClient, localStorageStore, OtpInvalidError } from '@baliola/auth-sdk';
23
31
 
24
32
  const auth = createAuthClient({
25
- baseUrl: 'https://baliola-auth.baliola.dev',
33
+ baseUrl: AUTH_URL,
26
34
  clientId: 'your-project-client-id', // omit for a central multi-project login
27
35
  store: localStorageStore(), // omit for in-memory (server-side / tests)
28
36
  });
@@ -95,6 +103,30 @@ auth.emailPassword.changePassword({ currentPassword, newPassword });
95
103
  auth.google.login({ idToken }); // → AuthSession
96
104
  ```
97
105
 
106
+ ### Custodial wallet (smart-account signer)
107
+
108
+ ```ts
109
+ import { createAuthClient } from '@baliola/auth-sdk';
110
+ import { createRemoteSigner } from '@baliola/auth-sdk/wallet';
111
+ import { toSmartAccount } from '@baliola/smart-account-sdk';
112
+
113
+ const auth = createAuthClient({ baseUrl });
114
+ await auth.emailOtp.verifyLoginCode({ email, code }); // authenticated session
115
+
116
+ const owner = await createRemoteSigner(auth); // viem LocalAccount, signs server-side
117
+ const account = await toSmartAccount({ owner, chain: 'macTestnet' });
118
+ ```
119
+
120
+ The private key never leaves baliola-auth; `owner.signMessage({ raw })` calls `POST /auth/wallet/sign`.
121
+
122
+ ### Profile (identity read)
123
+
124
+ ```ts
125
+ const { account, profile } = await auth.getProfile(); // GET /profile (authenticated)
126
+ // account = { id, email, status } — status is live, not a token-time snapshot
127
+ // profile = { displayName, avatarUrl, primaryEmail, primaryPhone } | null
128
+ ```
129
+
98
130
  ### Session lifecycle
99
131
 
100
132
  ```ts
@@ -210,7 +242,7 @@ Cross-tab sync (login / logout / refresh) is automatic via `BroadcastChannel`, r
210
242
 
211
243
  ```ts
212
244
  createAuthClient({
213
- baseUrl: 'https://baliola-auth.baliola.dev', // required, no trailing slash
245
+ baseUrl: AUTH_URL, // required, no trailing slash
214
246
  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.
215
247
  store: memoryStore(), // default; or localStorageStore() / custom
216
248
  fetch: globalThis.fetch, // override for tests / interceptors
@@ -239,6 +271,8 @@ if (session) {
239
271
  }
240
272
  ```
241
273
 
274
+ `decodeJwt` reads claims without checking the signature, which is fine on the client where the token came from your own session. A service that trusts the token for authorization must verify it: the tokens are EdDSA (Ed25519), issuer `baliola-auth`, and the public keys are served at `GET /.well-known/jwks.json` on the same `baseUrl`.
275
+
242
276
  ## Sample response shapes
243
277
 
244
278
  **`auth.emailOtp.sendLoginCode` success:**
@@ -273,7 +307,7 @@ if (session) {
273
307
 
274
308
  ```json
275
309
  {
276
- "accessToken": "eyJhbGciOiJIUzI1NiIs...",
310
+ "accessToken": "eyJhbGciOiJFZERTQSIs...",
277
311
  "refreshToken": "session-uuid",
278
312
  "expiresIn": 3600,
279
313
  "account": { "id": "...", "email": "user@example.com", "status": "active" },
@@ -32,6 +32,7 @@ var AuthError = class extends Error {
32
32
  Object.setPrototypeOf(this, new.target.prototype);
33
33
  }
34
34
  };
35
+ /** Concrete subclasses with structured fields lifted from `details` */
35
36
  var OtpInvalidError = class extends AuthError {
36
37
  attemptsRemaining;
37
38
  canResendInSeconds;
@@ -30,6 +30,7 @@ declare class AuthError extends Error {
30
30
  readonly cause?: unknown;
31
31
  constructor(init: AuthErrorInit);
32
32
  }
33
+ /** Concrete subclasses with structured fields lifted from `details` */
33
34
  declare class OtpInvalidError extends AuthError {
34
35
  readonly attemptsRemaining: number;
35
36
  readonly canResendInSeconds: number;
@@ -1,2 +1,2 @@
1
- import { a as GoogleNamespace, c as createAuthClient, i as EmailPasswordNamespace, l as AuthFetchInit, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient } from "../index-G-gqveGn.js";
2
- export { AuthClient, AuthFetchInit, CreateAuthClientOptions, EmailOtpNamespace, EmailPasswordNamespace, GoogleNamespace, ProactiveRefreshConfig, SubscribeOptions, createAuthClient };
1
+ import { a as GoogleNamespace, c as WalletNamespace, f as AuthFetchInit, i as EmailPasswordNamespace, l as createAuthClient, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient } from "../index-B2RuGLX_.js";
2
+ export { AuthClient, AuthFetchInit, CreateAuthClientOptions, EmailOtpNamespace, EmailPasswordNamespace, GoogleNamespace, ProactiveRefreshConfig, SubscribeOptions, WalletNamespace, createAuthClient };
@@ -1,2 +1,2 @@
1
- import { t as createAuthClient } from "../client-BcyoQ5Cj.js";
1
+ import { t as createAuthClient } from "../client-DU_DerYi.js";
2
2
  export { createAuthClient };
@@ -1,4 +1,4 @@
1
- import { _ as authErrorFromResponse, g as authErrorFromFetchFailure, n as AuthError } from "./authError-DbEZJnC4.js";
1
+ import { _ as authErrorFromResponse, g as authErrorFromFetchFailure, n as AuthError } from "./authError-CYUAl2Jt.js";
2
2
  import { n as memoryStore } from "./sessionStore-DD6lON9W.js";
3
3
  //#region src/client/methods.ts
4
4
  function toAuthSession(data) {
@@ -23,21 +23,17 @@ function createMethods(ctx) {
23
23
  clientId: ctx.clientId
24
24
  };
25
25
  };
26
- const stripUndefined = (body) => {
27
- const out = {};
28
- for (const [k, v] of Object.entries(body)) if (v !== void 0) out[k] = v;
29
- return out;
30
- };
31
26
  return {
27
+ /** emailOtp.* (passwordless) */
32
28
  async sendLoginCode(input) {
33
29
  return ctx.transport.request({
34
30
  path: "/auth/email-otp/send-login-code",
35
31
  method: "POST",
36
32
  authMode: "none",
37
- body: stripUndefined(withClientId({
33
+ body: withClientId({
38
34
  email: input.email,
39
35
  captchaToken: input.captchaToken
40
- }))
36
+ })
41
37
  });
42
38
  },
43
39
  async verifyLoginCode(input) {
@@ -56,22 +52,23 @@ function createMethods(ctx) {
56
52
  path: "/auth/email-otp/resend-login-code",
57
53
  method: "POST",
58
54
  authMode: "none",
59
- body: stripUndefined(withClientId({
55
+ body: withClientId({
60
56
  email: input.email,
61
57
  captchaToken: input.captchaToken
62
- }))
58
+ })
63
59
  });
64
60
  },
61
+ /** emailPassword.* (password flow) */
65
62
  async register(input) {
66
63
  return ctx.transport.request({
67
64
  path: "/auth/email-password/register",
68
65
  method: "POST",
69
66
  authMode: "none",
70
- body: stripUndefined(withClientId({
67
+ body: withClientId({
71
68
  email: input.email,
72
69
  password: input.password,
73
70
  captchaToken: input.captchaToken
74
- }))
71
+ })
75
72
  });
76
73
  },
77
74
  async verifyRegistrationCode(input) {
@@ -90,10 +87,10 @@ function createMethods(ctx) {
90
87
  path: "/auth/email-password/resend-registration-code",
91
88
  method: "POST",
92
89
  authMode: "none",
93
- body: stripUndefined(withClientId({
90
+ body: withClientId({
94
91
  email: input.email,
95
92
  captchaToken: input.captchaToken
96
- }))
93
+ })
97
94
  });
98
95
  },
99
96
  async loginWithPassword(input) {
@@ -101,11 +98,11 @@ function createMethods(ctx) {
101
98
  path: "/auth/email-password/login",
102
99
  method: "POST",
103
100
  authMode: "none",
104
- body: stripUndefined(withClientId({
101
+ body: withClientId({
105
102
  email: input.email,
106
103
  password: input.password,
107
104
  captchaToken: input.captchaToken
108
- }))
105
+ })
109
106
  }));
110
107
  },
111
108
  async setPassword(input) {
@@ -127,6 +124,7 @@ function createMethods(ctx) {
127
124
  }
128
125
  });
129
126
  },
127
+ /** google.* */
130
128
  async loginWithGoogle(input) {
131
129
  return toAuthSession(await ctx.transport.request({
132
130
  path: "/auth/google/login",
@@ -135,20 +133,37 @@ function createMethods(ctx) {
135
133
  body: withClientId({ idToken: input.idToken })
136
134
  }));
137
135
  },
136
+ /** wallet.* */
137
+ async getWalletAddress() {
138
+ return ctx.transport.request({
139
+ path: "/auth/wallet",
140
+ method: "GET",
141
+ authMode: "bearer+session"
142
+ });
143
+ },
144
+ async signWalletHash(hash) {
145
+ return ctx.transport.request({
146
+ path: "/auth/wallet/sign",
147
+ method: "POST",
148
+ authMode: "bearer+session",
149
+ body: { hash }
150
+ });
151
+ },
152
+ /** profile */
153
+ async getProfile() {
154
+ return ctx.transport.request({
155
+ path: "/profile",
156
+ method: "GET",
157
+ authMode: "bearer+session"
158
+ });
159
+ },
160
+ /** session lifecycle */
138
161
  async logoutRequest() {
139
162
  await ctx.transport.request({
140
163
  path: "/auth/logout",
141
164
  method: "POST",
142
165
  authMode: "bearer+session"
143
166
  });
144
- },
145
- /**
146
- * Type-only helper: declares the refresh response shape so consumers
147
- * pulling the inferred Methods type don't break. Refresh itself is
148
- * handled by transport.refreshSession() to keep stampede protection.
149
- */
150
- refreshResponseShape() {
151
- throw new Error("refreshResponseShape is a type-only helper");
152
167
  }
153
168
  };
154
169
  }
@@ -180,9 +195,6 @@ function createTransport(options) {
180
195
  case "session-only":
181
196
  headers.set("X-Session-ID", session.refreshToken);
182
197
  break;
183
- case "bearer-only-passthrough":
184
- headers.set("Authorization", `Bearer ${session.accessToken}`);
185
- break;
186
198
  case "none": break;
187
199
  }
188
200
  return headers;
@@ -460,6 +472,11 @@ function createAuthClient(options) {
460
472
  await setSession(session);
461
473
  return session;
462
474
  } },
475
+ wallet: {
476
+ getAddress: async () => (await methods.getWalletAddress()).address,
477
+ signHash: async (hash) => (await methods.signWalletHash(hash)).signature
478
+ },
479
+ getProfile: () => methods.getProfile(),
463
480
  async refresh() {
464
481
  return transport.refreshSession();
465
482
  },
@@ -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-C5g5jP5l.js";
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
2
  export { AccountSuspendedError, AuthError, type AuthErrorInit, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, 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-DbEZJnC4.js";
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
2
  export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse };
@@ -1,4 +1,4 @@
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";
1
+ import { c as ResendCodeResult, l as SendLoginCodeResult, n as AuthSession, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "./session-Cs_P7ojF.js";
2
2
  import { n as SessionStore } from "./sessionStore-BDdEpbL8.js";
3
3
  import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, i as LoginWithPasswordInput, l as SetPasswordInput, n as ChangePasswordInput, o as ResendLoginCodeInput, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, u as VerifyLoginCodeInput } from "./requests-eTORIjY5.js";
4
4
 
@@ -26,6 +26,20 @@ type AuthFetchInit = RequestInit & {
26
26
  disableRefresh?: boolean;
27
27
  };
28
28
  //#endregion
29
+ //#region src/types/profile.d.ts
30
+ /** The account's profile record; every field is user-editable and nullable. */
31
+ type Profile = {
32
+ displayName: string | null;
33
+ avatarUrl: string | null;
34
+ primaryEmail: string | null;
35
+ primaryPhone: string | null;
36
+ };
37
+ /** Result of `auth.getProfile` — identity read for the token holder. */
38
+ type ProfileResult = {
39
+ /** `status` is the live account status, not a token-time snapshot. */account: Account; /** `null` when the account has no profile row yet. */
40
+ profile: Profile | null;
41
+ };
42
+ //#endregion
29
43
  //#region src/client/index.d.ts
30
44
  type ProactiveRefreshConfig = false | {
31
45
  /** Refresh proactively if `expiresAt - now < leadTimeMs`. Defaults to 60_000 (60s). */leadTimeMs?: number;
@@ -119,10 +133,23 @@ type EmailPasswordNamespace = {
119
133
  type GoogleNamespace = {
120
134
  login(input: LoginWithGoogleInput): Promise<AuthSession>;
121
135
  };
136
+ /** Custodial wallet (Console tier). */
137
+ type WalletNamespace = {
138
+ /** The account's custodial EOA address (provisioned on first read). */getAddress(): Promise<string>; /** Sign a 32-byte userOp hash; returns a 65-byte recoverable signature. */
139
+ signHash(hash: string): Promise<string>;
140
+ };
122
141
  type AuthClient = {
123
142
  emailOtp: EmailOtpNamespace;
124
143
  emailPassword: EmailPasswordNamespace;
125
144
  google: GoogleNamespace;
145
+ wallet: WalletNamespace;
146
+ /**
147
+ * Fetch the token holder's identity: account (id, email, live status) plus
148
+ * its profile record (`profile: null` when none exists yet).
149
+ *
150
+ * @throws AuthError on 401 (missing/expired session) and other API errors.
151
+ */
152
+ getProfile(): Promise<ProfileResult>;
126
153
  refresh(): Promise<AuthSession>;
127
154
  logout(): Promise<void>;
128
155
  getSession(): AuthSession | null;
@@ -139,4 +166,4 @@ type AuthClient = {
139
166
  };
140
167
  declare function createAuthClient(options: CreateAuthClientOptions): AuthClient;
141
168
  //#endregion
142
- export { GoogleNamespace as a, createAuthClient as c, EmailPasswordNamespace as i, AuthFetchInit as l, CreateAuthClientOptions as n, ProactiveRefreshConfig as o, EmailOtpNamespace as r, SubscribeOptions as s, AuthClient as t };
169
+ export { GoogleNamespace as a, WalletNamespace as c, ProfileResult as d, AuthFetchInit as f, EmailPasswordNamespace as i, createAuthClient as l, CreateAuthClientOptions as n, ProactiveRefreshConfig as o, EmailOtpNamespace as r, SubscribeOptions as s, AuthClient as t, Profile as u };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,19 @@
1
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 createAuthClient, i as EmailPasswordNamespace, l as AuthFetchInit, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient } from "./index-G-gqveGn.js";
2
+ import { a as GoogleNamespace, c as WalletNamespace, d as ProfileResult, f as AuthFetchInit, i as EmailPasswordNamespace, l as createAuthClient, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient, u as Profile } from "./index-B2RuGLX_.js";
3
3
  import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "./sessionStore-BDdEpbL8.js";
4
4
  import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, i as LoginWithPasswordInput, l as SetPasswordInput, n as ChangePasswordInput, o as ResendLoginCodeInput, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, t as CaptchaArgs, u as VerifyLoginCodeInput } from "./requests-eTORIjY5.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-C5g5jP5l.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";
6
6
  import { n as AccessTokenPayloadProject, t as AccessTokenPayload } from "./tokens-BXrPLi5B.js";
7
- 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 Project, RateLimitedError, type RegisterInput, type RegisterResult, type ResendCodeResult, ResendCooldownError, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SessionStore, type SetPasswordInput, type SubscribeOptions, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
7
+
8
+ //#region src/types/wallet.d.ts
9
+ type WalletAddressResult = {
10
+ address: string;
11
+ isActive: boolean;
12
+ createdAt: string;
13
+ };
14
+ type SignHashResult = {
15
+ signature: string;
16
+ address: string;
17
+ };
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 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 };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createAuthClient } from "./client-BcyoQ5Cj.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-DbEZJnC4.js";
1
+ import { t as createAuthClient } from "./client-DU_DerYi.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";
3
3
  import { n as memoryStore, t as localStorageStore } from "./sessionStore-DD6lON9W.js";
4
4
  export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
@@ -0,0 +1,19 @@
1
+ import { t as AuthClient } from "../index-B2RuGLX_.js";
2
+ import { LocalAccount } from "viem";
3
+
4
+ //#region src/wallet/index.d.ts
5
+ /** The account's custodial wallet address. */
6
+ declare function getAddress(auth: AuthClient): Promise<string>;
7
+ /**
8
+ * Build a viem `LocalAccount` backed by the server-side custodial signer. Drop
9
+ * it into the smart-account SDK as `toSmartAccount({ owner })`. Only
10
+ * `signMessage` is supported — the smart-account SDK signs userOps via
11
+ * `signMessage({ raw })`; `signTransaction`/`signTypedData` are not custodial
12
+ * operations and throw.
13
+ *
14
+ * @param auth An authenticated AuthClient.
15
+ * @returns A viem LocalAccount whose signing is performed by baliola-auth.
16
+ */
17
+ declare function createRemoteSigner(auth: AuthClient): Promise<LocalAccount>;
18
+ //#endregion
19
+ export { createRemoteSigner, getAddress };
@@ -0,0 +1,34 @@
1
+ import { toAccount } from "viem/accounts";
2
+ //#region src/wallet/index.ts
3
+ /** The account's custodial wallet address. */
4
+ async function getAddress(auth) {
5
+ return auth.wallet.getAddress();
6
+ }
7
+ /**
8
+ * Build a viem `LocalAccount` backed by the server-side custodial signer. Drop
9
+ * it into the smart-account SDK as `toSmartAccount({ owner })`. Only
10
+ * `signMessage` is supported — the smart-account SDK signs userOps via
11
+ * `signMessage({ raw })`; `signTransaction`/`signTypedData` are not custodial
12
+ * operations and throw.
13
+ *
14
+ * @param auth An authenticated AuthClient.
15
+ * @returns A viem LocalAccount whose signing is performed by baliola-auth.
16
+ */
17
+ async function createRemoteSigner(auth) {
18
+ return toAccount({
19
+ address: await auth.wallet.getAddress(),
20
+ async signMessage({ message }) {
21
+ const raw = typeof message === "object" && message !== null && "raw" in message ? message.raw : message;
22
+ const hash = typeof raw === "string" ? raw : `0x${Buffer.from(raw).toString("hex")}`;
23
+ return await auth.wallet.signHash(hash);
24
+ },
25
+ async signTransaction() {
26
+ throw new Error("baliola remote signer: signTransaction is not supported");
27
+ },
28
+ async signTypedData() {
29
+ throw new Error("baliola remote signer: signTypedData is not supported");
30
+ }
31
+ });
32
+ }
33
+ //#endregion
34
+ export { createRemoteSigner, getAddress };
package/package.json CHANGED
@@ -1,7 +1,20 @@
1
1
  {
2
2
  "name": "@baliola/auth-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.4.1",
4
4
  "description": "Client SDK for Baliola Auth",
5
+ "keywords": [
6
+ "auth",
7
+ "authentication",
8
+ "passwordless",
9
+ "otp",
10
+ "email-otp",
11
+ "jwt",
12
+ "session",
13
+ "sdk",
14
+ "typescript",
15
+ "esm",
16
+ "baliola"
17
+ ],
5
18
  "license": "SEE LICENSE IN LICENSE",
6
19
  "author": "Baliola Development Team",
7
20
  "type": "module",
@@ -29,6 +42,10 @@
29
42
  "./types": {
30
43
  "types": "./dist/types/index.d.ts",
31
44
  "import": "./dist/types/index.js"
45
+ },
46
+ "./wallet": {
47
+ "types": "./dist/wallet/index.d.ts",
48
+ "import": "./dist/wallet/index.js"
32
49
  }
33
50
  },
34
51
  "files": [
@@ -37,24 +54,47 @@
37
54
  "README.md",
38
55
  "CHANGELOG.md"
39
56
  ],
57
+ "engines": {
58
+ "node": ">=20.3.0"
59
+ },
40
60
  "publishConfig": {
41
61
  "registry": "https://registry.npmjs.org",
42
62
  "access": "public"
43
63
  },
44
64
  "repository": {
45
65
  "type": "git",
46
- "url": "git+https://github.com/baliola/baliola-auth.git",
47
- "directory": "sdk"
66
+ "url": "git+https://github.com/baliola/baliola-auth-sdk.git"
67
+ },
68
+ "homepage": "https://github.com/baliola/baliola-auth-sdk#readme",
69
+ "bugs": {
70
+ "url": "https://github.com/baliola/baliola-auth-sdk/issues"
48
71
  },
49
72
  "scripts": {
50
73
  "build": "tsdown",
51
- "typecheck": "tsc --noEmit",
74
+ "typecheck": "bash scripts/typecheck.sh",
52
75
  "test": "bun test test/unit",
76
+ "lint": "eslint",
77
+ "lint:fix": "eslint --fix",
78
+ "format": "prettier --write \"{src,test}/**/*.ts\" \"*.ts\"",
79
+ "format:check": "prettier --check \"{src,test}/**/*.ts\" \"*.ts\"",
53
80
  "prepublishOnly": "bun run build"
54
81
  },
82
+ "peerDependencies": { "viem": "^2.47.6" },
83
+ "peerDependenciesMeta": { "viem": { "optional": true } },
55
84
  "devDependencies": {
56
- "@types/bun": "latest",
85
+ "@eslint/js": "^10.0.1",
86
+ "@types/bun": "^1.4.0",
87
+ "eslint": "^10.7.0",
88
+ "eslint-config-prettier": "^10.1.8",
89
+ "eslint-plugin-import-x": "^4.17.1",
90
+ "eslint-plugin-prettier": "^5.5.6",
91
+ "eslint-plugin-unused-imports": "^4.4.1",
92
+ "globals": "^17.7.0",
93
+ "jiti": "^2.7.0",
94
+ "prettier": "3.8.1",
57
95
  "tsdown": "^0.21.10",
58
- "typescript": "^5.7.0"
96
+ "typescript": "^5.7.0",
97
+ "typescript-eslint": "^8.64.0",
98
+ "viem": "^2.47.6"
59
99
  }
60
100
  }