@synqauth/js 0.0.0 → 0.2.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.
@@ -1,3 +1,441 @@
1
+ import * as oauth from "oauth4webapi";
2
+ //#region src/generated/challenge.d.ts
3
+ /** The ways in an app's own screens sign people in with (`synq_method`). */
4
+ declare const CHALLENGE_METHODS: readonly ["password", "email", "passkey"];
5
+ /** One of `CHALLENGE_METHODS`. */
6
+ type ChallengeMethod = (typeof CHALLENGE_METHODS)[number];
7
+ /** What went wrong with a step (`synq_problem`): branch on it, never on the English. */
8
+ declare const CHALLENGE_PROBLEMS: readonly ["missing", "invalid_email", "wrong_credentials", "email_taken", "password_too_short", "password_too_long", "password_guessable", "password_breached", "wrong_code", "code_expired", "code_used_up", "address_suppressed", "team_only", "send_failed", "passkey_failed", "passkey_unknown", "passkey_expired", "slow_down", "locked", "limited"];
9
+ /** One of `CHALLENGE_PROBLEMS`. */
10
+ type ChallengeProblem = (typeof CHALLENGE_PROBLEMS)[number];
11
+ /** Why only the hosted page can go on (`redirect_to_web`'s `synq_reason`). */
12
+ declare const REDIRECT_REASONS: readonly ["own_ui_off", "paused", "token_gate", "step_up", "passkey_unavailable", "code_locked", "email_in_use", "unproven", "sign_up_gate", "method_unavailable", "hosted_only"];
13
+ /** One of `REDIRECT_REASONS`. */
14
+ type RedirectReason = (typeof REDIRECT_REASONS)[number];
15
+ /** Why this person can't sign in here (`access_denied`'s `synq_reason`). */
16
+ declare const DENIED_REASONS: readonly ["disabled", "deleting", "sign_up_closed", "already_exists"];
17
+ /** One of `DENIED_REASONS`. */
18
+ type DeniedReason = (typeof DENIED_REASONS)[number];
19
+ //#endregion
20
+ //#region src/sign-in-flow.d.ts
21
+ /** A passkey's request options, for `PublicKeyCredential.parseRequestOptionsFromJSON`. */
22
+ type PasskeyRequestOptionsJSON = Record<string, unknown>;
23
+ /** A passkey's assertion, as `PublicKeyCredential.toJSON()` gives it. */
24
+ type PasskeyAssertionJSON = Record<string, unknown>;
25
+ /** Where a flow is: what the app's screen asks for now. */
26
+ type SignInFlowStep = 'start' | 'password' | 'email' | 'code' | 'verify-email' | 'passkey' | 'done';
27
+ /** What a step of a sign-in came to: what to show next. */
28
+ type SignInStepResult<S> =
29
+ /** Signed in: the session is kept, as after a callback. */
30
+ {
31
+ readonly status: 'signed-in';
32
+ readonly session: S;
33
+ } |
34
+ /** A code went to the address: ask for it (`email.verify`), or send another (`email.resend`). */
35
+ {
36
+ readonly status: 'code-sent';
37
+ readonly email: string;
38
+ readonly expiresAt: Date;
39
+ } |
40
+ /** Signed in with an address nobody proved: ask for its emailed code, or skip it where `canSkip`. */
41
+ {
42
+ readonly status: 'verify-email';
43
+ readonly email: string;
44
+ readonly canSkip: boolean;
45
+ } |
46
+ /** Ask the browser for a passkey with these options, and give `passkey.signIn` what it answers. */
47
+ {
48
+ readonly status: 'passkey';
49
+ readonly options: PasskeyRequestOptionsJSON;
50
+ /** On a custom domain: the brand's own host's passkeys too (`passkey.signIn(…, { related: true })`). */
51
+ readonly related?: {
52
+ readonly rpId: string;
53
+ readonly options: PasskeyRequestOptionsJSON;
54
+ };
55
+ /** What went wrong with the last passkey, when it is asked for again. */
56
+ readonly problem?: ChallengeProblem;
57
+ readonly message?: string;
58
+ } |
59
+ /** That didn't work: say `message` (or your own words for `problem`), and let the person try again. */
60
+ {
61
+ readonly status: 'retry';
62
+ readonly problem: ChallengeProblem;
63
+ readonly message: string;
64
+ /** Seconds to wait first, when Synq said. */
65
+ readonly retryAfter?: number;
66
+ } |
67
+ /** Only the hosted page can go on: `continue()` starts an ordinary sign-in there. */
68
+ {
69
+ readonly status: 'hosted';
70
+ readonly reason: RedirectReason;
71
+ readonly method?: ChallengeMethod;
72
+ readonly loginHint?: string;
73
+ continue(): Promise<void>;
74
+ } |
75
+ /** This person can't sign in here: say `message`. */
76
+ {
77
+ readonly status: 'refused';
78
+ readonly reason: DeniedReason;
79
+ readonly message: string;
80
+ };
81
+ /** What a sign-in asks for, beyond the client's own. */
82
+ interface StartSignInOptions {
83
+ /** The scopes, instead of the client's. */
84
+ scope?: string;
85
+ /** The API the access token is for, instead of the client's. */
86
+ resource?: string;
87
+ /** Other APIs the grant should cover too (RFC 8707), as for `authorizationUrl`. */
88
+ additionalResources?: readonly string[];
89
+ /** The sign-in level the app needs, as for `authorizationUrl` (`urn:synq:loa:2` is a passkey). */
90
+ acr?: {
91
+ readonly values: readonly string[];
92
+ readonly essential?: boolean;
93
+ };
94
+ }
95
+ /** A flow as `toJSON` saves it, for `resumeSignIn`: keep it where only the app reads it. */
96
+ interface SavedSignIn {
97
+ readonly v: 1;
98
+ readonly verifier: string;
99
+ readonly nonce: string;
100
+ readonly scope: string;
101
+ readonly resource?: string;
102
+ readonly additionalResources?: readonly string[];
103
+ readonly acr?: {
104
+ readonly values: readonly string[];
105
+ readonly essential?: boolean;
106
+ };
107
+ readonly authSession?: string;
108
+ readonly email?: string;
109
+ readonly step: SignInFlowStep;
110
+ }
111
+ /** What a flow needs of the client it signs in to. */
112
+ interface SignInPort<S> {
113
+ server(): Promise<oauth.AuthorizationServer>;
114
+ readonly client: oauth.Client;
115
+ readonly auth: oauth.ClientAuth;
116
+ readonly fetch: typeof fetch;
117
+ /** Headers every request carries: a server's `Synq-End-User-IP`. */
118
+ readonly headers?: Readonly<Record<string, string>>;
119
+ /** Keeps what the code was redeemed for, as a callback does, and answers the session. */
120
+ finish(result: oauth.TokenEndpointResponse, claims: oauth.IDToken | undefined): Promise<S>;
121
+ /** Starts an ordinary sign-in on the hosted page. */
122
+ hosted(options: {
123
+ method?: ChallengeMethod;
124
+ loginHint?: string;
125
+ }): Promise<void>;
126
+ }
127
+ /** One sign-in on an app's own screens. */
128
+ declare class SignInFlow<S> {
129
+ #private;
130
+ constructor(port: SignInPort<S>, state: SavedSignIn);
131
+ /** What the app's screen asks for now. */
132
+ get step(): SignInFlowStep;
133
+ /** The address the sign-in is for, once one was typed. */
134
+ get address(): string | undefined;
135
+ /** Email and password. */
136
+ readonly password: {
137
+ signIn: (input: {
138
+ email: string;
139
+ password: string;
140
+ }) => Promise<SignInStepResult<S>>;
141
+ /** Makes an account where the brand lets people sign up; its address is verified next. */
142
+ signUp: (input: {
143
+ email: string;
144
+ password: string;
145
+ }) => Promise<SignInStepResult<S>>;
146
+ };
147
+ /** A code by email. */
148
+ readonly email: {
149
+ send: (email: string) => Promise<SignInStepResult<S>>;
150
+ verify: (code: string) => Promise<SignInStepResult<S>>;
151
+ resend: () => Promise<SignInStepResult<S>>;
152
+ };
153
+ /** A passkey: `options()`, then the browser's assertion to `signIn`. */
154
+ readonly passkey: {
155
+ options: () => Promise<SignInStepResult<S>>;
156
+ signIn: (credential: PasskeyAssertionJSON, options?: {
157
+ related?: boolean;
158
+ }) => Promise<SignInStepResult<S>>;
159
+ };
160
+ /** Verifying an address after a sign-in that didn't prove it. */
161
+ readonly verifyEmail: {
162
+ verify: (code: string) => Promise<SignInStepResult<S>>;
163
+ resend: () => Promise<SignInStepResult<S>>;
164
+ skip: () => Promise<SignInStepResult<S>>;
165
+ };
166
+ /** The flow, to keep between requests (a server's sealed cookie, a phone's secure storage). */
167
+ toJSON(): SavedSignIn;
168
+ }
169
+ //#endregion
170
+ //#region src/storage.d.ts
171
+ /**
172
+ * Where the client keeps what it must remember: the session, and a sign-in's
173
+ * transaction (its PKCE verifier, state and nonce) across the redirect to
174
+ * Synq and back. Any key-value store fits; each method may be async.
175
+ *
176
+ * Refresh tokens are kept only by a store that says it may hold them
177
+ * (`holdsRefreshTokens`): the memory store, or one on a server or in a
178
+ * device's secure storage. Everything else keeps the session without its
179
+ * refresh token, which then lives in the client's memory alone, so a refresh
180
+ * token never lands in `localStorage` or `sessionStorage`, where any script
181
+ * on the page reads it.
182
+ *
183
+ * Synq rotates refresh tokens and ends the whole grant when a used one comes
184
+ * back, so only one refresh may run at a time for one session. One client
185
+ * makes sure of that by itself. When several clients share a store (requests
186
+ * on one server, or replicas sharing a cookie or Redis), the store must give
187
+ * them a lock (`lock`); a store without one is for one client per process.
188
+ */
189
+ interface SynqStorage {
190
+ get(key: string): string | null | undefined | Promise<string | null | undefined>;
191
+ set(key: string, value: string): void | Promise<void>;
192
+ remove(key: string): void | Promise<void>;
193
+ /** Whether this store may hold refresh tokens: true only for memory, a server or secure device storage. */
194
+ readonly holdsRefreshTokens?: boolean;
195
+ /**
196
+ * Runs `fn` while no other holder of `name`'s lock runs, in any client
197
+ * sharing this store: the client refreshes inside it, and reads the
198
+ * session again first, so a refresh another client finished meanwhile is
199
+ * used rather than repeated. A store shared across requests or replicas
200
+ * must have one.
201
+ */
202
+ lock?<T>(name: string, fn: () => Promise<T>): Promise<T>;
203
+ /** The keys it holds, so the client can sweep away sign-ins that were never finished. */
204
+ keys?(): Iterable<string> | Promise<Iterable<string>>;
205
+ }
206
+ /**
207
+ * Keeps everything in this process's memory: the default, and the only store
208
+ * that holds refresh tokens unless told. Clients that share one share its
209
+ * lock, so they never refresh one session twice at once.
210
+ */
211
+ declare function memoryStorage(): SynqStorage;
212
+ /**
213
+ * The tab's `sessionStorage`: it ends with the tab, and no other tab reads
214
+ * it. As the session's store it keeps the access and ID tokens only, never
215
+ * the refresh token.
216
+ */
217
+ declare function browserSessionStorage(): SynqStorage;
218
+ /**
219
+ * The browser's `localStorage`, which every tab of the app's origin shares.
220
+ * Where a sign-in's transaction goes by default in a browser: a magic link
221
+ * or an email's link opens a new tab, which finishes the sign-in and comes
222
+ * back to the app there, so the tab that started it can't be the only one
223
+ * that knows it. Each transaction is kept under its own state, for an hour
224
+ * at most, deleted when it finishes, and swept away when stale. The price:
225
+ * any script on the app's origin can read a transaction meanwhile (its PKCE
226
+ * verifier and nonce, useless without the code Synq sends to the app's own
227
+ * redirect URI), as it can read anything else the app keeps. As the
228
+ * session's store it keeps the access and ID tokens only, never the refresh
229
+ * token.
230
+ */
231
+ declare function browserLocalStorage(): SynqStorage;
232
+ //#endregion
233
+ //#region src/client.d.ts
234
+ /** What a Synq client needs to know about the app it signs people in to. */
235
+ interface SynqClientOptions {
236
+ /** The brand's issuer URL, as the dashboard shows it (`https://<issuer-slug>.auth.<Synq's domain>`). */
237
+ issuer: string | URL;
238
+ /** The app's client_id, from the dashboard or the management API. */
239
+ clientId: string;
240
+ /**
241
+ * A confidential app's secret, sent with HTTP Basic. Only on a server:
242
+ * an app whose code reaches a browser has no secret.
243
+ */
244
+ clientSecret?: string;
245
+ /** Where Synq sends the browser back with the code: one of the app's registered redirect URIs, exactly. */
246
+ redirectUri: string;
247
+ /** Where the browser lands after signing out: one of the app's registered post-logout URIs. */
248
+ postLogoutRedirectUri?: string;
249
+ /** The scopes to ask for: `openid` by default. Add `offline_access` for a refresh token. */
250
+ scope?: string;
251
+ /** The API the access token is for (RFC 8707): a registered API's identifier. Without it the token is only good for userinfo. */
252
+ resource?: string;
253
+ /**
254
+ * Where the session is kept: this process's memory by default. Clients
255
+ * that share a store (requests on a server, replicas) need one with a
256
+ * `lock`, so they never present one refresh token twice; without one, use
257
+ * one client per process.
258
+ */
259
+ storage?: SynqStorage;
260
+ /**
261
+ * Where a sign-in's transaction is kept across the redirect: the browser's
262
+ * localStorage, which every tab of the app shares (a magic link finishes a
263
+ * sign-in in a new tab), and memory outside a browser.
264
+ */
265
+ transactionStorage?: SynqStorage;
266
+ /** The `fetch` to call Synq with: the global one by default. */
267
+ fetch?: typeof fetch;
268
+ /** How many seconds before the access token expires `getAccessToken` gets a new one: 30 by default. */
269
+ refreshLeewaySeconds?: number;
270
+ }
271
+ /** What to ask for when sending someone to sign in. */
272
+ interface AuthorizationUrlOptions {
273
+ /** Go straight to one sign-in method of the hosted page (`synq_method`): `password`, `email`, `passkey`, `wallet`, `google`... */
274
+ method?: string;
275
+ /** The address or username to fill in (`login_hint`). */
276
+ loginHint?: string;
277
+ /** `login` to ask for a fresh sign-in, `none` to sign in without showing anything (or fail), `consent` to ask again. */
278
+ prompt?: 'none' | 'login' | 'consent' | 'select_account';
279
+ /**
280
+ * The longest ago, in seconds, the person may have signed in (`max_age`);
281
+ * the ID token's `auth_time` is checked against it. Sent only when given:
282
+ * Synq asks anyone whose sign-in time it never saw proved (a provider that
283
+ * says nothing of when, such as Discord) to sign in again whenever
284
+ * `max_age` is present, so ask for it only where a fresh sign-in matters.
285
+ */
286
+ maxAge?: number;
287
+ /** The scopes for this sign-in, instead of the client's. */
288
+ scope?: string;
289
+ /** The API for this sign-in's access token, instead of the client's. */
290
+ resource?: string;
291
+ /**
292
+ * Other APIs the sign-in's grant should cover too (RFC 8707: one
293
+ * `resource` each). The code is still exchanged for `resource`'s token;
294
+ * another client sharing the session's store gets one for these by
295
+ * refreshing with its own `resource`, as @synqauth/nextjs does for the
296
+ * account API beside the app's own.
297
+ */
298
+ additionalResources?: readonly string[];
299
+ /** Any other parameter Synq takes, such as `synq_intent`. They can't replace the flow's own (state, nonce, PKCE, redirect URI). */
300
+ params?: Readonly<Record<string, string>>;
301
+ /**
302
+ * Sends the request's parameters to the issuer first, as a pushed
303
+ * authorization request (RFC 9126), and answers a URL that carries only
304
+ * its reference: for a parameter that must never travel in a URL, such
305
+ * as a wallet's signature (`synq_siws`).
306
+ */
307
+ pushed?: boolean;
308
+ /**
309
+ * The sign-in level the app needs, as an `acr` value: Synq's passkey
310
+ * sign-in is `urn:synq:loa:2`, every other `urn:synq:loa:1`. By default it
311
+ * is a request (`acr_values`): someone without a passkey still signs in,
312
+ * at loa:1, and the app checks `claims.acr`. With `essential: true` it is
313
+ * a requirement, through the claims parameter: a sign-in below it fails
314
+ * with `unmet_authentication_requirements` instead, and `handleCallback`
315
+ * checks the ID token's `acr` too.
316
+ */
317
+ acr?: {
318
+ readonly values: readonly string[];
319
+ readonly essential?: boolean;
320
+ };
321
+ }
322
+ /** The claims of a validated ID token: who signed in, when, and how. */
323
+ interface IdTokenClaims {
324
+ readonly iss: string;
325
+ readonly sub: string;
326
+ readonly aud: string | string[];
327
+ readonly iat: number;
328
+ readonly exp: number;
329
+ readonly auth_time?: number;
330
+ readonly amr?: string[];
331
+ readonly acr?: string;
332
+ readonly [claim: string]: unknown;
333
+ }
334
+ /** A signed-in session, without its refresh token, which the client keeps to itself. */
335
+ interface Session {
336
+ readonly accessToken: string;
337
+ readonly tokenType: string;
338
+ /** When the access token expires, in seconds since the epoch, when Synq said. */
339
+ readonly expiresAt: number | undefined;
340
+ readonly scope: string | undefined;
341
+ readonly idToken: string | undefined;
342
+ /** The ID token's claims, validated when it was issued. */
343
+ readonly claims: IdTokenClaims | undefined;
344
+ /** Whether the client holds a refresh token for this session. */
345
+ readonly refreshable: boolean;
346
+ /**
347
+ * Where the person signed in: `hosted` on the brand's hosted page, `app`
348
+ * on the app's own screens (`startSignIn`), whose session no browser
349
+ * cookie at Synq holds, so signing out revokes its refresh token alone.
350
+ */
351
+ readonly via: 'hosted' | 'app';
352
+ }
353
+ /** A change to the session, as `subscribe` reports it. */
354
+ type SessionEvent = {
355
+ readonly type: 'signed-in';
356
+ readonly session: Session;
357
+ } | {
358
+ readonly type: 'refreshed';
359
+ readonly session: Session;
360
+ } | {
361
+ readonly type: 'signed-out';
362
+ readonly session: null;
363
+ };
364
+ /** What a server signing people in for its app's own screens adds to a flow (`@synqauth/nextjs`). */
365
+ interface ServerSignInOptions {
366
+ /**
367
+ * The address the person is at, as the server saw it: Synq counts its
368
+ * per-address limits for the app with it, and counts none without it.
369
+ * Only a confidential app's is taken.
370
+ */
371
+ endUserIp?: string;
372
+ }
373
+ /** What `signOut` takes: where the browser lands afterwards, and the `state` handed back there. */
374
+ interface SignOutOptions {
375
+ /** Where the browser lands afterwards, instead of the client's `postLogoutRedirectUri`. */
376
+ postLogoutRedirectUri?: string;
377
+ /** Handed back on the post-logout redirect. */
378
+ state?: string;
379
+ }
380
+ /**
381
+ * A client for one app at one brand's issuer, made by `createSynqClient`: it
382
+ * signs people in with the authorization code flow, keeps the session, and
383
+ * refreshes and ends it.
384
+ */
385
+ interface SynqClient {
386
+ /** The URL to send the browser to, to sign in; the transaction it starts is kept in `transactionStorage`. */
387
+ authorizationUrl(options?: AuthorizationUrlOptions): Promise<URL>;
388
+ /** In a browser: sends it to Synq to sign in. */
389
+ signIn(options?: AuthorizationUrlOptions): Promise<void>;
390
+ /** Finishes a sign-in from the URL Synq sent the browser back to; each transaction finishes once. */
391
+ handleCallback(url: string | URL): Promise<Session>;
392
+ /** The current session, or `null` when nobody is signed in. */
393
+ getSession(): Promise<Session | null>;
394
+ /**
395
+ * After a page loads: the session, while it can still give a current
396
+ * access token (its access token is good for the leeway, or the client
397
+ * holds its refresh token), or `null` when there is none. A session the
398
+ * browser kept without its refresh token, whose access token is running
399
+ * out, is ended here, and the browser is sent to Synq with `prompt=none` to
400
+ * sign the person in again without showing anything; it resolves `null` as
401
+ * the page leaves. On the way back, `handleCallback` signs them in, or
402
+ * fails with `login_required` when they are no longer signed in at Synq.
403
+ * Call it on page load, but not on the callback's own page.
404
+ */
405
+ restoreSession(options?: Omit<AuthorizationUrlOptions, 'prompt'>): Promise<Session | null>;
406
+ /** A current access token, refreshed first when it expires within `minValiditySeconds` (the client's leeway by default). */
407
+ getAccessToken(options?: {
408
+ minValiditySeconds?: number;
409
+ }): Promise<string>;
410
+ /**
411
+ * Gets new tokens with the refresh token. Calls at once share one request:
412
+ * Synq rotates refresh tokens and treats a used one presented again as
413
+ * theft, ending the whole grant.
414
+ */
415
+ refresh(): Promise<Session>;
416
+ /** Calls `listener` on every sign-in, refresh and sign-out; returns the function that stops it. */
417
+ subscribe(listener: (event: SessionEvent) => void): () => void;
418
+ /**
419
+ * Ends the session here, revokes its refresh token, and returns Synq's
420
+ * sign-out URL (RP-initiated logout), to send the browser to so the
421
+ * brand's session ends too: `null` when the issuer has none, or for a
422
+ * session signed in on the app's own screens, which revoking its refresh
423
+ * token ends.
424
+ */
425
+ signOut(options?: SignOutOptions): Promise<URL | null>;
426
+ /**
427
+ * Starts a sign-in on the app's own screens (AERO-1411): its forms call
428
+ * the flow's methods, and each resolves to what to show next. For the
429
+ * brand's own native app with own-UI sign-in on; a web app does this
430
+ * from its server (`@synqauth/nextjs`).
431
+ */
432
+ startSignIn(options?: StartSignInOptions & ServerSignInOptions): SignInFlow<Session>;
433
+ /** Picks up a flow `toJSON()` saved: after the app was closed, say, or on a server's next request. */
434
+ resumeSignIn(saved: SavedSignIn | string, options?: ServerSignInOptions): SignInFlow<Session>;
435
+ }
436
+ /** A client for one app at one of a brand's Synq issuers. */
437
+ declare function createSynqClient(options: SynqClientOptions): SynqClient;
438
+ //#endregion
1
439
  //#region src/generated/errors.d.ts
