@aithos/sdk 0.1.0-alpha.32 → 0.1.0-alpha.34

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.
@@ -325,6 +325,42 @@ export declare class AithosAuth {
325
325
  * @internal
326
326
  */
327
327
  _findDelegateForSubject(did: string): DelegateActor | undefined;
328
+ /**
329
+ * Sign in with email + password, dispatching automatically between
330
+ * the legacy zero-knowledge flow ({@link signIn}) and the custodial
331
+ * flow ({@link signInCustodial}) based on which mode the account
332
+ * was provisioned with.
333
+ *
334
+ * Use this in apps that want a single sign-in form for users who
335
+ * may have been created under either mode (e.g. an app that's
336
+ * migrating from zk to custodial — pre-existing users stay zk
337
+ * forever, new ones go custodial, the SDK figures it out).
338
+ *
339
+ * Strategy: try {@link signInCustodial} first (the modern path).
340
+ * If the backend reports `auth_invalid_credentials` — which it
341
+ * uniformly returns for "wrong password", "unknown user", AND
342
+ * "user exists but not in custodial mode" (anti-enum) — fall
343
+ * back to {@link signIn} (zk).
344
+ *
345
+ * Other failure modes from the custodial path are NOT swallowed:
346
+ * - `auth_email_not_verified` → propagate (user is custodial but
347
+ * hasn't clicked the confirmation link yet; the app should
348
+ * surface a "resend mail" CTA rather than retrying as zk,
349
+ * which would also fail and mask the real cause)
350
+ * - server / network errors → propagate (don't double the
351
+ * incident by retrying through the other flow)
352
+ *
353
+ * Latency profile:
354
+ * - Pure custodial (success or wrong pwd) : 1 round-trip
355
+ * - Pure zk (any outcome) : 1 custodial probe + 2 zk
356
+ * - Unknown email : same as zk worst case
357
+ *
358
+ * Anti-enum note: timing slightly leaks the mode (custodial path is
359
+ * faster than zk). Acceptable for V1 — rate limiting + strong
360
+ * passwords are the real defenses. A future strict-anti-enum mode
361
+ * could race both paths in parallel and accept the 2x backend load.
362
+ */
363
+ signInAuto(input: SignInInput): Promise<AithosSession>;
328
364
  signIn(input: SignInInput): Promise<AithosSession>;
329
365
  signUp(input: SignUpInput): Promise<SignUpResult>;
330
366
  /**
package/dist/src/auth.js CHANGED
@@ -20,7 +20,7 @@
20
20
  // JWT-less sessions (recovery / mandate sign-ins) are valid: the
21
21
  // keyStore is the source of truth for "is the user signed in", the
22
22
  // JWT is auxiliary for compute/wallet.
23
- import { buildBlobPlaintext, buildSignedEnvelope, createBrowserIdentity, decryptBlob, DEFAULT_KDF, deriveAuthAndEncKeys, encryptBlob, parseBlob, randomNonce, randomSalt, serializeBlob, signedDidDocument, zeroize, } from "@aithos/protocol-client";
23
+ import { browserIdentityFromStored, buildBlobPlaintext, buildSignedEnvelope, createBrowserIdentity, decryptBlob, DEFAULT_KDF, deriveAuthAndEncKeys, encryptBlob, parseBlob, randomNonce, randomSalt, serializeBlob, signedDidDocument, zeroize, } from "@aithos/protocol-client";
24
24
  import { custodialResendVerify, custodialResetFinalize, custodialResetRequest, custodialSignIn, custodialSignUp, custodialVerifyEmail, loginChallenge, loginVerify, putBlob, registerAccount, } from "./auth-api.js";
25
25
  import { defaultSessionStore, } from "./session-store.js";
26
26
  import { defaultKeyStore, } from "./key-store.js";
@@ -178,6 +178,63 @@ export class AithosAuth {
178
178
  return this.#delegates.findForSubject(did);
179
179
  }
180
180
  /* ------------------------------------------------------------------------ */
181
+ /* Unified email + password — signInAuto (zk / custodial dispatch) */
182
+ /* ------------------------------------------------------------------------ */
183
+ /**
184
+ * Sign in with email + password, dispatching automatically between
185
+ * the legacy zero-knowledge flow ({@link signIn}) and the custodial
186
+ * flow ({@link signInCustodial}) based on which mode the account
187
+ * was provisioned with.
188
+ *
189
+ * Use this in apps that want a single sign-in form for users who
190
+ * may have been created under either mode (e.g. an app that's
191
+ * migrating from zk to custodial — pre-existing users stay zk
192
+ * forever, new ones go custodial, the SDK figures it out).
193
+ *
194
+ * Strategy: try {@link signInCustodial} first (the modern path).
195
+ * If the backend reports `auth_invalid_credentials` — which it
196
+ * uniformly returns for "wrong password", "unknown user", AND
197
+ * "user exists but not in custodial mode" (anti-enum) — fall
198
+ * back to {@link signIn} (zk).
199
+ *
200
+ * Other failure modes from the custodial path are NOT swallowed:
201
+ * - `auth_email_not_verified` → propagate (user is custodial but
202
+ * hasn't clicked the confirmation link yet; the app should
203
+ * surface a "resend mail" CTA rather than retrying as zk,
204
+ * which would also fail and mask the real cause)
205
+ * - server / network errors → propagate (don't double the
206
+ * incident by retrying through the other flow)
207
+ *
208
+ * Latency profile:
209
+ * - Pure custodial (success or wrong pwd) : 1 round-trip
210
+ * - Pure zk (any outcome) : 1 custodial probe + 2 zk
211
+ * - Unknown email : same as zk worst case
212
+ *
213
+ * Anti-enum note: timing slightly leaks the mode (custodial path is
214
+ * faster than zk). Acceptable for V1 — rate limiting + strong
215
+ * passwords are the real defenses. A future strict-anti-enum mode
216
+ * could race both paths in parallel and accept the 2x backend load.
217
+ */
218
+ async signInAuto(input) {
219
+ if (!input.email || !input.password) {
220
+ throw new AithosSDKError("auth_invalid_input", "signInAuto: email and password are required");
221
+ }
222
+ try {
223
+ const r = await this.signInCustodial(input);
224
+ return r.session;
225
+ }
226
+ catch (e) {
227
+ // Only fall back on the specific anti-enum sentinel — preserve
228
+ // other error codes (notably auth_email_not_verified) so the
229
+ // caller can surface the right UI hint.
230
+ if (e instanceof AithosSDKError &&
231
+ e.code === "auth_invalid_credentials") {
232
+ return await this.signIn(input);
233
+ }
234
+ throw e;
235
+ }
236
+ }
237
+ /* ------------------------------------------------------------------------ */
181
238
  /* Email + password — signIn */
182
239
  /* ------------------------------------------------------------------------ */
183
240
  async signIn(input) {
@@ -883,6 +940,18 @@ export class AithosAuth {
883
940
  zeroize(seedCircle);
884
941
  zeroize(seedSelf);
885
942
  zeroize(resp.encKey);
943
+ // Bootstrap the Ethos on api.aithos.be (cf. notes in signInCustodial).
944
+ // The magic-link flow is the FIRST time the user actually has
945
+ // hydrated keys client-side, so this is typically when the identity
946
+ // gets published. Idempotent — safe to call again on subsequent
947
+ // clicks (which won't get here normally, but defensively).
948
+ const identity = browserIdentityFromStored({
949
+ handle: stored.handle,
950
+ displayName: stored.displayName,
951
+ did: stored.did,
952
+ seeds: stored.seedsHex,
953
+ });
954
+ await this.#publishIdentity(identity);
886
955
  if (this.#ownerSigners)
887
956
  this.#ownerSigners.destroy();
888
957
  this.#ownerSigners = OwnerSigners.fromStoredOwnerKeys(stored);
@@ -989,6 +1058,24 @@ export class AithosAuth {
989
1058
  // The enc_key is informational here — the custodial blob is empty
990
1059
  // at first login. We still don't keep it in memory.
991
1060
  zeroize(resp.encKey);
1061
+ // Bootstrap the Ethos on api.aithos.be — same as signUp(zk). Without
1062
+ // this, the DID returned by signInCustodial isn't resolvable on the
1063
+ // platform (feed / profile lookups return "not found: did …"). The
1064
+ // call is idempotent server-side: a published identity replays as a
1065
+ // no-op. We do it here (rather than only on a "first login" flag)
1066
+ // because the auth Lambda doesn't know whether the api.aithos.be
1067
+ // side has been populated — the SDK is the single source of truth
1068
+ // for "the user's Ethos is bootstrapped".
1069
+ //
1070
+ // Failure aborts the sign-in: the user can retry (same behaviour as
1071
+ // signUp(zk)), and the local keystore is NOT populated half-way.
1072
+ const identity = browserIdentityFromStored({
1073
+ handle: stored.handle,
1074
+ displayName: stored.displayName,
1075
+ did: stored.did,
1076
+ seeds: stored.seedsHex,
1077
+ });
1078
+ await this.#publishIdentity(identity);
992
1079
  // Hydrate in-memory owner signers from the freshly-stored material.
993
1080
  if (this.#ownerSigners)
994
1081
  this.#ownerSigners.destroy();
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.1.0-alpha.32";
1
+ export declare const VERSION = "0.1.0-alpha.34";
2
2
  export { AithosSDK } from "./sdk.js";
3
3
  export type { AithosSDKConfig } from "./types.js";
4
4
  export { AithosSDKError } from "./types.js";
package/dist/src/index.js CHANGED
@@ -17,7 +17,7 @@
17
17
  // Public types specific to the SDK (`AithosSDKConfig`, `AithosSDKError`)
18
18
  // are exported from here. Endpoint config (`AithosSdkEndpoints`,
19
19
  // `DEFAULT_SDK_ENDPOINTS`) likewise.
20
- export const VERSION = "0.1.0-alpha.32";
20
+ export const VERSION = "0.1.0-alpha.34";
21
21
  export { AithosSDK } from "./sdk.js";
22
22
  export { AithosSDKError } from "./types.js";
23
23
  // Re-export protocol-client's JSON-RPC error type so consumers can
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aithos/sdk",
3
- "version": "0.1.0-alpha.32",
4
- "description": "Aithos SDK \u2014 high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
3
+ "version": "0.1.0-alpha.34",
4
+ "description": "Aithos SDK high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
5
5
  "keywords": [
6
6
  "aithos",
7
7
  "sdk",
@@ -39,15 +39,6 @@
39
39
  "README.md",
40
40
  "LICENSE"
41
41
  ],
42
- "scripts": {
43
- "build": "tsc",
44
- "build:test": "tsc -p tsconfig.test.json",
45
- "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
46
- "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
47
- "test:watch": "cd dist && node --test --watch",
48
- "clean": "rm -rf dist",
49
- "prepublishOnly": "npm run clean && npm run build && npm test"
50
- },
51
42
  "engines": {
52
43
  "node": ">=20"
53
44
  },
@@ -63,5 +54,13 @@
63
54
  "publishConfig": {
64
55
  "access": "public",
65
56
  "tag": "alpha"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc",
60
+ "build:test": "tsc -p tsconfig.test.json",
61
+ "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
62
+ "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
63
+ "test:watch": "cd dist && node --test --watch",
64
+ "clean": "rm -rf dist"
66
65
  }
67
- }
66
+ }