@aithos/sdk 0.1.0-alpha.30 → 0.1.0-alpha.32
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/dist/src/auth-api.d.ts +81 -7
- package/dist/src/auth-api.js +79 -7
- package/dist/src/auth.d.ts +130 -24
- package/dist/src/auth.js +138 -14
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/package.json +1 -1
package/dist/src/auth-api.d.ts
CHANGED
|
@@ -46,27 +46,101 @@ export interface LoginVerifyResponse {
|
|
|
46
46
|
readonly blobVersion: number;
|
|
47
47
|
}
|
|
48
48
|
export declare function loginVerify(http: HttpClient, email: string, authKey: Uint8Array): Promise<LoginVerifyResponse>;
|
|
49
|
+
/**
|
|
50
|
+
* Input for {@link custodialSignUp}. Caller authenticates the app via ONE
|
|
51
|
+
* of `apiKey` (server-only secret) or `publicKey` (browser-safe). The
|
|
52
|
+
* user's `password` is always required — sign-up no longer auto-generates
|
|
53
|
+
* one server-side. The created account starts in a *pending* state and
|
|
54
|
+
* the user must click the link sent to their inbox before they can
|
|
55
|
+
* sign in.
|
|
56
|
+
*/
|
|
49
57
|
export interface CustodialSignUpApiInput {
|
|
50
|
-
|
|
58
|
+
/** Server-only Bearer secret: `aithos_<env>_<…>`. Mutually exclusive
|
|
59
|
+
* with `publicKey`. Use this from your backend. */
|
|
60
|
+
readonly apiKey?: string;
|
|
61
|
+
/** Browser-safe public client key: `pk_<env>_<…>`. Mutually exclusive
|
|
62
|
+
* with `apiKey`. The browser sends its `Origin` header alongside; the
|
|
63
|
+
* Aithos backend matches it against the app's allowed_origins list. */
|
|
64
|
+
readonly publicKey?: string;
|
|
51
65
|
readonly email: string;
|
|
66
|
+
/** Raw password the user chose. Must be ≥ 10 chars and mix letters
|
|
67
|
+
* with at least one digit or symbol (server-side rule). */
|
|
68
|
+
readonly password: string;
|
|
52
69
|
readonly displayName?: string;
|
|
53
70
|
readonly handleHint?: string;
|
|
54
71
|
}
|
|
55
72
|
export interface CustodialSignUpApiResponse {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
readonly
|
|
73
|
+
/** Always "pending_verification" — sign-in is blocked until the
|
|
74
|
+
* user clicks the confirmation link. */
|
|
75
|
+
readonly status: "pending_verification";
|
|
59
76
|
readonly email: string;
|
|
60
77
|
readonly mailSent: boolean;
|
|
61
78
|
readonly mailMessageId?: string;
|
|
62
79
|
}
|
|
63
80
|
/**
|
|
64
81
|
* Provision a custodial-mode account on behalf of a registered app.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
82
|
+
*
|
|
83
|
+
* Two integration paths:
|
|
84
|
+
* - **Backend** caller passes `apiKey` (server-only secret).
|
|
85
|
+
* - **Browser** caller passes `publicKey` (safe to ship in the bundle).
|
|
86
|
+
* The browser also sends its `Origin` header automatically and the
|
|
87
|
+
* auth backend validates it against the app's allowed_origins list.
|
|
88
|
+
*
|
|
89
|
+
* On success the account exists in DDB with `email_verified: false` and
|
|
90
|
+
* the response carries `status: "pending_verification"` — call
|
|
91
|
+
* {@link custodialVerifyEmail} after the user clicks the confirmation
|
|
92
|
+
* link before attempting sign-in.
|
|
68
93
|
*/
|
|
69
94
|
export declare function custodialSignUp(http: HttpClient, input: CustodialSignUpApiInput): Promise<CustodialSignUpApiResponse>;
|
|
95
|
+
export interface CustodialVerifyEmailApiInput {
|
|
96
|
+
readonly email: string;
|
|
97
|
+
readonly token: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Result of consuming the verification link. Magic-link mode: a
|
|
101
|
+
* successful first-time consumption returns a full session payload
|
|
102
|
+
* (JWT + seeds) so the caller can sign the user in without prompting
|
|
103
|
+
* for the password. Replays of the same link (after first consumption)
|
|
104
|
+
* land on `status: "already_verified"` — the user is already verified
|
|
105
|
+
* and must use the regular sign-in flow from now on.
|
|
106
|
+
*
|
|
107
|
+
* The discriminator is the `status` field. Callers should pattern-match.
|
|
108
|
+
*/
|
|
109
|
+
export type CustodialVerifyEmailApiResponse = {
|
|
110
|
+
readonly status: "signed_in";
|
|
111
|
+
readonly session: string;
|
|
112
|
+
readonly exp: number;
|
|
113
|
+
readonly did: string;
|
|
114
|
+
readonly handle: string;
|
|
115
|
+
readonly displayName: string;
|
|
116
|
+
readonly seed: Uint8Array;
|
|
117
|
+
readonly encKey: Uint8Array;
|
|
118
|
+
readonly blob: Uint8Array;
|
|
119
|
+
readonly blobNonce: Uint8Array;
|
|
120
|
+
readonly blobVersion: number;
|
|
121
|
+
} | {
|
|
122
|
+
readonly status: "already_verified";
|
|
123
|
+
readonly email: string;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Consume the verification token from the confirmation link. On a fresh
|
|
127
|
+
* click: returns a full session payload (magic-link auto-signin). On a
|
|
128
|
+
* replayed click of an already-consumed link: returns
|
|
129
|
+
* `{ status: "already_verified" }`.
|
|
130
|
+
*
|
|
131
|
+
* Throws `auth_token_invalid_or_expired` if the token is wrong, consumed,
|
|
132
|
+
* or past its TTL.
|
|
133
|
+
*/
|
|
134
|
+
export declare function custodialVerifyEmail(http: HttpClient, input: CustodialVerifyEmailApiInput): Promise<CustodialVerifyEmailApiResponse>;
|
|
135
|
+
/** Re-send the verification mail for a pending account. The backend
|
|
136
|
+
* is anti-enum (always 200) and rate-limited 1/h/account, so this is
|
|
137
|
+
* safe to call even when the user state is unknown. Accepts the same
|
|
138
|
+
* credential families as {@link custodialSignUp}. */
|
|
139
|
+
export declare function custodialResendVerify(http: HttpClient, args: {
|
|
140
|
+
readonly email: string;
|
|
141
|
+
readonly apiKey?: string;
|
|
142
|
+
readonly publicKey?: string;
|
|
143
|
+
}): Promise<void>;
|
|
70
144
|
export interface CustodialSignInApiInput {
|
|
71
145
|
readonly email: string;
|
|
72
146
|
readonly password: string;
|
package/dist/src/auth-api.js
CHANGED
|
@@ -101,19 +101,35 @@ export async function loginVerify(http, email, authKey) {
|
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* Provision a custodial-mode account on behalf of a registered app.
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
104
|
+
*
|
|
105
|
+
* Two integration paths:
|
|
106
|
+
* - **Backend** caller passes `apiKey` (server-only secret).
|
|
107
|
+
* - **Browser** caller passes `publicKey` (safe to ship in the bundle).
|
|
108
|
+
* The browser also sends its `Origin` header automatically and the
|
|
109
|
+
* auth backend validates it against the app's allowed_origins list.
|
|
110
|
+
*
|
|
111
|
+
* On success the account exists in DDB with `email_verified: false` and
|
|
112
|
+
* the response carries `status: "pending_verification"` — call
|
|
113
|
+
* {@link custodialVerifyEmail} after the user clicks the confirmation
|
|
114
|
+
* link before attempting sign-in.
|
|
107
115
|
*/
|
|
108
116
|
export async function custodialSignUp(http, input) {
|
|
117
|
+
if (!input.apiKey && !input.publicKey) {
|
|
118
|
+
throw new AithosSDKError("auth_missing_api_key", "signUpCustodial requires either apiKey or publicKey");
|
|
119
|
+
}
|
|
120
|
+
if (input.apiKey && input.publicKey) {
|
|
121
|
+
throw new AithosSDKError("auth_invalid_input", "signUpCustodial: pass exactly one of apiKey or publicKey, not both");
|
|
122
|
+
}
|
|
123
|
+
const bearer = (input.apiKey ?? input.publicKey);
|
|
109
124
|
const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/sign-up`, {
|
|
110
125
|
method: "POST",
|
|
111
126
|
headers: {
|
|
112
127
|
"content-type": "application/json",
|
|
113
|
-
authorization: `Bearer ${
|
|
128
|
+
authorization: `Bearer ${bearer}`,
|
|
114
129
|
},
|
|
115
130
|
body: JSON.stringify({
|
|
116
131
|
email: input.email,
|
|
132
|
+
password: input.password,
|
|
117
133
|
...(input.displayName ? { display_name: input.displayName } : {}),
|
|
118
134
|
...(input.handleHint ? { handle_hint: input.handleHint } : {}),
|
|
119
135
|
}),
|
|
@@ -122,9 +138,7 @@ export async function custodialSignUp(http, input) {
|
|
|
122
138
|
throw await readError(res, "custodial_signup_failed");
|
|
123
139
|
const wire = (await res.json());
|
|
124
140
|
return {
|
|
125
|
-
|
|
126
|
-
did: wire.did,
|
|
127
|
-
handle: wire.handle,
|
|
141
|
+
status: "pending_verification",
|
|
128
142
|
email: wire.email,
|
|
129
143
|
mailSent: wire.mail_sent,
|
|
130
144
|
...(wire.mail_message_id !== undefined
|
|
@@ -132,6 +146,64 @@ export async function custodialSignUp(http, input) {
|
|
|
132
146
|
: {}),
|
|
133
147
|
};
|
|
134
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Consume the verification token from the confirmation link. On a fresh
|
|
151
|
+
* click: returns a full session payload (magic-link auto-signin). On a
|
|
152
|
+
* replayed click of an already-consumed link: returns
|
|
153
|
+
* `{ status: "already_verified" }`.
|
|
154
|
+
*
|
|
155
|
+
* Throws `auth_token_invalid_or_expired` if the token is wrong, consumed,
|
|
156
|
+
* or past its TTL.
|
|
157
|
+
*/
|
|
158
|
+
export async function custodialVerifyEmail(http, input) {
|
|
159
|
+
const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/verify`, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: { "content-type": "application/json" },
|
|
162
|
+
body: JSON.stringify({ email: input.email, token: input.token }),
|
|
163
|
+
});
|
|
164
|
+
if (!res.ok)
|
|
165
|
+
throw await readError(res, "custodial_verify_failed");
|
|
166
|
+
const wire = (await res.json());
|
|
167
|
+
if (wire.status === "already_verified") {
|
|
168
|
+
return { status: "already_verified", email: wire.email };
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
status: "signed_in",
|
|
172
|
+
session: wire.session,
|
|
173
|
+
exp: wire.exp,
|
|
174
|
+
did: wire.did,
|
|
175
|
+
handle: wire.handle,
|
|
176
|
+
displayName: wire.display_name,
|
|
177
|
+
seed: b64ToBytes(wire.seed_b64),
|
|
178
|
+
encKey: b64ToBytes(wire.enc_key_b64),
|
|
179
|
+
blob: wire.blob_b64 ? b64ToBytes(wire.blob_b64) : new Uint8Array(0),
|
|
180
|
+
blobNonce: wire.blob_nonce_b64
|
|
181
|
+
? b64ToBytes(wire.blob_nonce_b64)
|
|
182
|
+
: new Uint8Array(0),
|
|
183
|
+
blobVersion: wire.blob_version,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/* ---- POST /auth/custodial/verify/resend -------------------------------- */
|
|
187
|
+
/** Re-send the verification mail for a pending account. The backend
|
|
188
|
+
* is anti-enum (always 200) and rate-limited 1/h/account, so this is
|
|
189
|
+
* safe to call even when the user state is unknown. Accepts the same
|
|
190
|
+
* credential families as {@link custodialSignUp}. */
|
|
191
|
+
export async function custodialResendVerify(http, args) {
|
|
192
|
+
if (!args.apiKey && !args.publicKey) {
|
|
193
|
+
throw new AithosSDKError("auth_missing_api_key", "resendVerificationEmail requires either apiKey or publicKey");
|
|
194
|
+
}
|
|
195
|
+
const bearer = (args.apiKey ?? args.publicKey);
|
|
196
|
+
const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/verify/resend`, {
|
|
197
|
+
method: "POST",
|
|
198
|
+
headers: {
|
|
199
|
+
"content-type": "application/json",
|
|
200
|
+
authorization: `Bearer ${bearer}`,
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({ email: args.email }),
|
|
203
|
+
});
|
|
204
|
+
if (!res.ok)
|
|
205
|
+
throw await readError(res, "custodial_resend_failed");
|
|
206
|
+
}
|
|
135
207
|
export async function custodialSignIn(http, input) {
|
|
136
208
|
const wire = await postJson(http, "/auth/custodial/sign-in", { email: input.email, password: input.password });
|
|
137
209
|
return {
|
package/dist/src/auth.d.ts
CHANGED
|
@@ -22,6 +22,22 @@ export interface AithosAuthConfig {
|
|
|
22
22
|
readonly sessionStore?: AithosSessionStore;
|
|
23
23
|
/** Pluggable key persistence. Defaults to {@link defaultKeyStore}. */
|
|
24
24
|
readonly keyStore?: AithosKeyStore;
|
|
25
|
+
/**
|
|
26
|
+
* Public client key issued by Aithos for browser callers
|
|
27
|
+
* (`pk_<env>_<…>`). When set, the custodial endpoints (`signUpCustodial`,
|
|
28
|
+
* `verifyEmail`, `resendVerificationEmail`) authenticate as this app
|
|
29
|
+
* by default — the caller no longer has to repeat the key on every
|
|
30
|
+
* call. The corresponding `allowed_origins` allowlist must include the
|
|
31
|
+
* current page's origin.
|
|
32
|
+
*
|
|
33
|
+
* Safe to ship in the browser bundle: it grants nothing beyond what
|
|
34
|
+
* a visitor of the app can already do, is gated by Origin on every
|
|
35
|
+
* call, and is rate-limited per IP by the backend.
|
|
36
|
+
*
|
|
37
|
+
* If your app authenticates with a SECRET Bearer API key from a
|
|
38
|
+
* backend instead, leave this unset and pass `apiKey` per call.
|
|
39
|
+
*/
|
|
40
|
+
readonly publicKey?: string;
|
|
25
41
|
}
|
|
26
42
|
/**
|
|
27
43
|
* Active Aithos session. Returned by JWT-backed entry points
|
|
@@ -136,36 +152,85 @@ export interface ImportMandateInput {
|
|
|
136
152
|
readonly bundle: Blob | string;
|
|
137
153
|
}
|
|
138
154
|
/**
|
|
139
|
-
* Input to {@link AithosAuth.signUpCustodial}.
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
155
|
+
* Input to {@link AithosAuth.signUpCustodial}.
|
|
156
|
+
*
|
|
157
|
+
* The caller authenticates as an app via ONE of:
|
|
158
|
+
* - `apiKey` : server-only secret Bearer. Pass from your backend
|
|
159
|
+
* only — never ship it in browser code.
|
|
160
|
+
* - `publicKey` : browser-safe public client key. Safe to embed in
|
|
161
|
+
* the bundle; the backend gates it by Origin and
|
|
162
|
+
* rate-limits by IP.
|
|
163
|
+
*
|
|
164
|
+
* If you set `publicKey` on the {@link AithosAuth} constructor, omit
|
|
165
|
+
* both fields here — the default credential is used.
|
|
166
|
+
*
|
|
167
|
+
* The `password` is always required and is chosen by the user (sign-up
|
|
168
|
+
* no longer auto-generates one). The created account starts as
|
|
169
|
+
* **pending** (`email_verified=false`); the user must click the
|
|
170
|
+
* confirmation link sent by SES before {@link signInCustodial} works.
|
|
143
171
|
*/
|
|
144
172
|
export interface CustodialSignUpInput {
|
|
145
|
-
/** Bearer
|
|
146
|
-
readonly apiKey
|
|
147
|
-
/**
|
|
173
|
+
/** Server-only Bearer secret. Mutually exclusive with `publicKey`. */
|
|
174
|
+
readonly apiKey?: string;
|
|
175
|
+
/** Browser-safe public client key. Mutually exclusive with `apiKey`.
|
|
176
|
+
* Overrides the constructor's default `publicKey` when provided. */
|
|
177
|
+
readonly publicKey?: string;
|
|
178
|
+
/** Email address of the new user. Will receive the verification mail. */
|
|
148
179
|
readonly email: string;
|
|
180
|
+
/** Raw password the user chose. ≥ 10 chars, mix of letters with
|
|
181
|
+
* ≥ 1 digit or symbol. Enforced server-side. */
|
|
182
|
+
readonly password: string;
|
|
149
183
|
/** Optional display name. Capped at 200 chars by the backend. */
|
|
150
184
|
readonly displayName?: string;
|
|
151
185
|
/** Optional handle hint. Backend may sanitise or replace. */
|
|
152
186
|
readonly handleHint?: string;
|
|
153
187
|
}
|
|
154
188
|
/**
|
|
155
|
-
* Result of {@link AithosAuth.signUpCustodial}.
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
189
|
+
* Result of {@link AithosAuth.signUpCustodial}. Always carries
|
|
190
|
+
* `status: "pending_verification"` — the user must click the link in
|
|
191
|
+
* their inbox before sign-in works. If `mailSent` is false the row
|
|
192
|
+
* exists in DDB anyway; trigger {@link AithosAuth.resendVerificationEmail}
|
|
193
|
+
* to retry the SES send.
|
|
160
194
|
*/
|
|
161
195
|
export interface CustodialSignUpResult {
|
|
162
|
-
readonly
|
|
163
|
-
readonly did: string;
|
|
164
|
-
readonly handle: string;
|
|
196
|
+
readonly status: "pending_verification";
|
|
165
197
|
readonly email: string;
|
|
166
198
|
readonly mailSent: boolean;
|
|
167
199
|
readonly mailMessageId?: string;
|
|
168
200
|
}
|
|
201
|
+
/** Input to {@link AithosAuth.verifyEmail}. Both fields come straight
|
|
202
|
+
* out of the `?email=&token=` query string of the confirmation URL. */
|
|
203
|
+
export interface VerifyEmailInput {
|
|
204
|
+
readonly email: string;
|
|
205
|
+
readonly token: string;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Result of {@link AithosAuth.verifyEmail}. Discriminated by `status`.
|
|
209
|
+
*
|
|
210
|
+
* - `"signed_in"` (magic-link mode): the user has been authenticated
|
|
211
|
+
* in this call. A JWT session is persisted to the session store and
|
|
212
|
+
* the local keystore is hydrated with the unwrapped seed bundle.
|
|
213
|
+
* The caller can navigate the user straight to a logged-in area.
|
|
214
|
+
* - `"already_verified"`: the verification link had already been
|
|
215
|
+
* consumed on a previous click. No session is minted (the token is
|
|
216
|
+
* spent). The caller should route the user to the sign-in form.
|
|
217
|
+
*/
|
|
218
|
+
export type VerifyEmailResult = {
|
|
219
|
+
readonly status: "signed_in";
|
|
220
|
+
readonly session: AithosSession;
|
|
221
|
+
readonly passwordMustChange: false;
|
|
222
|
+
} | {
|
|
223
|
+
readonly status: "already_verified";
|
|
224
|
+
readonly email: string;
|
|
225
|
+
};
|
|
226
|
+
/** Input to {@link AithosAuth.resendVerificationEmail}. The `email` is
|
|
227
|
+
* required; credential overrides follow the same rules as
|
|
228
|
+
* {@link CustodialSignUpInput}. */
|
|
229
|
+
export interface ResendVerificationInput {
|
|
230
|
+
readonly email: string;
|
|
231
|
+
readonly apiKey?: string;
|
|
232
|
+
readonly publicKey?: string;
|
|
233
|
+
}
|
|
169
234
|
export interface CustodialSignInInput {
|
|
170
235
|
readonly email: string;
|
|
171
236
|
readonly password: string;
|
|
@@ -330,25 +395,66 @@ export declare class AithosAuth {
|
|
|
330
395
|
/**
|
|
331
396
|
* Provision a custodial-mode account on behalf of a registered app.
|
|
332
397
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
398
|
+
* Two integration patterns:
|
|
399
|
+
* - **Frontend-only** apps : set `publicKey` on the constructor
|
|
400
|
+
* (or on this call). Safe to ship in browser bundles — the
|
|
401
|
+
* backend gates each request by Origin + IP rate limit.
|
|
402
|
+
* - **Backend-fronted** apps : the backend passes `apiKey` (secret
|
|
403
|
+
* Bearer); the browser never sees the credential.
|
|
336
404
|
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
405
|
+
* The created account is in a *pending* state — sign-in stays blocked
|
|
406
|
+
* until the user clicks the confirmation link sent to their inbox.
|
|
407
|
+
* Call {@link verifyEmail} from the page mounted on
|
|
408
|
+
* `app.verify_base_url` to consume the token; afterwards
|
|
409
|
+
* {@link signInCustodial} works.
|
|
341
410
|
*
|
|
342
411
|
* Errors map to `AithosSDKError` codes:
|
|
343
|
-
* - `auth_missing_api_key` (
|
|
412
|
+
* - `auth_missing_api_key` (no credential provided)
|
|
344
413
|
* - `auth_invalid_api_key` (Bearer rejected by backend)
|
|
345
|
-
* - `
|
|
414
|
+
* - `auth_invalid_public_key` (public key rejected by backend)
|
|
415
|
+
* - `auth_api_key_revoked` / `auth_public_key_revoked`
|
|
416
|
+
* - `auth_origin_not_allowed` (public key + Origin not in allowlist)
|
|
417
|
+
* - `auth_password_too_weak` (400 — server-side strength check)
|
|
346
418
|
* - `auth_email_exists` (409 — email already registered)
|
|
347
419
|
* - `auth_email_invalid` (400 — bad email format)
|
|
348
420
|
* - `auth_mail_send_failed` (502 — DDB row exists but SES failed)
|
|
349
421
|
* - `auth_custodial_signup_failed` (catch-all)
|
|
350
422
|
*/
|
|
351
423
|
signUpCustodial(input: CustodialSignUpInput): Promise<CustodialSignUpResult>;
|
|
424
|
+
/**
|
|
425
|
+
* Magic-link auto-signin: consume the verification token from the
|
|
426
|
+
* confirmation link, KMS-unwrap the seed bundle server-side, and
|
|
427
|
+
* hydrate the local session + keystore in one round-trip.
|
|
428
|
+
*
|
|
429
|
+
* Outcome depends on the link's state:
|
|
430
|
+
* - First click on a fresh link → returns
|
|
431
|
+
* `{ status: "signed_in", session, … }`. The session store is
|
|
432
|
+
* populated, the owner signers are loaded — the user is signed
|
|
433
|
+
* in. The caller should navigate them to a logged-in route.
|
|
434
|
+
* - Click of an already-consumed link → returns
|
|
435
|
+
* `{ status: "already_verified", email }`. No session is minted;
|
|
436
|
+
* the user must sign in via {@link signInCustodial}.
|
|
437
|
+
*
|
|
438
|
+
* Mount this on the page declared as `verify_base_url` in your app's
|
|
439
|
+
* registration. Read `email` + `token` from `window.location.search`,
|
|
440
|
+
* call this, branch on `result.status`.
|
|
441
|
+
*
|
|
442
|
+
* Throws `auth_token_invalid_or_expired` if the token is wrong or
|
|
443
|
+
* past its 1h TTL — surface a "request a fresh link" CTA in that case.
|
|
444
|
+
*/
|
|
445
|
+
verifyEmail(input: VerifyEmailInput): Promise<VerifyEmailResult>;
|
|
446
|
+
/**
|
|
447
|
+
* Re-send the verification mail for a pending account. Use when the
|
|
448
|
+
* user reports never having received the welcome mail, or when their
|
|
449
|
+
* verification token expired (24h TTL).
|
|
450
|
+
*
|
|
451
|
+
* The backend is anti-enumeration (always 200) and rate-limited
|
|
452
|
+
* 1/h/account, so it's safe to call even when the state of `email`
|
|
453
|
+
* is unknown. Accepts the same credential families as
|
|
454
|
+
* {@link signUpCustodial}; falls back to the constructor's
|
|
455
|
+
* `publicKey` when neither override is set.
|
|
456
|
+
*/
|
|
457
|
+
resendVerificationEmail(input: ResendVerificationInput): Promise<void>;
|
|
352
458
|
/**
|
|
353
459
|
* Authenticate a custodial-mode user with email + password. Single
|
|
354
460
|
* round-trip: returns a fresh JWT session AND hydrates the local
|
package/dist/src/auth.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// keyStore is the source of truth for "is the user signed in", the
|
|
22
22
|
// JWT is auxiliary for compute/wallet.
|
|
23
23
|
import { buildBlobPlaintext, buildSignedEnvelope, createBrowserIdentity, decryptBlob, DEFAULT_KDF, deriveAuthAndEncKeys, encryptBlob, parseBlob, randomNonce, randomSalt, serializeBlob, signedDidDocument, zeroize, } from "@aithos/protocol-client";
|
|
24
|
-
import { custodialResetFinalize, custodialResetRequest, custodialSignIn, custodialSignUp, loginChallenge, loginVerify, putBlob, registerAccount, } from "./auth-api.js";
|
|
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";
|
|
27
27
|
import { parseDelegateBundle, readDelegateBundleText, } from "./internal/delegate-bundle.js";
|
|
@@ -43,6 +43,7 @@ export class AithosAuth {
|
|
|
43
43
|
#win;
|
|
44
44
|
#sessionStore;
|
|
45
45
|
#keyStore;
|
|
46
|
+
#publicKey;
|
|
46
47
|
/** In-memory owner signers — populated after sign-in or `resume`. */
|
|
47
48
|
#ownerSigners = null;
|
|
48
49
|
/** Active delegate registry. */
|
|
@@ -66,6 +67,7 @@ export class AithosAuth {
|
|
|
66
67
|
(typeof window !== "undefined" ? window : undefined);
|
|
67
68
|
this.#sessionStore = config.sessionStore ?? defaultSessionStore();
|
|
68
69
|
this.#keyStore = config.keyStore ?? defaultKeyStore();
|
|
70
|
+
this.#publicKey = config.publicKey;
|
|
69
71
|
}
|
|
70
72
|
/* ------------------------------------------------------------------------ */
|
|
71
73
|
/* Boot-time hydration */
|
|
@@ -776,32 +778,154 @@ export class AithosAuth {
|
|
|
776
778
|
/**
|
|
777
779
|
* Provision a custodial-mode account on behalf of a registered app.
|
|
778
780
|
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
781
|
+
* Two integration patterns:
|
|
782
|
+
* - **Frontend-only** apps : set `publicKey` on the constructor
|
|
783
|
+
* (or on this call). Safe to ship in browser bundles — the
|
|
784
|
+
* backend gates each request by Origin + IP rate limit.
|
|
785
|
+
* - **Backend-fronted** apps : the backend passes `apiKey` (secret
|
|
786
|
+
* Bearer); the browser never sees the credential.
|
|
782
787
|
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
*
|
|
788
|
+
* The created account is in a *pending* state — sign-in stays blocked
|
|
789
|
+
* until the user clicks the confirmation link sent to their inbox.
|
|
790
|
+
* Call {@link verifyEmail} from the page mounted on
|
|
791
|
+
* `app.verify_base_url` to consume the token; afterwards
|
|
792
|
+
* {@link signInCustodial} works.
|
|
787
793
|
*
|
|
788
794
|
* Errors map to `AithosSDKError` codes:
|
|
789
|
-
* - `auth_missing_api_key` (
|
|
795
|
+
* - `auth_missing_api_key` (no credential provided)
|
|
790
796
|
* - `auth_invalid_api_key` (Bearer rejected by backend)
|
|
791
|
-
* - `
|
|
797
|
+
* - `auth_invalid_public_key` (public key rejected by backend)
|
|
798
|
+
* - `auth_api_key_revoked` / `auth_public_key_revoked`
|
|
799
|
+
* - `auth_origin_not_allowed` (public key + Origin not in allowlist)
|
|
800
|
+
* - `auth_password_too_weak` (400 — server-side strength check)
|
|
792
801
|
* - `auth_email_exists` (409 — email already registered)
|
|
793
802
|
* - `auth_email_invalid` (400 — bad email format)
|
|
794
803
|
* - `auth_mail_send_failed` (502 — DDB row exists but SES failed)
|
|
795
804
|
* - `auth_custodial_signup_failed` (catch-all)
|
|
796
805
|
*/
|
|
797
806
|
async signUpCustodial(input) {
|
|
798
|
-
if (!input.apiKey) {
|
|
799
|
-
throw new AithosSDKError("auth_missing_api_key", "signUpCustodial: apiKey is required");
|
|
800
|
-
}
|
|
801
807
|
if (!input.email) {
|
|
802
808
|
throw new AithosSDKError("auth_invalid_input", "signUpCustodial: email is required");
|
|
803
809
|
}
|
|
804
|
-
|
|
810
|
+
if (!input.password) {
|
|
811
|
+
throw new AithosSDKError("auth_invalid_input", "signUpCustodial: password is required");
|
|
812
|
+
}
|
|
813
|
+
const apiKey = input.apiKey;
|
|
814
|
+
const publicKey = input.publicKey ?? this.#publicKey;
|
|
815
|
+
if (!apiKey && !publicKey) {
|
|
816
|
+
throw new AithosSDKError("auth_missing_api_key", "signUpCustodial: pass apiKey, or publicKey, or set publicKey on the AithosAuth constructor");
|
|
817
|
+
}
|
|
818
|
+
return custodialSignUp({ fetchImpl: this.#fetchImpl, authBaseUrl: this.authBaseUrl }, {
|
|
819
|
+
email: input.email,
|
|
820
|
+
password: input.password,
|
|
821
|
+
...(apiKey ? { apiKey } : {}),
|
|
822
|
+
...(apiKey ? {} : publicKey ? { publicKey } : {}),
|
|
823
|
+
...(input.displayName ? { displayName: input.displayName } : {}),
|
|
824
|
+
...(input.handleHint ? { handleHint: input.handleHint } : {}),
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Magic-link auto-signin: consume the verification token from the
|
|
829
|
+
* confirmation link, KMS-unwrap the seed bundle server-side, and
|
|
830
|
+
* hydrate the local session + keystore in one round-trip.
|
|
831
|
+
*
|
|
832
|
+
* Outcome depends on the link's state:
|
|
833
|
+
* - First click on a fresh link → returns
|
|
834
|
+
* `{ status: "signed_in", session, … }`. The session store is
|
|
835
|
+
* populated, the owner signers are loaded — the user is signed
|
|
836
|
+
* in. The caller should navigate them to a logged-in route.
|
|
837
|
+
* - Click of an already-consumed link → returns
|
|
838
|
+
* `{ status: "already_verified", email }`. No session is minted;
|
|
839
|
+
* the user must sign in via {@link signInCustodial}.
|
|
840
|
+
*
|
|
841
|
+
* Mount this on the page declared as `verify_base_url` in your app's
|
|
842
|
+
* registration. Read `email` + `token` from `window.location.search`,
|
|
843
|
+
* call this, branch on `result.status`.
|
|
844
|
+
*
|
|
845
|
+
* Throws `auth_token_invalid_or_expired` if the token is wrong or
|
|
846
|
+
* past its 1h TTL — surface a "request a fresh link" CTA in that case.
|
|
847
|
+
*/
|
|
848
|
+
async verifyEmail(input) {
|
|
849
|
+
if (!input.email || !input.token) {
|
|
850
|
+
throw new AithosSDKError("auth_invalid_input", "verifyEmail: email and token are required");
|
|
851
|
+
}
|
|
852
|
+
const resp = await custodialVerifyEmail({ fetchImpl: this.#fetchImpl, authBaseUrl: this.authBaseUrl }, input);
|
|
853
|
+
if (resp.status === "already_verified") {
|
|
854
|
+
return { status: "already_verified", email: resp.email };
|
|
855
|
+
}
|
|
856
|
+
// Magic-link sign-in path. Mirror `signInCustodial` to materialise
|
|
857
|
+
// the 4 sphere seeds in the keystore.
|
|
858
|
+
if (resp.seed.byteLength !== 128) {
|
|
859
|
+
zeroize(resp.seed);
|
|
860
|
+
zeroize(resp.encKey);
|
|
861
|
+
throw new AithosSDKError("auth_custodial_seed_format", `verifyEmail: expected 128-byte seed bundle, got ${resp.seed.byteLength}`);
|
|
862
|
+
}
|
|
863
|
+
const seedRoot = resp.seed.slice(0, 32);
|
|
864
|
+
const seedPublic = resp.seed.slice(32, 64);
|
|
865
|
+
const seedCircle = resp.seed.slice(64, 96);
|
|
866
|
+
const seedSelf = resp.seed.slice(96, 128);
|
|
867
|
+
const stored = {
|
|
868
|
+
version: "0.1.0-hex",
|
|
869
|
+
did: resp.did,
|
|
870
|
+
handle: resp.handle,
|
|
871
|
+
displayName: resp.displayName,
|
|
872
|
+
seedsHex: {
|
|
873
|
+
root: bytesToHex(seedRoot),
|
|
874
|
+
public: bytesToHex(seedPublic),
|
|
875
|
+
circle: bytesToHex(seedCircle),
|
|
876
|
+
self: bytesToHex(seedSelf),
|
|
877
|
+
},
|
|
878
|
+
savedAt: new Date().toISOString(),
|
|
879
|
+
};
|
|
880
|
+
zeroize(resp.seed);
|
|
881
|
+
zeroize(seedRoot);
|
|
882
|
+
zeroize(seedPublic);
|
|
883
|
+
zeroize(seedCircle);
|
|
884
|
+
zeroize(seedSelf);
|
|
885
|
+
zeroize(resp.encKey);
|
|
886
|
+
if (this.#ownerSigners)
|
|
887
|
+
this.#ownerSigners.destroy();
|
|
888
|
+
this.#ownerSigners = OwnerSigners.fromStoredOwnerKeys(stored);
|
|
889
|
+
await this.#keyStore.saveOwner(stored);
|
|
890
|
+
const session = {
|
|
891
|
+
session: resp.session,
|
|
892
|
+
exp: resp.exp,
|
|
893
|
+
did: resp.did,
|
|
894
|
+
handle: resp.handle,
|
|
895
|
+
blob_b64: bytesToB64Public(resp.blob),
|
|
896
|
+
blob_nonce_b64: bytesToB64Public(resp.blobNonce),
|
|
897
|
+
blob_version: resp.blobVersion,
|
|
898
|
+
enc_key_b64: "",
|
|
899
|
+
is_first_login: false,
|
|
900
|
+
};
|
|
901
|
+
this.#sessionStore.set(session);
|
|
902
|
+
return { status: "signed_in", session, passwordMustChange: false };
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Re-send the verification mail for a pending account. Use when the
|
|
906
|
+
* user reports never having received the welcome mail, or when their
|
|
907
|
+
* verification token expired (24h TTL).
|
|
908
|
+
*
|
|
909
|
+
* The backend is anti-enumeration (always 200) and rate-limited
|
|
910
|
+
* 1/h/account, so it's safe to call even when the state of `email`
|
|
911
|
+
* is unknown. Accepts the same credential families as
|
|
912
|
+
* {@link signUpCustodial}; falls back to the constructor's
|
|
913
|
+
* `publicKey` when neither override is set.
|
|
914
|
+
*/
|
|
915
|
+
async resendVerificationEmail(input) {
|
|
916
|
+
if (!input.email) {
|
|
917
|
+
throw new AithosSDKError("auth_invalid_input", "resendVerificationEmail: email is required");
|
|
918
|
+
}
|
|
919
|
+
const apiKey = input.apiKey;
|
|
920
|
+
const publicKey = input.publicKey ?? this.#publicKey;
|
|
921
|
+
if (!apiKey && !publicKey) {
|
|
922
|
+
throw new AithosSDKError("auth_missing_api_key", "resendVerificationEmail: pass apiKey, publicKey, or set publicKey on the AithosAuth constructor");
|
|
923
|
+
}
|
|
924
|
+
await custodialResendVerify({ fetchImpl: this.#fetchImpl, authBaseUrl: this.authBaseUrl }, {
|
|
925
|
+
email: input.email,
|
|
926
|
+
...(apiKey ? { apiKey } : {}),
|
|
927
|
+
...(apiKey ? {} : publicKey ? { publicKey } : {}),
|
|
928
|
+
});
|
|
805
929
|
}
|
|
806
930
|
/**
|
|
807
931
|
* Authenticate a custodial-mode user with email + password. Single
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.0-alpha.
|
|
1
|
+
export declare const VERSION = "0.1.0-alpha.32";
|
|
2
2
|
export { AithosSDK } from "./sdk.js";
|
|
3
3
|
export type { AithosSDKConfig } from "./types.js";
|
|
4
4
|
export { AithosSDKError } from "./types.js";
|
|
@@ -12,7 +12,7 @@ export { WalletNamespace } from "./wallet.js";
|
|
|
12
12
|
export type { ComponentStyle, ExtractArgs, ExtractContent, ExtractData, ExtractForm, ExtractFormField, ExtractHeading, ExtractIconDeclaration, ExtractImage, ExtractLink, ExtractLogo, ExtractMeta, ExtractResult, ExtractSection, ExtractStructure, ExtractStyles, FetchAssetArgs, FetchAssetResult, PaletteEntry, VisualSignature, WebNamespaceDeps, } from "./web.js";
|
|
13
13
|
export { WebNamespace, WEB_EXTRACT_SCOPE } from "./web.js";
|
|
14
14
|
export { AithosAuth, DEFAULT_API_BASE_URL, DEFAULT_AUTH_BASE_URL, } from "./auth.js";
|
|
15
|
-
export type { AithosAuthConfig, AithosSession, ApplyPasswordResetInput, ApplyPasswordResetResult, CompleteSsoFirstLoginInput, CompleteSsoFirstLoginResult, CustodialSignInInput, CustodialSignInResult, CustodialSignUpInput, CustodialSignUpResult, DelegateInfo, ImportMandateInput, OwnerInfo, RequestPasswordResetInput, SignInInput, SignInWithGoogleOptions, SignInWithRecoveryInput, SignUpInput, SignUpResult, } from "./auth.js";
|
|
15
|
+
export type { AithosAuthConfig, AithosSession, ApplyPasswordResetInput, ApplyPasswordResetResult, CompleteSsoFirstLoginInput, CompleteSsoFirstLoginResult, CustodialSignInInput, CustodialSignInResult, CustodialSignUpInput, CustodialSignUpResult, DelegateInfo, ImportMandateInput, OwnerInfo, RequestPasswordResetInput, ResendVerificationInput, SignInInput, SignInWithGoogleOptions, SignInWithRecoveryInput, SignUpInput, SignUpResult, VerifyEmailInput, VerifyEmailResult, } from "./auth.js";
|
|
16
16
|
export { DEFAULT_SESSION_STORAGE_KEY, defaultSessionStore, localStorageStore, noopStore, sessionStorageStore, type AithosSessionStore, } from "./session-store.js";
|
|
17
17
|
export { DEFAULT_KEYSTORE_DB_NAME, defaultKeyStore, indexedDbKeyStore, memoryKeyStore, type AithosKeyStore, type StoredDelegateKeys, type StoredOwnerKeys, } from "./key-store.js";
|
|
18
18
|
export { EthosClient, EthosNamespace, EthosZone, ZONE_NAMES, } from "./ethos.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.
|
|
20
|
+
export const VERSION = "0.1.0-alpha.32";
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aithos/sdk",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.32",
|
|
4
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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aithos",
|