2
440
  /** Each `code` a Synq API's problem documents carry, with its HTTP status. */
3
441
  declare const ERROR_CODES: {
@@ -53,6 +491,8 @@ declare const ERROR_CODES: {
53
491
  readonly upload_expired: 410;
54
492
  readonly file_rejected: 422;
55
493
  readonly upload_limit_reached: 409;
494
+ readonly api_keys_not_allowed: 403;
495
+ readonly api_key_limit_reached: 409;
56
496
  readonly rate_limited: 429;
57
497
  readonly internal_error: 500;
58
498
  readonly service_unavailable: 503;
@@ -84,7 +524,15 @@ type SynqClientErrorCode =
84
524
  /** A webhook delivery signed outside the tolerance: a replay, or a clock far off. */
85
525
  'stale_webhook';
86
526
  /** OAuth's and OpenID Connect's error codes, which Synq's issuers answer with. */
87
- type OAuthErrorCode = 'invalid_request' | 'invalid_client' | 'invalid_grant' | 'unauthorized_client' | 'unsupported_grant_type' | 'invalid_scope' | 'invalid_target' | 'access_denied' | 'login_required' | 'consent_required' | 'interaction_required' | 'invalid_token' | 'insufficient_scope' | 'server_error' | 'temporarily_unavailable';
527
+ type OAuthErrorCode = 'invalid_request' | 'invalid_client' | 'invalid_grant' | 'unauthorized_client' | 'unsupported_grant_type' | 'invalid_scope' | 'invalid_target' | 'access_denied' | 'login_required' | 'consent_required' | 'interaction_required' |
528
+ /** The sign-in didn't reach the `acr` the app insisted on (`acr: { essential: true }`). */
529
+ 'unmet_authentication_requirements' | 'invalid_token' | 'insufficient_scope' | 'server_error' | 'temporarily_unavailable' |
530
+ /** A sign-in on the app's own screens ended or expired (AERO-1411): start another. */
531
+ 'invalid_session' |
532
+ /** Only the hosted page can go on with this sign-in. */
533
+ 'redirect_to_web' |
534
+ /** Too many requests: wait `retryAfter` seconds. */
535
+ 'too_many_requests';
88
536
  /** Every code a `SynqError` can carry; an unknown one from a newer Synq passes through as a string. */
89
537
  type SynqAnyErrorCode = SynqErrorCode | OAuthErrorCode | SynqClientErrorCode | (string & Record<never, never>);
90
538
  /**
@@ -934,7 +1382,7 @@ interface paths {
934
1382
  readonly get: {
935
1383
  readonly parameters: {
936
1384
  readonly query?: {
937
- /** @description Only this action: org.created, org.updated, org.deleted, org.plan_changed, org.disabled, org.enabled, brand.created, brand.updated, brand.deleted, brand.reply_to_confirmed, app.created, app.updated, app.secret_rotated, app.deleted, app.api_access_changed, app.refresh_token_replayed, api.created, api.updated, api.deleted, org.ownership_transferred, member.added, member.role_assigned, member.removed, member.left, role.created, role.updated, role.deleted, invite.created, invite.revoked, invite.accepted, webhook.created, webhook.updated, webhook.secret_rotated, webhook.deleted, webhook.disabled, token_gate.created, token_gate.updated, token_gate.deleted, email_domain.created, email_domain.verified, email_domain.failed, email_domain.deleted, brand.custom_domain_added, brand.custom_domain_verified, brand.custom_domain_activated, brand.custom_domain_failed, brand.custom_domain_suspended, brand.custom_domain_restored, brand.custom_domain_removed, api_key.created, api_key.revoked, change_request.created, change_request.approved, change_request.declined, file.uploaded, file.rejected, file.downloaded, staff.role_granted, user.created, user.updated, user.looked_up, user.email_verified, user.password_reset, user.passkey_added, user.passkey_removed, user.identity_linked, user.identity_unlinked, user.consent_granted, user.consent_revoked, user.sessions_revoked, user.deletion_scheduled, user.deletion_cancelled, user.deleted, user.sign_in_failed, user.token_gate_checked, user.token_gate_dry_run. */
1385
+ /** @description Only this action: org.created, org.updated, org.deleted, org.plan_changed, org.disabled, org.enabled, brand.created, brand.updated, brand.deleted, brand.reply_to_confirmed, brand.app_registrations_full, app.created, app.updated, app.secret_rotated, app.deleted, app.api_access_changed, app.refresh_token_replayed, api.created, api.updated, api.deleted, org.ownership_transferred, member.added, member.role_assigned, member.removed, member.left, role.created, role.updated, role.deleted, invite.created, invite.revoked, invite.accepted, webhook.created, webhook.updated, webhook.secret_rotated, webhook.deleted, webhook.disabled, token_gate.created, token_gate.updated, token_gate.deleted, email_domain.created, email_domain.verified, email_domain.failed, email_domain.deleted, brand.custom_domain_added, brand.custom_domain_verified, brand.custom_domain_activated, brand.custom_domain_failed, brand.custom_domain_suspended, brand.custom_domain_restored, brand.custom_domain_removed, api_key.created, api_key.revoked, change_request.created, change_request.approved, change_request.declined, file.uploaded, file.rejected, file.downloaded, staff.role_granted, user.created, user.updated, user.looked_up, user.email_verified, user.password_reset, user.passkey_added, user.passkey_removed, user.identity_linked, user.identity_unlinked, user.consent_granted, user.consent_revoked, user.api_key_created, user.api_key_revoked, user.sessions_revoked, user.deletion_scheduled, user.deletion_cancelled, user.deleted, user.sign_in_failed, user.token_gate_checked, user.token_gate_dry_run. */
938
1386
  readonly action?: string;
939
1387
  /** @description Only this actor's changes: a user's or an API key's id. */
940
1388
  readonly actor?: string;
@@ -5162,6 +5610,165 @@ interface paths {
5162
5610
  };
5163
5611
  readonly trace?: never;
5164
5612
  };
5613
+ readonly "/v1/orgs/{org}/brands/{brand}/users/{user}/api-keys": {
5614
+ readonly parameters: {
5615
+ readonly query?: never;
5616
+ readonly header?: never;
5617
+ readonly path?: never;
5618
+ readonly cookie?: never;
5619
+ };
5620
+ /**
5621
+ * A user's API keys
5622
+ * @description The keys they made for the brand's APIs (AERO-1693), oldest first, revoked and expired ones too; never a key itself. Needs `org.users.view`.
5623
+ */
5624
+ readonly get: {
5625
+ readonly parameters: {
5626
+ readonly query?: never;
5627
+ readonly header?: never;
5628
+ readonly path: {
5629
+ readonly brand: string;
5630
+ readonly org: string;
5631
+ readonly user: string;
5632
+ };
5633
+ readonly cookie?: never;
5634
+ };
5635
+ readonly requestBody?: never;
5636
+ readonly responses: {
5637
+ /** @description Their API keys */
5638
+ readonly 200: {
5639
+ headers: {
5640
+ readonly [name: string]: unknown;
5641
+ };
5642
+ content: {
5643
+ readonly "application/json": {
5644
+ readonly data: readonly components["schemas"]["UserApiKey"][];
5645
+ };
5646
+ };
5647
+ };
5648
+ /** @description No credential, or one that is not valid */
5649
+ readonly 401: {
5650
+ headers: {
5651
+ readonly [name: string]: unknown;
5652
+ };
5653
+ content: {
5654
+ readonly "application/problem+json": components["schemas"]["Problem"];
5655
+ };
5656
+ };
5657
+ /** @description The credential lacks the permission this route needs */
5658
+ readonly 403: {
5659
+ headers: {
5660
+ readonly [name: string]: unknown;
5661
+ };
5662
+ content: {
5663
+ readonly "application/problem+json": components["schemas"]["Problem"];
5664
+ };
5665
+ };
5666
+ /** @description No such org, or one the credential may not act in */
5667
+ readonly 404: {
5668
+ headers: {
5669
+ readonly [name: string]: unknown;
5670
+ };
5671
+ content: {
5672
+ readonly "application/problem+json": components["schemas"]["Problem"];
5673
+ };
5674
+ };
5675
+ /** @description Too many requests */
5676
+ readonly 429: {
5677
+ headers: {
5678
+ readonly [name: string]: unknown;
5679
+ };
5680
+ content: {
5681
+ readonly "application/problem+json": components["schemas"]["Problem"];
5682
+ };
5683
+ };
5684
+ };
5685
+ };
5686
+ readonly put?: never;
5687
+ readonly post?: never;
5688
+ readonly delete?: never;
5689
+ readonly options?: never;
5690
+ readonly head?: never;
5691
+ readonly patch?: never;
5692
+ readonly trace?: never;
5693
+ };
5694
+ readonly "/v1/orgs/{org}/brands/{brand}/users/{user}/api-keys/{key}": {
5695
+ readonly parameters: {
5696
+ readonly query?: never;
5697
+ readonly header?: never;
5698
+ readonly path?: never;
5699
+ readonly cookie?: never;
5700
+ };
5701
+ readonly get?: never;
5702
+ readonly put?: never;
5703
+ readonly post?: never;
5704
+ /**
5705
+ * Revoke a user's API key
5706
+ * @description At once: its API's next check of it answers `active: false`. Needs `org.users.edit`. Recorded as `user.api_key_revoked`.
5707
+ */
5708
+ readonly delete: {
5709
+ readonly parameters: {
5710
+ readonly query?: never;
5711
+ readonly header?: never;
5712
+ readonly path: {
5713
+ readonly brand: string;
5714
+ readonly key: string;
5715
+ readonly org: string;
5716
+ readonly user: string;
5717
+ };
5718
+ readonly cookie?: never;
5719
+ };
5720
+ readonly requestBody?: never;
5721
+ readonly responses: {
5722
+ /** @description Revoked */
5723
+ readonly 204: {
5724
+ headers: {
5725
+ readonly [name: string]: unknown;
5726
+ };
5727
+ content?: never;
5728
+ };
5729
+ /** @description No credential, or one that is not valid */
5730
+ readonly 401: {
5731
+ headers: {
5732
+ readonly [name: string]: unknown;
5733
+ };
5734
+ content: {
5735
+ readonly "application/problem+json": components["schemas"]["Problem"];
5736
+ };
5737
+ };
5738
+ /** @description The credential lacks the permission this route needs */
5739
+ readonly 403: {
5740
+ headers: {
5741
+ readonly [name: string]: unknown;
5742
+ };
5743
+ content: {
5744
+ readonly "application/problem+json": components["schemas"]["Problem"];
5745
+ };
5746
+ };
5747
+ /** @description No such org, or one the credential may not act in */
5748
+ readonly 404: {
5749
+ headers: {
5750
+ readonly [name: string]: unknown;
5751
+ };
5752
+ content: {
5753
+ readonly "application/problem+json": components["schemas"]["Problem"];
5754
+ };
5755
+ };
5756
+ /** @description Too many requests */
5757
+ readonly 429: {
5758
+ headers: {
5759
+ readonly [name: string]: unknown;
5760
+ };
5761
+ content: {
5762
+ readonly "application/problem+json": components["schemas"]["Problem"];
5763
+ };
5764
+ };
5765
+ };
5766
+ };
5767
+ readonly options?: never;
5768
+ readonly head?: never;
5769
+ readonly patch?: never;
5770
+ readonly trace?: never;
5771
+ };
5165
5772
  readonly "/v1/orgs/{org}/brands/{brand}/users/{user}/consents": {
5166
5773
  readonly parameters: {
5167
5774
  readonly query?: never;
@@ -8121,7 +8728,7 @@ interface paths {
8121
8728
  readonly put?: never;
8122
8729
  /**
8123
8730
  * Test a token gate or a draft rule
8124
- * @description Checks a gate (`gate`), or a rule not saved yet (`rule`, with an optional `schedule`), against wallets (`wallets`, at most 10 addresses) or a user's wallets in a brand (`user` and `brand`), as a sign-in would, at `at` (an ISO time, for the window; now when left out), with what was read in the last minute unless `fresh`. Answers what each condition came to, how many reads it cost, and the refusal page's words for it. A draft is read from the chain as saving it would read it (400 at the path of what isn't what its condition says, 503 when the chain can't say), so it is tested as the gate it would be. Saves nothing. Needs `org.tokenGates.view`, and `org.users.view` to test a user, which is recorded as `user.token_gate_dry_run` (a test against addresses reads only public chain data, and isn't). Limited per org (`token_gate.dry_runs`), and reads the chain from the org's own budget (`token_gate.org_reads`), never a brand's sign-ins'.
8731
+ * @description Checks a gate (`gate`), or a rule not saved yet (`rule`, with an optional `schedule`), against wallets (`wallets`, at most 10 addresses) or a user's wallets in a brand (`user` and `brand`), as a sign-in would, at `at` (an ISO time, for the window; now when left out), with what was read in the last minute unless `fresh`. Answers what each condition came to, how many reads it cost, and the refusal page's words for it. A draft is read from the chain as saving it would read it (400 at the path of what isn't what its condition says, 503 when the chain can't say), so it is tested as the gate it would be. Saves nothing. Wants the org's plan to have token gating (`auth.token_gating`, 403 `plan_feature_unavailable` otherwise), since every test may read the chain through Synq's own key. Needs `org.tokenGates.view`, and `org.users.view` to test a user, which is recorded as `user.token_gate_dry_run` (a test against addresses reads only public chain data, and isn't). Limited per org (`token_gate.dry_runs`), and reads the chain from the org's own budget (`token_gate.org_reads`), never a brand's sign-ins'.
8125
8732
  */
8126
8733
  readonly post: {
8127
8734
  readonly parameters: {
@@ -8183,13 +8790,13 @@ interface paths {
8183
8790
  readonly "application/problem+json": components["schemas"]["Problem"];
8184
8791
  };
8185
8792
  };
8186
- /** @description The credential lacks the permission this route needs */
8793
+ /** @description The credential lacks org.tokenGates.view (or org.users.view to test a user), or the org's plan has no token gating */
8187
8794
  readonly 403: {
8188
8795
  headers: {
8189
8796
  readonly [name: string]: unknown;
8190
8797
  };
8191
8798
  content: {
8192
- readonly "application/problem+json": components["schemas"]["Problem"];
8799
+ readonly "application/problem+json": components["schemas"]["PlanProblem"];
8193
8800
  };
8194
8801
  };
8195
8802
  /** @description No such org, or one the credential may not act in */
@@ -8407,7 +9014,7 @@ interface paths {
8407
9014
  readonly "application/json": {
8408
9015
  readonly description?: string | null;
8409
9016
  /** @description The event types it wants, or `*` for every one */
8410
- readonly events: readonly ("*" | "org.created" | "org.updated" | "org.deleted" | "org.plan_changed" | "org.disabled" | "org.enabled" | "brand.created" | "brand.updated" | "brand.deleted" | "brand.reply_to_confirmed" | "app.created" | "app.updated" | "app.secret_rotated" | "app.deleted" | "app.api_access_changed" | "app.refresh_token_replayed" | "api.created" | "api.updated" | "api.deleted" | "org.ownership_transferred" | "member.added" | "member.role_assigned" | "member.removed" | "member.left" | "role.created" | "role.updated" | "role.deleted" | "invite.created" | "invite.revoked" | "invite.accepted" | "webhook.created" | "webhook.updated" | "webhook.secret_rotated" | "webhook.deleted" | "webhook.disabled" | "token_gate.created" | "token_gate.updated" | "token_gate.deleted" | "email_domain.created" | "email_domain.verified" | "email_domain.failed" | "email_domain.deleted" | "brand.custom_domain_added" | "brand.custom_domain_verified" | "brand.custom_domain_activated" | "brand.custom_domain_failed" | "brand.custom_domain_suspended" | "brand.custom_domain_restored" | "brand.custom_domain_removed" | "api_key.created" | "api_key.revoked" | "change_request.created" | "change_request.approved" | "change_request.declined" | "file.uploaded" | "file.rejected" | "file.downloaded" | "staff.role_granted" | "user.created" | "user.updated" | "user.looked_up" | "user.email_verified" | "user.password_reset" | "user.passkey_added" | "user.passkey_removed" | "user.identity_linked" | "user.identity_unlinked" | "user.consent_granted" | "user.consent_revoked" | "user.sessions_revoked" | "user.deletion_scheduled" | "user.deletion_cancelled" | "user.deleted" | "user.sign_in_failed" | "user.token_gate_checked" | "user.token_gate_dry_run" | "webhook.test")[];
9017
+ readonly events: readonly ("*" | "org.created" | "org.updated" | "org.deleted" | "org.plan_changed" | "org.disabled" | "org.enabled" | "brand.created" | "brand.updated" | "brand.deleted" | "brand.reply_to_confirmed" | "brand.app_registrations_full" | "app.created" | "app.updated" | "app.secret_rotated" | "app.deleted" | "app.api_access_changed" | "app.refresh_token_replayed" | "api.created" | "api.updated" | "api.deleted" | "org.ownership_transferred" | "member.added" | "member.role_assigned" | "member.removed" | "member.left" | "role.created" | "role.updated" | "role.deleted" | "invite.created" | "invite.revoked" | "invite.accepted" | "webhook.created" | "webhook.updated" | "webhook.secret_rotated" | "webhook.deleted" | "webhook.disabled" | "token_gate.created" | "token_gate.updated" | "token_gate.deleted" | "email_domain.created" | "email_domain.verified" | "email_domain.failed" | "email_domain.deleted" | "brand.custom_domain_added" | "brand.custom_domain_verified" | "brand.custom_domain_activated" | "brand.custom_domain_failed" | "brand.custom_domain_suspended" | "brand.custom_domain_restored" | "brand.custom_domain_removed" | "api_key.created" | "api_key.revoked" | "change_request.created" | "change_request.approved" | "change_request.declined" | "file.uploaded" | "file.rejected" | "file.downloaded" | "staff.role_granted" | "user.created" | "user.updated" | "user.looked_up" | "user.email_verified" | "user.password_reset" | "user.passkey_added" | "user.passkey_removed" | "user.identity_linked" | "user.identity_unlinked" | "user.consent_granted" | "user.consent_revoked" | "user.api_key_created" | "user.api_key_revoked" | "user.sessions_revoked" | "user.deletion_scheduled" | "user.deletion_cancelled" | "user.deleted" | "user.sign_in_failed" | "user.token_gate_checked" | "user.token_gate_dry_run" | "webhook.test")[];
8411
9018
  readonly url: string;
8412
9019
  };
8413
9020
  };
@@ -8630,7 +9237,7 @@ interface paths {
8630
9237
  readonly description?: string | null;
8631
9238
  readonly enabled?: boolean;
8632
9239
  /** @description The event types it wants, or `*` for every one */
8633
- readonly events?: readonly ("*" | "org.created" | "org.updated" | "org.deleted" | "org.plan_changed" | "org.disabled" | "org.enabled" | "brand.created" | "brand.updated" | "brand.deleted" | "brand.reply_to_confirmed" | "app.created" | "app.updated" | "app.secret_rotated" | "app.deleted" | "app.api_access_changed" | "app.refresh_token_replayed" | "api.created" | "api.updated" | "api.deleted" | "org.ownership_transferred" | "member.added" | "member.role_assigned" | "member.removed" | "member.left" | "role.created" | "role.updated" | "role.deleted" | "invite.created" | "invite.revoked" | "invite.accepted" | "webhook.created" | "webhook.updated" | "webhook.secret_rotated" | "webhook.deleted" | "webhook.disabled" | "token_gate.created" | "token_gate.updated" | "token_gate.deleted" | "email_domain.created" | "email_domain.verified" | "email_domain.failed" | "email_domain.deleted" | "brand.custom_domain_added" | "brand.custom_domain_verified" | "brand.custom_domain_activated" | "brand.custom_domain_failed" | "brand.custom_domain_suspended" | "brand.custom_domain_restored" | "brand.custom_domain_removed" | "api_key.created" | "api_key.revoked" | "change_request.created" | "change_request.approved" | "change_request.declined" | "file.uploaded" | "file.rejected" | "file.downloaded" | "staff.role_granted" | "user.created" | "user.updated" | "user.looked_up" | "user.email_verified" | "user.password_reset" | "user.passkey_added" | "user.passkey_removed" | "user.identity_linked" | "user.identity_unlinked" | "user.consent_granted" | "user.consent_revoked" | "user.sessions_revoked" | "user.deletion_scheduled" | "user.deletion_cancelled" | "user.deleted" | "user.sign_in_failed" | "user.token_gate_checked" | "user.token_gate_dry_run" | "webhook.test")[];
9240
+ readonly events?: readonly ("*" | "org.created" | "org.updated" | "org.deleted" | "org.plan_changed" | "org.disabled" | "org.enabled" | "brand.created" | "brand.updated" | "brand.deleted" | "brand.reply_to_confirmed" | "brand.app_registrations_full" | "app.created" | "app.updated" | "app.secret_rotated" | "app.deleted" | "app.api_access_changed" | "app.refresh_token_replayed" | "api.created" | "api.updated" | "api.deleted" | "org.ownership_transferred" | "member.added" | "member.role_assigned" | "member.removed" | "member.left" | "role.created" | "role.updated" | "role.deleted" | "invite.created" | "invite.revoked" | "invite.accepted" | "webhook.created" | "webhook.updated" | "webhook.secret_rotated" | "webhook.deleted" | "webhook.disabled" | "token_gate.created" | "token_gate.updated" | "token_gate.deleted" | "email_domain.created" | "email_domain.verified" | "email_domain.failed" | "email_domain.deleted" | "brand.custom_domain_added" | "brand.custom_domain_verified" | "brand.custom_domain_activated" | "brand.custom_domain_failed" | "brand.custom_domain_suspended" | "brand.custom_domain_restored" | "brand.custom_domain_removed" | "api_key.created" | "api_key.revoked" | "change_request.created" | "change_request.approved" | "change_request.declined" | "file.uploaded" | "file.rejected" | "file.downloaded" | "staff.role_granted" | "user.created" | "user.updated" | "user.looked_up" | "user.email_verified" | "user.password_reset" | "user.passkey_added" | "user.passkey_removed" | "user.identity_linked" | "user.identity_unlinked" | "user.consent_granted" | "user.consent_revoked" | "user.api_key_created" | "user.api_key_revoked" | "user.sessions_revoked" | "user.deletion_scheduled" | "user.deletion_cancelled" | "user.deleted" | "user.sign_in_failed" | "user.token_gate_checked" | "user.token_gate_dry_run" | "webhook.test")[];
8634
9241
  readonly url?: string;
8635
9242
  };
8636
9243
  };
@@ -9049,8 +9656,10 @@ interface components {
9049
9656
  /** @description Seconds, 300 to 900 */
9050
9657
  readonly access_token_ttl: number;
9051
9658
  readonly allow_third_party: boolean;
9659
+ /** @description Whether the brand's users may make API keys for it (AERO-1693), which its own app checks at the issuer's `POST /api-keys/verify` */
9660
+ readonly allow_user_api_keys: boolean;
9052
9661
  readonly brand_id: string;
9053
- /** @description The app that may introspect this API's tokens */
9662
+ /** @description The app that may introspect this API's tokens, and check its users' API keys */
9054
9663
  readonly client_id: string | null;
9055
9664
  /** Format: date-time */
9056
9665
  readonly created_at: string;
@@ -9121,6 +9730,8 @@ interface components {
9121
9730
  readonly ApiUpdate: {
9122
9731
  readonly access_token_ttl?: number;
9123
9732
  readonly allow_third_party?: boolean;
9733
+ /** @description Let the brand's users make API keys for it. Needs the API's own app (`client_id`), which alone checks them. Turning it off revokes every key its users made, for good. */
9734
+ readonly allow_user_api_keys?: boolean;
9124
9735
  /** @description One of the brand's web or machine apps, or null */
9125
9736
  readonly client_id?: string | null;
9126
9737
  readonly name?: string;
@@ -9149,6 +9760,8 @@ interface components {
9149
9760
  readonly last_used_at: string | null;
9150
9761
  readonly logo_uri: string | null;
9151
9762
  readonly name: string;
9763
+ /** @description Whether the app signs people in on its own screens, through the issuer's `/authorize-challenge` */
9764
+ readonly own_ui_sign_in: boolean;
9152
9765
  readonly pkce_optional: boolean;
9153
9766
  readonly policy_uri: string | null;
9154
9767
  readonly post_logout_redirect_uris: readonly string[];
@@ -9208,6 +9821,8 @@ interface components {
9208
9821
  readonly logo_uri?: string | null;
9209
9822
  /** @example Acme web */
9210
9823
  readonly name: string;
9824
+ /** @description The app signs people in on its own screens, with a password, an emailed code or a passkey, through the issuer's `/authorize-challenge` (OAuth 2.0 for First-Party Applications), rather than sending them to the hosted page. For the brand's own web or native apps only (`first_party`), and off by default: the app's code then sees the passwords and codes people type. Turning `first_party` off turns this off too */
9825
+ readonly own_ui_sign_in?: boolean;
9211
9826
  /** @description A web app may skip PKCE (not recommended); public apps never can */
9212
9827
  readonly pkce_optional?: boolean;
9213
9828
  /** Format: uri */
@@ -9264,6 +9879,8 @@ interface components {
9264
9879
  /** Format: uri */
9265
9880
  readonly logo_uri?: string | null;
9266
9881
  readonly name?: string;
9882
+ /** @description The app signs people in on its own screens, with a password, an emailed code or a passkey, through the issuer's `/authorize-challenge` (OAuth 2.0 for First-Party Applications), rather than sending them to the hosted page. For the brand's own web or native apps only (`first_party`), and off by default: the app's code then sees the passwords and codes people type. Turning `first_party` off turns this off too */
9883
+ readonly own_ui_sign_in?: boolean;
9267
9884
  /** @description A web app may skip PKCE (not recommended); public apps never can */
9268
9885
  readonly pkce_optional?: boolean;
9269
9886
  /** Format: uri */
@@ -9289,7 +9906,7 @@ interface components {
9289
9906
  };
9290
9907
  readonly AppWarning: {
9291
9908
  /**
9292
- * @description What it is about, for code to read. `loopback_redirect_uris`: deployed, a web app or SPA still has redirect or post-logout URIs on a loopback address (localhost, 127.0.0.1, [::1]), a dev server on a laptop
9909
+ * @description What it is about, for code to read. `loopback_redirect_uris`: deployed, a web app or SPA still has redirect or post-logout URIs on a loopback address (localhost, 127.0.0.1, [::1]), a dev server on a laptop. `passkey_origin_beyond_related_origins_limit`: a web app that signs people in on its own screens has an origin past the first 5 sites of the brand's passkey list, which browsers read, so passkeys from there go through the hosted page
9293
9910
  * @example loopback_redirect_uris
9294
9911
  */
9295
9912
  readonly code: string;
@@ -11032,6 +11649,40 @@ interface components {
11032
11649
  /** @description Solana addresses they sign in with */
11033
11650
  readonly wallets: readonly string[];
11034
11651
  };
11652
+ readonly UserApiKey: {
11653
+ readonly api: {
11654
+ /** @example https://api.acme.example */
11655
+ readonly identifier: string;
11656
+ readonly name: string;
11657
+ };
11658
+ /** Format: date-time */
11659
+ readonly created_at: string;
11660
+ /** Format: date-time */
11661
+ readonly expires_at: string | null;
11662
+ /** @example uak_01h455vb4pex5vsknk084sn02q */
11663
+ readonly id: string;
11664
+ /**
11665
+ * Format: date-time
11666
+ * @description When its API last checked it, to the minute
11667
+ */
11668
+ readonly last_used_at: string | null;
11669
+ /** @example Reports script */
11670
+ readonly name: string;
11671
+ /**
11672
+ * @description The start of the key, to tell keys apart
11673
+ * @example ak_live_Ab3d
11674
+ */
11675
+ readonly prefix: string;
11676
+ /** Format: date-time */
11677
+ readonly revoked_at: string | null;
11678
+ /**
11679
+ * @description What it may do: scopes of its API (one the API drops leaves every key)
11680
+ * @example [
11681
+ * "reports:read"
11682
+ * ]
11683
+ */
11684
+ readonly scopes: readonly string[];
11685
+ };
11035
11686
  readonly UserCreate: {
11036
11687
  /** Format: email */
11037
11688
  readonly email: string;
@@ -11056,6 +11707,8 @@ interface components {
11056
11707
  readonly identities: readonly {
11057
11708
  /** Format: date-time */
11058
11709
  readonly created_at: string;
11710
+ /** @description Their handle at the provider, without the @, as of their last sign-in with it: X’s, Discord’s, Telegram’s and Matrica’s username (AERO-2078). For showing people only: it changes, and never identifies anyone */
11711
+ readonly handle: string | null;
11059
11712
  /** @example idn_01h455vb4pex5vsknk084sn02q */
11060
11713
  readonly id: string;
11061
11714
  /** @description A wallet’s name, as its person called it (AERO-1397) */
@@ -11078,6 +11731,11 @@ interface components {
11078
11731
  readonly id: string;
11079
11732
  /** Format: date-time */
11080
11733
  readonly signed_in_at: string | null;
11734
+ /**
11735
+ * @description Where it was signed in: the brand's hosted page, or one of its apps' own screens
11736
+ * @enum {string}
11737
+ */
11738
+ readonly via: "hosted" | "app";
11081
11739
  }[];
11082
11740
  };
11083
11741
  readonly UserPage: {
@@ -11195,4 +11853,4 @@ interface components {
11195
11853
  }
11196
11854
  type operations = Record<string, never>;
11197
11855
  //#endregion
11198
- export { Problem as a, SynqError as c, ERROR_CODES as d, SynqErrorCode as f, OAuthErrorCode as i, isSynqError as l, operations as n, SynqAnyErrorCode as o, paths as r, SynqClientErrorCode as s, components as t, problemError as u };
11856
+ export { SignInFlowStep as A, browserLocalStorage as C, PasskeyRequestOptionsJSON as D, PasskeyAssertionJSON as E, ChallengeMethod as F, ChallengeProblem as I, DeniedReason as L, StartSignInOptions as M, CHALLENGE_METHODS as N, SavedSignIn as O, CHALLENGE_PROBLEMS as P, RedirectReason as R, SynqStorage as S, memoryStorage as T, SessionEvent as _, Problem as a, SynqClientOptions as b, SynqError as c, ERROR_CODES as d, SynqErrorCode as f, Session as g, ServerSignInOptions as h, OAuthErrorCode as i, SignInStepResult as j, SignInFlow as k, isSynqError as l, IdTokenClaims as m, operations as n, SynqAnyErrorCode as o, AuthorizationUrlOptions as p, paths as r, SynqClientErrorCode as s, components as t, problemError as u, SignOutOptions as v, browserSessionStorage as w, createSynqClient as x, SynqClient as y };