@k9o/gate 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +58 -19
- package/dist/{chunk-JEV37YEC.js → chunk-7EED6YWR.js} +1 -1
- package/dist/chunk-7EED6YWR.js.map +1 -0
- package/dist/{chunk-YINWOWJC.js → chunk-OJQYLAGZ.js} +42 -5
- package/dist/chunk-OJQYLAGZ.js.map +1 -0
- package/dist/{chunk-XP6U5QKF.js → chunk-RPE5N37W.js} +1 -1
- package/dist/chunk-RPE5N37W.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +149 -239
- package/dist/index.js +75 -113
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +25 -36
- package/dist/jwt.js +4 -2
- package/dist/mode.d.ts +6 -33
- package/dist/mode.js +1 -1
- package/dist/types.d.ts +63 -99
- package/package.json +1 -1
- package/src/api-host.ts +43 -0
- package/src/client-factory.ts +2 -5
- package/src/client.ts +3 -3
- package/src/errors.ts +1 -1
- package/src/gate.ts +62 -134
- package/src/idp-providers/identity-providers.ts +8 -11
- package/src/index.ts +10 -6
- package/src/jwt.ts +63 -45
- package/src/magic-link-redirects.ts +1 -2
- package/src/mode.ts +6 -33
- package/src/paths.ts +2 -6
- package/src/region.ts +24 -24
- package/src/types.ts +60 -94
- package/dist/chunk-JEV37YEC.js.map +0 -1
- package/dist/chunk-XP6U5QKF.js.map +0 -1
- package/dist/chunk-YINWOWJC.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { GateConfig } from './mode.js';
|
|
2
2
|
export { GateMode, configureGate, getGateConfig, resetGateConfig } from './mode.js';
|
|
3
|
-
import { Envelope, SignUpInput, AuthResult, SignInInput, SignOutInput, WhoamiResult, RefreshTokenInput,
|
|
3
|
+
import { Envelope, SignUpInput, AuthResult, SignInInput, SignOutInput, SignOutResult, WhoamiResult, RefreshTokenInput, RefreshResult, SignInCallbackResult, RequestOtpInput, RequestOtpResult, VerifyOtpInput, VerifyOtpResult, RequestMagicLinkInput, RequestMagicLinkResult, VerifyMagicLinkInput, VerifyMagicLinkResult, MagicLinkCallbackResult } from './types.js';
|
|
4
4
|
export { EndUser, GateJwtClaims, OtpPurpose, Session, VerifyJwtFailure, VerifyJwtResult } from './types.js';
|
|
5
|
-
export { VerifyJwtOptions, extractClaims, requireScope, verifyJwt } from './jwt.js';
|
|
5
|
+
export { VerifyJwtOptions, extractClaims, fetchJwks, requireScope, verifyJwt } from './jwt.js';
|
|
6
6
|
export { GateError, GateErrorCode, KErrorWire, gateErrorFromWire } from './errors.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* `gate.magicLinkRedirects` sub-namespace — the per-project magic-link
|
|
10
|
-
* redirect-URL allowlist, PROJECT_ADMIN+ managed.
|
|
11
|
-
* 2026-07-16): `list()`/`add()`/`remove()`, mirroring the
|
|
10
|
+
* redirect-URL allowlist, PROJECT_ADMIN+ managed. `list()`/`add()`/`remove()`, mirroring the
|
|
12
11
|
* `gate.idpProviders.*` sub-namespace convention (gate-admin surface,
|
|
13
12
|
* not the end-user-facing `passwordless.*` send/verify flow).
|
|
14
13
|
*
|
|
@@ -80,18 +79,15 @@ type RemoveMagicLinkRedirectResult = {
|
|
|
80
79
|
* `partnerToken` in config, or per-call via the client's bearer plumbing.
|
|
81
80
|
*
|
|
82
81
|
* 🔴 `clientSecretRef` is a RAW SECRET on the way in, not a reference.
|
|
83
|
-
*
|
|
82
|
+
* The gateway encrypts the submitted value (AES-256-GCM under
|
|
84
83
|
* `K9O_SECRET_STORAGE_MASTER_KEY`) into the opaque `client_secret_enc`
|
|
85
84
|
* handle and persists only that; the field name is retained for wire
|
|
86
|
-
* compatibility
|
|
87
|
-
* (`project-identity-providers.ts`, the v105 INSERT) is authoritative —
|
|
88
|
-
* there is NO Kibo custody on this path. Reads NEVER carry the secret or its
|
|
85
|
+
* compatibility and there is no Kibo custody on this path. Reads NEVER carry the secret or its
|
|
89
86
|
* handle: they carry only `clientSecretSet` (a presence boolean,
|
|
90
87
|
* the write-only fix). `serviceAccountKey` (directory sync) follows the same write-only rule.
|
|
91
88
|
*/
|
|
92
89
|
|
|
93
|
-
/** Closed end-user-class provider enum
|
|
94
|
-
* `_sys.end_user_idp_provider_configs`); the org plane carries the same 11 since v222/v223. */
|
|
90
|
+
/** Closed end-user-class provider enum (11 values). */
|
|
95
91
|
type IdpProviderKind = "google" | "github" | "microsoft" | "oidc" | "saml" | "scim" | "ldap" | "active_directory" | "apple" | "facebook" | "twitter";
|
|
96
92
|
/**
|
|
97
93
|
* The result of a provider connectivity test (`testProvider`). `ok` is the reachability + metadata-validity
|
|
@@ -104,7 +100,7 @@ type FederationTestProviderResult = {
|
|
|
104
100
|
ok: boolean;
|
|
105
101
|
reason?: "endpoint_unreachable" | "endpoint_forbidden" | "metadata_invalid" | "secret_not_provisioned" | "kind_unsupported";
|
|
106
102
|
};
|
|
107
|
-
/**
|
|
103
|
+
/** The provider→tier pairing is structurally fixed:
|
|
108
104
|
* `free_tier_ok ∈ {google, github, microsoft, apple, facebook, twitter}`,
|
|
109
105
|
* `ridge_plus ∈ {oidc, saml, scim, ldap, active_directory}`. The gateway rejects a mismatched
|
|
110
106
|
* pairing (`K9O_INVALID_FIELD_VALUE`); the SDK forwards the caller value
|
|
@@ -117,7 +113,7 @@ type IdpTierGating = "free_tier_ok" | "ridge_plus";
|
|
|
117
113
|
*
|
|
118
114
|
* Reads carry `clientSecretSet` (presence only), never the secret or a handle (see the module header).
|
|
119
115
|
* `metadataUrl`/`updatedAt` are `null` until set; `attributeMap` defaults to `{}`; `allowedRedirectUris`
|
|
120
|
-
* defaults to `[]`. The `directory*` fields are
|
|
116
|
+
* defaults to `[]`. The `directory*` fields are scheduled directory sync (Google only):
|
|
121
117
|
* settings plus the scheduler's last-run status; the service-account key itself is never returned.
|
|
122
118
|
*/
|
|
123
119
|
type IdpConfig = {
|
|
@@ -142,7 +138,7 @@ type IdpConfig = {
|
|
|
142
138
|
createdAt: string;
|
|
143
139
|
updatedAt: string | null;
|
|
144
140
|
};
|
|
145
|
-
/**
|
|
141
|
+
/** Directory-sync write fields — accepted on create AND update (one PROJECT_ADMIN surface). */
|
|
146
142
|
type DirectorySyncInput = {
|
|
147
143
|
/** Enabling requires `idpProvider: "google"`, a `serviceAccountKey` (now or already stored) and an interval. */
|
|
148
144
|
directoryPullEnabled?: boolean;
|
|
@@ -187,7 +183,7 @@ type CreateIdpInput = {
|
|
|
187
183
|
* `tierGating` are immutable after create. An empty patch returns the current config unchanged.
|
|
188
184
|
*
|
|
189
185
|
* 🔴 For `oidc`/`microsoft`, changing `metadataUrl` WITHOUT `clientSecretRef` in the same patch CLEARS the
|
|
190
|
-
* stored client secret (the gateway's secret-invalidate-on-host-change control
|
|
186
|
+
* stored client secret (the gateway's secret-invalidate-on-host-change control): re-supply the
|
|
191
187
|
* secret whenever the metadata URL changes.
|
|
192
188
|
*/
|
|
193
189
|
type PatchIdpInput = {
|
|
@@ -235,7 +231,7 @@ declare function makeIdentityProviders(getConfig: () => GateConfig): {
|
|
|
235
231
|
/**
|
|
236
232
|
* Update a provider config by id (PATCH; PROJECT_ADMIN, server-enforced). Partial: only supplied fields
|
|
237
233
|
* change; `idpProvider`/`tierGating` are immutable. Returns the updated config (secrets never included).
|
|
238
|
-
*
|
|
234
|
+
* Write directory-sync settings here and read their status back off `list()`. See `PatchIdpInput`
|
|
239
235
|
* for the metadataUrl-change-clears-secret rule.
|
|
240
236
|
*/
|
|
241
237
|
update(configId: string, patch: PatchIdpInput): Promise<Envelope<IdpConfig>>;
|
|
@@ -248,75 +244,43 @@ type IdentityProvidersNamespace = ReturnType<typeof makeIdentityProviders>;
|
|
|
248
244
|
* and `createGateClient(config)` (config supplied at construction time). */
|
|
249
245
|
declare function makeGateNamespace(getConfig: () => GateConfig): {
|
|
250
246
|
/**
|
|
251
|
-
* Create a new end-user
|
|
252
|
-
*
|
|
253
|
-
* `data.write.auth.signup` audit event.
|
|
247
|
+
* Create a new end-user for the configured project. Returns the new user and an initial session. The body is
|
|
248
|
+
* closed: `{ email, password, displayName? }`.
|
|
254
249
|
*
|
|
255
|
-
* Errors
|
|
250
|
+
* Errors:
|
|
256
251
|
* - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
|
|
257
|
-
* - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
|
|
252
|
+
* - `INVALID_FIELD_VALUE` (400) on a schema violation (password policy, malformed email, unknown field)
|
|
258
253
|
* - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
|
|
259
254
|
*/
|
|
260
255
|
signUp(input: SignUpInput): Promise<Envelope<AuthResult>>;
|
|
261
256
|
/**
|
|
262
|
-
* Sign in an existing end-user with email
|
|
263
|
-
* user record + a fresh session. Emits a `data.write.auth.signin`
|
|
264
|
-
* audit event.
|
|
257
|
+
* Sign in an existing end-user with email and password. Returns the user and a fresh session.
|
|
265
258
|
*
|
|
266
259
|
* Errors:
|
|
267
|
-
* - `INVALID_CREDENTIALS` (401) on bad email
|
|
268
|
-
* purpose — does not distinguish "user not found" vs "password
|
|
269
|
-
* wrong" to avoid enumeration)
|
|
260
|
+
* - `INVALID_CREDENTIALS` (401) on a bad email or password (the same answer for both, on purpose)
|
|
270
261
|
*/
|
|
271
262
|
signIn(input: SignInInput): Promise<Envelope<AuthResult>>;
|
|
272
263
|
/**
|
|
273
|
-
* Sign out
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* Errors:
|
|
277
|
-
* - `UNAUTHENTICATED` (401) when no session is present
|
|
278
|
-
* - `REFRESH_TOKEN_INVALID` (400) when the supplied refresh token
|
|
279
|
-
* doesn't match the bearer's session
|
|
264
|
+
* Sign out: revoke the session that the given refresh token belongs to. The route needs the refresh token in the
|
|
265
|
+
* body, so it is required here. Revoking an unknown or already-revoked token also answers `{ revoked: false }`.
|
|
280
266
|
*/
|
|
281
|
-
signOut(input
|
|
282
|
-
revoked: boolean;
|
|
283
|
-
}>>;
|
|
267
|
+
signOut(input: SignOutInput): Promise<Envelope<SignOutResult>>;
|
|
284
268
|
/**
|
|
285
|
-
* Return the
|
|
286
|
-
* Standalone mode where the SDK consumer wants to surface the user's
|
|
287
|
-
* roles / groups without parsing the JWT itself.
|
|
269
|
+
* Return the signed-in end-user (the bearer token's subject). The answer is the user record itself, not wrapped.
|
|
288
270
|
*
|
|
289
271
|
* Errors:
|
|
290
272
|
* - `UNAUTHENTICATED` (401) when no bearer is present
|
|
291
|
-
* - `SESSION_EXPIRED` (401) when the bearer
|
|
273
|
+
* - `SESSION_EXPIRED` (401) when the bearer has expired or was revoked
|
|
292
274
|
*/
|
|
293
275
|
whoami(): Promise<Envelope<WhoamiResult>>;
|
|
294
276
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* also rotate the refresh token per project policy).
|
|
277
|
+
* Exchange a refresh token for a new access token. The answer carries only the new `accessToken` and its
|
|
278
|
+
* `expiresAt`; the refresh token you hold stays valid and is not returned again.
|
|
298
279
|
*
|
|
299
280
|
* Errors:
|
|
300
|
-
* - `
|
|
301
|
-
* expired, or revoked
|
|
302
|
-
*/
|
|
303
|
-
refreshToken(input: RefreshTokenInput): Promise<Envelope<{
|
|
304
|
-
session: AuthResult["session"];
|
|
305
|
-
}>>;
|
|
306
|
-
/**
|
|
307
|
-
* Check whether the bearer's session is currently valid. Does NOT
|
|
308
|
-
* throw on expiry or invalid token — returns `{ valid: false }`
|
|
309
|
-
* inside the envelope. The intended use is health-checking a session
|
|
310
|
-
* without surfacing an error to the end-user (the SDK consumer
|
|
311
|
-
* typically wants to redirect to sign-in rather than surface a 401).
|
|
312
|
-
*/
|
|
313
|
-
verifySession(): Promise<Envelope<VerifySessionResult>>;
|
|
314
|
-
/**
|
|
315
|
-
* The project's hosted sign-in page URL and WebAuthn `rpId`. Unauthenticated and cacheable; no credential is sent,
|
|
316
|
-
* even when a partner token is configured. Take both values as given: an unknown org, a deleted project and a
|
|
317
|
-
* non-Gate project all answer the same `PROJECT_NOT_FOUND`, so a failure here says nothing about which.
|
|
281
|
+
* - `INVALID_CREDENTIALS` (401) when the refresh token is unknown, used or revoked
|
|
318
282
|
*/
|
|
319
|
-
|
|
283
|
+
refreshToken(input: RefreshTokenInput): Promise<Envelope<RefreshResult>>;
|
|
320
284
|
/**
|
|
321
285
|
* Initiate an external-IdP sign-in flow (Google / GitHub / Microsoft).
|
|
322
286
|
* The SDK POSTs to the gateway's authorize endpoint, receives the
|
|
@@ -335,8 +299,6 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
|
|
|
335
299
|
* });
|
|
336
300
|
* // …user lands at /auth/callback#access_token=…&refresh_token=…
|
|
337
301
|
*
|
|
338
|
-
* Generic OIDC requires authorization-endpoint discovery and is not
|
|
339
|
-
* yet wired; use a hosted provider (google/github/microsoft) for now.
|
|
340
302
|
*/
|
|
341
303
|
signInWithProvider(input: {
|
|
342
304
|
provider: "google" | "github" | "microsoft" | "oidc" | "saml";
|
|
@@ -354,74 +316,56 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
|
|
|
354
316
|
*/
|
|
355
317
|
parseSignInCallback(hash: string): SignInCallbackResult;
|
|
356
318
|
/**
|
|
357
|
-
* Passwordless
|
|
358
|
-
* for email-based authentication without a stored password.
|
|
319
|
+
* Passwordless sign-in: one-time passcode and magic link, both by email.
|
|
359
320
|
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
|
|
364
|
-
* canonical paths; the SDK ergonomic method names (`requestMagicLink`/
|
|
365
|
-
* `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
|
|
366
|
-
* (The SDK paths match the shipped gateway routes.)
|
|
321
|
+
* Routes: `POST /@<projectSlug>/gate/otp/{request,verify}` and `POST /@<projectSlug>/gate/magic-link/{send,redeem}`.
|
|
322
|
+
* The method names do not mirror the last path segment (`requestMagicLink` calls `send`, `verifyMagicLink` calls
|
|
323
|
+
* `redeem`).
|
|
367
324
|
*/
|
|
368
325
|
passwordless: {
|
|
369
326
|
/**
|
|
370
|
-
* Request a one-time passcode
|
|
371
|
-
* The
|
|
372
|
-
* single-use; redemption via `verifyOtp` mints a session.
|
|
327
|
+
* Request a one-time passcode for the email. It is short-lived and single-use; redeem it with `verifyOtp`.
|
|
328
|
+
* The answer carries the code's id and expiry (`data.otp`); the code itself goes by email.
|
|
373
329
|
*/
|
|
374
|
-
requestOtp(input: RequestOtpInput): Promise<Envelope<
|
|
375
|
-
requested: boolean;
|
|
376
|
-
}>>;
|
|
330
|
+
requestOtp(input: RequestOtpInput): Promise<Envelope<RequestOtpResult>>;
|
|
377
331
|
/**
|
|
378
|
-
* Verify a one-time passcode previously sent via `requestOtp`.
|
|
379
|
-
*
|
|
380
|
-
*
|
|
332
|
+
* Verify a one-time passcode previously sent via `requestOtp`. For the default `signin` purpose the answer
|
|
333
|
+
* carries a fresh `session`; for any other purpose it only confirms (`verified: true`) and the caller continues
|
|
334
|
+
* its own flow.
|
|
381
335
|
*
|
|
382
336
|
* Errors:
|
|
383
|
-
* - INVALID_CREDENTIALS (401) on wrong
|
|
384
|
-
* (ambiguous on purpose — enumeration defense)
|
|
337
|
+
* - `INVALID_CREDENTIALS` (401) on a wrong, expired or used code
|
|
385
338
|
*/
|
|
386
|
-
verifyOtp(input: VerifyOtpInput): Promise<Envelope<
|
|
339
|
+
verifyOtp(input: VerifyOtpInput): Promise<Envelope<VerifyOtpResult>>;
|
|
387
340
|
/**
|
|
388
|
-
* Request a magic
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* redirect URL with the token in the query string, which the
|
|
392
|
-
* customer's frontend then exchanges via `verifyMagicLink`.
|
|
341
|
+
* Request a magic link by email. The link carries an opaque short-lived token and lands at the project's
|
|
342
|
+
* redirect URL with the token in the query string; exchange it with `verifyMagicLink`. The answer is
|
|
343
|
+
* `data.magicLink` with `sent` and the expiry.
|
|
393
344
|
*/
|
|
394
|
-
requestMagicLink(input: RequestMagicLinkInput): Promise<Envelope<
|
|
395
|
-
requested: boolean;
|
|
396
|
-
}>>;
|
|
345
|
+
requestMagicLink(input: RequestMagicLinkInput): Promise<Envelope<RequestMagicLinkResult>>;
|
|
397
346
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
* shape as `signIn`.
|
|
347
|
+
* Redeem a magic-link token previously delivered via `requestMagicLink`. The answer carries the new `session`
|
|
348
|
+
* and the `endUserId` (not the full user record; call `whoami` for that).
|
|
401
349
|
*
|
|
402
350
|
* Errors:
|
|
403
|
-
* - INVALID_CREDENTIALS (401) on wrong
|
|
404
|
-
* token (same enumeration-defense rationale as verifyOtp)
|
|
351
|
+
* - `INVALID_CREDENTIALS` (401) on a wrong, expired or used token
|
|
405
352
|
*/
|
|
406
|
-
verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<
|
|
353
|
+
verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<VerifyMagicLinkResult>>;
|
|
407
354
|
/**
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
355
|
+
* Extract the opaque magic-link token from the landing-page URL's query
|
|
356
|
+
* string (the gateway redirects to `<configured-redirect>?token=…&project=…`).
|
|
357
|
+
* Pure + side-effect-free — pass `window.location.search`. Feed the
|
|
358
|
+
* returned `token` to `verifyMagicLink({ token })` to mint the session.
|
|
359
|
+
*
|
|
360
|
+
* This is the magic-link counterpart to `parseSignInCallback` (which parses
|
|
361
|
+
* the SSO *hash fragment* for session tokens) — magic-link delivers an
|
|
362
|
+
* opaque token in the *query string*, so the two are not interchangeable.
|
|
363
|
+
*/
|
|
417
364
|
parseMagicLinkCallback(search: string): MagicLinkCallbackResult;
|
|
418
365
|
};
|
|
419
366
|
/**
|
|
420
|
-
* Project-scoped end-user IdP provider-config management.
|
|
421
|
-
* PROJECT_ADMIN+ required; end-user JWTs rejected at the route.
|
|
422
|
-
* `create()` is bound — the REST create the Gate Studio
|
|
423
|
-
* `FederationPresetsPanel` routes to when it drops the `gate_flows`
|
|
424
|
-
* `__addFromPreset` sentinel. Gate-admin surface.
|
|
367
|
+
* Project-scoped end-user IdP provider-config management (create, list, update, test).
|
|
368
|
+
* PROJECT_ADMIN+ required; end-user JWTs are rejected at the route.
|
|
425
369
|
*/
|
|
426
370
|
identityProviders: {
|
|
427
371
|
create(input: CreateIdpInput): Promise<Envelope<IdpConfig>>;
|
|
@@ -430,10 +374,8 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
|
|
|
430
374
|
testProvider(configId: string): Promise<Envelope<FederationTestProviderResult>>;
|
|
431
375
|
};
|
|
432
376
|
/**
|
|
433
|
-
* Per-project magic-link redirect-URL allowlist management (
|
|
434
|
-
*
|
|
435
|
-
* — PROJECT_ADMIN+ required, distinct from the end-user-facing
|
|
436
|
-
* `passwordless.requestMagicLink`/`verifyMagicLink` send/verify flow.
|
|
377
|
+
* Per-project magic-link redirect-URL allowlist management (list, add, remove).
|
|
378
|
+
* PROJECT_ADMIN+ required; distinct from the end-user `passwordless` flow.
|
|
437
379
|
*/
|
|
438
380
|
magicLinkRedirects: {
|
|
439
381
|
list(): Promise<Envelope<{
|
|
@@ -449,75 +391,43 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
|
|
|
449
391
|
* Per-instance clients: `createGateClient(config)`. */
|
|
450
392
|
declare const gate: {
|
|
451
393
|
/**
|
|
452
|
-
* Create a new end-user
|
|
453
|
-
*
|
|
454
|
-
* `data.write.auth.signup` audit event.
|
|
394
|
+
* Create a new end-user for the configured project. Returns the new user and an initial session. The body is
|
|
395
|
+
* closed: `{ email, password, displayName? }`.
|
|
455
396
|
*
|
|
456
|
-
* Errors
|
|
397
|
+
* Errors:
|
|
457
398
|
* - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
|
|
458
|
-
* - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
|
|
399
|
+
* - `INVALID_FIELD_VALUE` (400) on a schema violation (password policy, malformed email, unknown field)
|
|
459
400
|
* - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
|
|
460
401
|
*/
|
|
461
402
|
signUp(input: SignUpInput): Promise<Envelope<AuthResult>>;
|
|
462
403
|
/**
|
|
463
|
-
* Sign in an existing end-user with email
|
|
464
|
-
* user record + a fresh session. Emits a `data.write.auth.signin`
|
|
465
|
-
* audit event.
|
|
404
|
+
* Sign in an existing end-user with email and password. Returns the user and a fresh session.
|
|
466
405
|
*
|
|
467
406
|
* Errors:
|
|
468
|
-
* - `INVALID_CREDENTIALS` (401) on bad email
|
|
469
|
-
* purpose — does not distinguish "user not found" vs "password
|
|
470
|
-
* wrong" to avoid enumeration)
|
|
407
|
+
* - `INVALID_CREDENTIALS` (401) on a bad email or password (the same answer for both, on purpose)
|
|
471
408
|
*/
|
|
472
409
|
signIn(input: SignInInput): Promise<Envelope<AuthResult>>;
|
|
473
410
|
/**
|
|
474
|
-
* Sign out
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
* Errors:
|
|
478
|
-
* - `UNAUTHENTICATED` (401) when no session is present
|
|
479
|
-
* - `REFRESH_TOKEN_INVALID` (400) when the supplied refresh token
|
|
480
|
-
* doesn't match the bearer's session
|
|
411
|
+
* Sign out: revoke the session that the given refresh token belongs to. The route needs the refresh token in the
|
|
412
|
+
* body, so it is required here. Revoking an unknown or already-revoked token also answers `{ revoked: false }`.
|
|
481
413
|
*/
|
|
482
|
-
signOut(input
|
|
483
|
-
revoked: boolean;
|
|
484
|
-
}>>;
|
|
414
|
+
signOut(input: SignOutInput): Promise<Envelope<SignOutResult>>;
|
|
485
415
|
/**
|
|
486
|
-
* Return the
|
|
487
|
-
* Standalone mode where the SDK consumer wants to surface the user's
|
|
488
|
-
* roles / groups without parsing the JWT itself.
|
|
416
|
+
* Return the signed-in end-user (the bearer token's subject). The answer is the user record itself, not wrapped.
|
|
489
417
|
*
|
|
490
418
|
* Errors:
|
|
491
419
|
* - `UNAUTHENTICATED` (401) when no bearer is present
|
|
492
|
-
* - `SESSION_EXPIRED` (401) when the bearer
|
|
420
|
+
* - `SESSION_EXPIRED` (401) when the bearer has expired or was revoked
|
|
493
421
|
*/
|
|
494
422
|
whoami(): Promise<Envelope<WhoamiResult>>;
|
|
495
423
|
/**
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
* also rotate the refresh token per project policy).
|
|
424
|
+
* Exchange a refresh token for a new access token. The answer carries only the new `accessToken` and its
|
|
425
|
+
* `expiresAt`; the refresh token you hold stays valid and is not returned again.
|
|
499
426
|
*
|
|
500
427
|
* Errors:
|
|
501
|
-
* - `
|
|
502
|
-
* expired, or revoked
|
|
428
|
+
* - `INVALID_CREDENTIALS` (401) when the refresh token is unknown, used or revoked
|
|
503
429
|
*/
|
|
504
|
-
refreshToken(input: RefreshTokenInput): Promise<Envelope<
|
|
505
|
-
session: AuthResult["session"];
|
|
506
|
-
}>>;
|
|
507
|
-
/**
|
|
508
|
-
* Check whether the bearer's session is currently valid. Does NOT
|
|
509
|
-
* throw on expiry or invalid token — returns `{ valid: false }`
|
|
510
|
-
* inside the envelope. The intended use is health-checking a session
|
|
511
|
-
* without surfacing an error to the end-user (the SDK consumer
|
|
512
|
-
* typically wants to redirect to sign-in rather than surface a 401).
|
|
513
|
-
*/
|
|
514
|
-
verifySession(): Promise<Envelope<VerifySessionResult>>;
|
|
515
|
-
/**
|
|
516
|
-
* The project's hosted sign-in page URL and WebAuthn `rpId`. Unauthenticated and cacheable; no credential is sent,
|
|
517
|
-
* even when a partner token is configured. Take both values as given: an unknown org, a deleted project and a
|
|
518
|
-
* non-Gate project all answer the same `PROJECT_NOT_FOUND`, so a failure here says nothing about which.
|
|
519
|
-
*/
|
|
520
|
-
hosted(): Promise<Envelope<HostedGateInfo>>;
|
|
430
|
+
refreshToken(input: RefreshTokenInput): Promise<Envelope<RefreshResult>>;
|
|
521
431
|
/**
|
|
522
432
|
* Initiate an external-IdP sign-in flow (Google / GitHub / Microsoft).
|
|
523
433
|
* The SDK POSTs to the gateway's authorize endpoint, receives the
|
|
@@ -536,8 +446,6 @@ declare const gate: {
|
|
|
536
446
|
* });
|
|
537
447
|
* // …user lands at /auth/callback#access_token=…&refresh_token=…
|
|
538
448
|
*
|
|
539
|
-
* Generic OIDC requires authorization-endpoint discovery and is not
|
|
540
|
-
* yet wired; use a hosted provider (google/github/microsoft) for now.
|
|
541
449
|
*/
|
|
542
450
|
signInWithProvider(input: {
|
|
543
451
|
provider: "google" | "github" | "microsoft" | "oidc" | "saml";
|
|
@@ -555,74 +463,56 @@ declare const gate: {
|
|
|
555
463
|
*/
|
|
556
464
|
parseSignInCallback(hash: string): SignInCallbackResult;
|
|
557
465
|
/**
|
|
558
|
-
* Passwordless
|
|
559
|
-
* for email-based authentication without a stored password.
|
|
466
|
+
* Passwordless sign-in: one-time passcode and magic link, both by email.
|
|
560
467
|
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
* {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
|
|
565
|
-
* canonical paths; the SDK ergonomic method names (`requestMagicLink`/
|
|
566
|
-
* `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
|
|
567
|
-
* (The SDK paths match the shipped gateway routes.)
|
|
468
|
+
* Routes: `POST /@<projectSlug>/gate/otp/{request,verify}` and `POST /@<projectSlug>/gate/magic-link/{send,redeem}`.
|
|
469
|
+
* The method names do not mirror the last path segment (`requestMagicLink` calls `send`, `verifyMagicLink` calls
|
|
470
|
+
* `redeem`).
|
|
568
471
|
*/
|
|
569
472
|
passwordless: {
|
|
570
473
|
/**
|
|
571
|
-
* Request a one-time passcode
|
|
572
|
-
* The
|
|
573
|
-
* single-use; redemption via `verifyOtp` mints a session.
|
|
474
|
+
* Request a one-time passcode for the email. It is short-lived and single-use; redeem it with `verifyOtp`.
|
|
475
|
+
* The answer carries the code's id and expiry (`data.otp`); the code itself goes by email.
|
|
574
476
|
*/
|
|
575
|
-
requestOtp(input: RequestOtpInput): Promise<Envelope<
|
|
576
|
-
requested: boolean;
|
|
577
|
-
}>>;
|
|
477
|
+
requestOtp(input: RequestOtpInput): Promise<Envelope<RequestOtpResult>>;
|
|
578
478
|
/**
|
|
579
|
-
* Verify a one-time passcode previously sent via `requestOtp`.
|
|
580
|
-
*
|
|
581
|
-
*
|
|
479
|
+
* Verify a one-time passcode previously sent via `requestOtp`. For the default `signin` purpose the answer
|
|
480
|
+
* carries a fresh `session`; for any other purpose it only confirms (`verified: true`) and the caller continues
|
|
481
|
+
* its own flow.
|
|
582
482
|
*
|
|
583
483
|
* Errors:
|
|
584
|
-
* - INVALID_CREDENTIALS (401) on wrong
|
|
585
|
-
* (ambiguous on purpose — enumeration defense)
|
|
484
|
+
* - `INVALID_CREDENTIALS` (401) on a wrong, expired or used code
|
|
586
485
|
*/
|
|
587
|
-
verifyOtp(input: VerifyOtpInput): Promise<Envelope<
|
|
486
|
+
verifyOtp(input: VerifyOtpInput): Promise<Envelope<VerifyOtpResult>>;
|
|
588
487
|
/**
|
|
589
|
-
* Request a magic
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
* redirect URL with the token in the query string, which the
|
|
593
|
-
* customer's frontend then exchanges via `verifyMagicLink`.
|
|
488
|
+
* Request a magic link by email. The link carries an opaque short-lived token and lands at the project's
|
|
489
|
+
* redirect URL with the token in the query string; exchange it with `verifyMagicLink`. The answer is
|
|
490
|
+
* `data.magicLink` with `sent` and the expiry.
|
|
594
491
|
*/
|
|
595
|
-
requestMagicLink(input: RequestMagicLinkInput): Promise<Envelope<
|
|
596
|
-
requested: boolean;
|
|
597
|
-
}>>;
|
|
492
|
+
requestMagicLink(input: RequestMagicLinkInput): Promise<Envelope<RequestMagicLinkResult>>;
|
|
598
493
|
/**
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
* shape as `signIn`.
|
|
494
|
+
* Redeem a magic-link token previously delivered via `requestMagicLink`. The answer carries the new `session`
|
|
495
|
+
* and the `endUserId` (not the full user record; call `whoami` for that).
|
|
602
496
|
*
|
|
603
497
|
* Errors:
|
|
604
|
-
* - INVALID_CREDENTIALS (401) on wrong
|
|
605
|
-
* token (same enumeration-defense rationale as verifyOtp)
|
|
498
|
+
* - `INVALID_CREDENTIALS` (401) on a wrong, expired or used token
|
|
606
499
|
*/
|
|
607
|
-
verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<
|
|
500
|
+
verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<VerifyMagicLinkResult>>;
|
|
608
501
|
/**
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
502
|
+
* Extract the opaque magic-link token from the landing-page URL's query
|
|
503
|
+
* string (the gateway redirects to `<configured-redirect>?token=…&project=…`).
|
|
504
|
+
* Pure + side-effect-free — pass `window.location.search`. Feed the
|
|
505
|
+
* returned `token` to `verifyMagicLink({ token })` to mint the session.
|
|
506
|
+
*
|
|
507
|
+
* This is the magic-link counterpart to `parseSignInCallback` (which parses
|
|
508
|
+
* the SSO *hash fragment* for session tokens) — magic-link delivers an
|
|
509
|
+
* opaque token in the *query string*, so the two are not interchangeable.
|
|
510
|
+
*/
|
|
618
511
|
parseMagicLinkCallback(search: string): MagicLinkCallbackResult;
|
|
619
512
|
};
|
|
620
513
|
/**
|
|
621
|
-
* Project-scoped end-user IdP provider-config management.
|
|
622
|
-
* PROJECT_ADMIN+ required; end-user JWTs rejected at the route.
|
|
623
|
-
* `create()` is bound — the REST create the Gate Studio
|
|
624
|
-
* `FederationPresetsPanel` routes to when it drops the `gate_flows`
|
|
625
|
-
* `__addFromPreset` sentinel. Gate-admin surface.
|
|
514
|
+
* Project-scoped end-user IdP provider-config management (create, list, update, test).
|
|
515
|
+
* PROJECT_ADMIN+ required; end-user JWTs are rejected at the route.
|
|
626
516
|
*/
|
|
627
517
|
identityProviders: {
|
|
628
518
|
create(input: CreateIdpInput): Promise<Envelope<IdpConfig>>;
|
|
@@ -631,10 +521,8 @@ declare const gate: {
|
|
|
631
521
|
testProvider(configId: string): Promise<Envelope<FederationTestProviderResult>>;
|
|
632
522
|
};
|
|
633
523
|
/**
|
|
634
|
-
* Per-project magic-link redirect-URL allowlist management (
|
|
635
|
-
*
|
|
636
|
-
* — PROJECT_ADMIN+ required, distinct from the end-user-facing
|
|
637
|
-
* `passwordless.requestMagicLink`/`verifyMagicLink` send/verify flow.
|
|
524
|
+
* Per-project magic-link redirect-URL allowlist management (list, add, remove).
|
|
525
|
+
* PROJECT_ADMIN+ required; distinct from the end-user `passwordless` flow.
|
|
638
526
|
*/
|
|
639
527
|
magicLinkRedirects: {
|
|
640
528
|
list(): Promise<Envelope<{
|
|
@@ -661,11 +549,8 @@ type GateNamespace = ReturnType<typeof makeGateNamespace>;
|
|
|
661
549
|
* incoming request's resolved tenant) or for apps that maintain both a
|
|
662
550
|
* Standalone and Native binding against the same SDK instance.
|
|
663
551
|
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
* project addressing form — project ids are internal-only
|
|
667
|
-
* (`feedback_slug_addressing_v2_5_68`); the retired `/v2/projects/...`
|
|
668
|
-
* mounts were dropped at pass17 (`feedback_v2_path_is_an_error`).
|
|
552
|
+
* Supply `projectSlug` for
|
|
553
|
+
* `/@<slug>/gate/...` routing. The slug is the only project addressing form.
|
|
669
554
|
*
|
|
670
555
|
* Example:
|
|
671
556
|
* const client = createGateClient({
|
|
@@ -683,26 +568,51 @@ type GateClient = ReturnType<typeof makeGateNamespace>;
|
|
|
683
568
|
* re-read process-level state. Throws `TypeError` on invalid config. */
|
|
684
569
|
declare function createGateClient(config: GateConfig): GateClient;
|
|
685
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Reads the structure of a gateway API host without knowing its apex.
|
|
573
|
+
*
|
|
574
|
+
* The gateway label is `api` or `api-staging`, optionally followed by `-<rgn>` (a 4-character region code). The label
|
|
575
|
+
* before it is the org slug and whatever follows it is the apex, which is never matched against a list: a public-cloud,
|
|
576
|
+
* internal-apex, air-gapped or localhost deployment all work.
|
|
577
|
+
*/
|
|
578
|
+
/** The structure of a gateway API host, read from the host itself. */
|
|
579
|
+
type ApiHost = {
|
|
580
|
+
/** First label of an org host (`acme` in `acme.api.corp.internal`); null for a platform host (`api.corp.internal`). */
|
|
581
|
+
orgSlug: string | null;
|
|
582
|
+
/** `api` or `api-staging`: the label that marks a gateway host. */
|
|
583
|
+
family: "api" | "api-staging";
|
|
584
|
+
/** 4-character region code when the host is regional (`api-de01`), else null (region-free). */
|
|
585
|
+
region: string | null;
|
|
586
|
+
/** Everything after the gateway label (`corp.internal`, `k9o.io`). */
|
|
587
|
+
apex: string;
|
|
588
|
+
};
|
|
589
|
+
/**
|
|
590
|
+
* Returns null for any other shape (localhost, an IP, a single-label host, a host with no gateway label), which callers
|
|
591
|
+
* use exactly as given.
|
|
592
|
+
*/
|
|
593
|
+
declare function parseApiHost(host: string): ApiHost | null;
|
|
594
|
+
|
|
686
595
|
/**
|
|
687
596
|
* Regional host resolution.
|
|
688
597
|
*
|
|
689
|
-
*
|
|
690
|
-
* body, an `Authorization` header or a cookie (`400 K9O_INVALID_REQUEST_SHAPE`,
|
|
691
|
-
* so credentials must go to `<org>.api
|
|
692
|
-
* `HEAD
|
|
598
|
+
* A region-free org host `<org>.api.<rest>` (or `<org>.api-staging.<rest>`) redirects GET/HEAD only and REFUSES a
|
|
599
|
+
* request that carries a body, an `Authorization` header or a cookie (`400 K9O_INVALID_REQUEST_SHAPE`,
|
|
600
|
+
* `region_free_host_takes_no_credentials`), so credentials must go to `<org>.api-<rgn>.<rest>`. The client learns the
|
|
601
|
+
* region with ONE unauthenticated `HEAD` to the configured host, takes the `308` `Location`, and caches the regional
|
|
602
|
+
* origin.
|
|
693
603
|
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
604
|
+
* Nothing here knows an apex. The host comes from the configured baseUrl; `<rest>` is whatever follows the second
|
|
605
|
+
* label, so a public-cloud, internal-apex, air-gapped or localhost deployment all work. Properties:
|
|
606
|
+
* - Only a host shaped `<org>.api.<rest>` / `<org>.api-staging.<rest>` is probed. A regional, single-label, IP,
|
|
607
|
+
* localhost or any other baseUrl is used exactly as given.
|
|
697
608
|
* - The probe carries NO credential and none of the configured default headers (they may hold one).
|
|
698
|
-
* - The `Location` is trusted only if it is `https`, has no userinfo/port, and its host is exactly the SAME org
|
|
699
|
-
*
|
|
700
|
-
* so a hostile redirect can never receive a credential.
|
|
701
|
-
* - Not a redirect
|
|
702
|
-
*
|
|
703
|
-
* life of the client's config; a network failure is not cached.
|
|
609
|
+
* - The `Location` is trusted only if it is `https`, has no userinfo/port, and its host is exactly the SAME org and
|
|
610
|
+
* SAME remainder with a 4-character region code appended to the second label. Anything else is ignored and the
|
|
611
|
+
* configured baseUrl stays, so a hostile redirect can never receive a credential.
|
|
612
|
+
* - Not a redirect, or any failure: the configured baseUrl stays and the server's own refusal, if any, is what the
|
|
613
|
+
* caller sees. A definite answer is cached for the life of the client's config; a network failure is not cached.
|
|
704
614
|
*/
|
|
705
|
-
/** The slice of a client config this module needs
|
|
615
|
+
/** The slice of a client config this module needs. */
|
|
706
616
|
type RegionConfig = {
|
|
707
617
|
baseUrl: string;
|
|
708
618
|
fetch?: typeof globalThis.fetch | undefined;
|
|
@@ -712,4 +622,4 @@ declare function regionalOriginFromLocation(host: string, location: string, base
|
|
|
712
622
|
/** The origin requests for this client go to: the cached regional origin once learned, else the configured baseUrl. */
|
|
713
623
|
declare function resolveRegionalOrigin(config: RegionConfig): Promise<string>;
|
|
714
624
|
|
|
715
|
-
export { AuthResult, type CreateIdpInput, type DirectorySyncInput, Envelope, type GateClient, GateConfig, type GateNamespace,
|
|
625
|
+
export { type ApiHost, AuthResult, type CreateIdpInput, type DirectorySyncInput, Envelope, type GateClient, GateConfig, type GateNamespace, type IdentityProvidersNamespace, type IdpConfig, type IdpProviderKind, type IdpTierGating, MagicLinkCallbackResult, type PatchIdpInput, RefreshResult, RefreshTokenInput, type RegionConfig, RequestMagicLinkInput, RequestMagicLinkResult, RequestOtpInput, RequestOtpResult, SignInCallbackResult, SignInInput, SignOutInput, SignOutResult, SignUpInput, VerifyMagicLinkInput, VerifyMagicLinkResult, VerifyOtpInput, VerifyOtpResult, WhoamiResult, createGateClient, gate, parseApiHost, regionalOriginFromLocation, resolveRegionalOrigin };
|