@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/jwt.d.ts
CHANGED
|
@@ -1,58 +1,39 @@
|
|
|
1
1
|
import { GateJwtClaims, VerifyJwtResult } from './types.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* JWT verification primitives for
|
|
5
|
-
*
|
|
6
|
-
* Standalone-mode customers consume Gate-issued JWTs in their own
|
|
7
|
-
* application code without needing the full SDK shape — they just need
|
|
8
|
-
* to verify the signature, extract claims, and (optionally) check
|
|
9
|
-
* scopes. This module is the canonical entry for that surface.
|
|
10
|
-
*
|
|
11
|
-
* Backed by `jose` (already a peer dependency in the gateway via the
|
|
12
|
-
* same library; chose `jose` for consistency + JWK fetch support out
|
|
13
|
-
* of the box).
|
|
14
|
-
*
|
|
15
|
-
* Native-mode customers don't typically reach for this directly — the
|
|
16
|
-
* gateway verifies project-issued tokens on every request and rehydrates
|
|
17
|
-
* `subject.*` claims. But the same primitives are
|
|
18
|
-
* portable: Native customers running their own services downstream of
|
|
19
|
-
* the gateway can verify Gate-issued JWTs identically.
|
|
4
|
+
* JWT verification primitives for apps that consume Gate-issued tokens without the rest of the SDK: verify the
|
|
5
|
+
* signature, read the claims, check scopes. Backed by `jose`.
|
|
20
6
|
*/
|
|
21
7
|
|
|
22
8
|
/**
|
|
23
|
-
* Configuration for `verifyJwt`.
|
|
24
|
-
*
|
|
25
|
-
* an inline key set (for tests + air-gapped deployments).
|
|
9
|
+
* Configuration for `verifyJwt`. Signing keys come from either the gateway's JWKS route (`jwksUrl`) or an inline key
|
|
10
|
+
* set (`jwks`, for tests and air-gapped deployments).
|
|
26
11
|
*/
|
|
27
12
|
type VerifyJwtOptions = {
|
|
28
13
|
token: string;
|
|
29
14
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
15
|
+
* URL of the gateway's JWKS route: `https://<org>.api.<apex>/@<projectSlug>/auth/jwks` for a project's end-user
|
|
16
|
+
* tokens (`/auth/jwks` on the org host for org keys). The gateway wraps the key set in its response envelope
|
|
17
|
+
* (`{ data: { keys }, meta }`), which `jose` cannot read directly; this helper fetches the URL and unwraps it.
|
|
18
|
+
* Either `jwksUrl` or `jwks` must be set; not both.
|
|
32
19
|
*/
|
|
33
20
|
jwksUrl?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Inline JWK set — used by tests + integration scenarios that want to
|
|
36
|
-
* avoid the network hop. The shape is the standard JWKS `{keys: [...]}`
|
|
37
|
-
* envelope; pass through unchanged to `jose`.
|
|
38
|
-
*/
|
|
21
|
+
/** Inline JWK set, the standard `{ keys: [...] }` object. */
|
|
39
22
|
jwks?: {
|
|
40
23
|
keys: ReadonlyArray<Record<string, unknown>>;
|
|
41
24
|
};
|
|
25
|
+
/** Fetch implementation used for `jwksUrl`. Defaults to the global `fetch`. */
|
|
26
|
+
fetch?: typeof globalThis.fetch;
|
|
42
27
|
/** Expected issuer (`iss` claim). Strict comparison. */
|
|
43
28
|
expectedIssuer?: string;
|
|
44
|
-
/** Expected audience (`aud` claim).
|
|
29
|
+
/** Expected audience (`aud` claim). */
|
|
45
30
|
expectedAudience?: string;
|
|
46
31
|
/**
|
|
47
|
-
* Allowed signing algorithms. Defaults to `["RS256", "ES256"]
|
|
48
|
-
*
|
|
49
|
-
* (defense against alg-confusion attacks).
|
|
32
|
+
* Allowed signing algorithms. Defaults to `["RS256", "ES256"]`. Anything outside this set is rejected (defense
|
|
33
|
+
* against algorithm confusion).
|
|
50
34
|
*/
|
|
51
35
|
allowedAlgorithms?: ReadonlyArray<string>;
|
|
52
|
-
/**
|
|
53
|
-
* Clock-skew tolerance in seconds. Defaults to 30. Applies to both
|
|
54
|
-
* `exp` and `nbf` checks.
|
|
55
|
-
*/
|
|
36
|
+
/** Clock-skew tolerance in seconds. Defaults to 30. Applies to `exp` and `nbf`. */
|
|
56
37
|
clockToleranceSeconds?: number;
|
|
57
38
|
};
|
|
58
39
|
/**
|
|
@@ -61,7 +42,7 @@ type VerifyJwtOptions = {
|
|
|
61
42
|
*
|
|
62
43
|
* Throws `GateError` only when:
|
|
63
44
|
* - `jwksUrl` and `jwks` are both unset or both set
|
|
64
|
-
* - the JWKS fetch fails
|
|
45
|
+
* - the JWKS fetch fails (UPSTREAM_UNAVAILABLE) or answers something that is not a key set
|
|
65
46
|
*/
|
|
66
47
|
declare function verifyJwt(opts: VerifyJwtOptions): Promise<VerifyJwtResult>;
|
|
67
48
|
/**
|
|
@@ -80,5 +61,13 @@ declare function extractClaims(token: string): GateJwtClaims;
|
|
|
80
61
|
* Returns nothing on success — caller uses the throw-or-pass shape.
|
|
81
62
|
*/
|
|
82
63
|
declare function requireScope(claims: GateJwtClaims, requiredScopes: ReadonlyArray<string>): void;
|
|
64
|
+
type Jwks = {
|
|
65
|
+
keys: ReadonlyArray<Record<string, unknown>>;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Fetch a JWKS from the gateway. The route answers `{ data: { keys }, meta }`; a bare `{ keys }` (any other JWKS
|
|
69
|
+
* host) is accepted too.
|
|
70
|
+
*/
|
|
71
|
+
declare function fetchJwks(url: string, fetchImpl?: typeof globalThis.fetch | undefined): Promise<Jwks>;
|
|
83
72
|
|
|
84
|
-
export { type VerifyJwtOptions, extractClaims, requireScope, verifyJwt };
|
|
73
|
+
export { type VerifyJwtOptions, extractClaims, fetchJwks, requireScope, verifyJwt };
|
package/dist/jwt.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractClaims,
|
|
3
|
+
fetchJwks,
|
|
3
4
|
requireScope,
|
|
4
5
|
verifyJwt
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-OJQYLAGZ.js";
|
|
7
|
+
import "./chunk-7EED6YWR.js";
|
|
7
8
|
export {
|
|
8
9
|
extractClaims,
|
|
10
|
+
fetchJwks,
|
|
9
11
|
requireScope,
|
|
10
12
|
verifyJwt
|
|
11
13
|
};
|
package/dist/mode.d.ts
CHANGED
|
@@ -1,37 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Standalone vs Native mode wiring.
|
|
3
3
|
*
|
|
4
|
-
* The SDK surface is identical across modes; mode
|
|
5
|
-
*
|
|
6
|
-
* not SDK ergonomics. The mode boundary is a deployment-time decision
|
|
7
|
-
*; the SDK does not branch behavior at function-call time.
|
|
4
|
+
* The SDK surface is identical across modes; the mode only tells the gateway how to route consumption. The SDK
|
|
5
|
+
* records it on every request in the `X-Gate-Mode` header and does not branch on it at call time.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - **Billing-identity routing** (commission callout):
|
|
12
|
-
* Standalone customers are billed under the partner-credentialled
|
|
13
|
-
* contract (Gate as Auth0/Clerk/Okta alternative — Trojan Horse
|
|
14
|
-
* motion); Native customers compose with the broader Kilimanjaro
|
|
15
|
-
* billing model. The SDK records the mode on every wire request via
|
|
16
|
-
* the `X-Gate-Mode` header so the gateway routes consumption to the
|
|
17
|
-
* correct billing surface without per-function branching.
|
|
18
|
-
* - **Auto-scoping expectations** in marketing copy + docs hub copy
|
|
19
|
-
* (per memory `feedback_gate_iak_brand_architecture.md` marketing-
|
|
20
|
-
* honesty discipline). Standalone mode delivers Sovereign Ingress +
|
|
21
|
-
* Hardware-Attested Identity + Forensic Attribution + Unlimited MAUs;
|
|
22
|
-
* Native mode adds Automatic Identity-Aware Scoping on top.
|
|
23
|
-
*
|
|
24
|
-
* What mode does NOT affect:
|
|
25
|
-
*
|
|
26
|
-
* - Function names + signatures (identical across modes)
|
|
27
|
-
* - Wire-envelope shape (identical)
|
|
28
|
-
* - Error taxonomy (identical)
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* Two-mode discriminant. The third candidate "Hybrid" was rejected per
|
|
32
|
-
* memory `feedback_gate_iak_brand_architecture.md` — modes are
|
|
33
|
-
* orthogonal deployment shapes, not interleaved.
|
|
7
|
+
* Function names, signatures, response envelope and error taxonomy are the same in both modes.
|
|
34
8
|
*/
|
|
9
|
+
/** Two orthogonal deployment shapes; they are not interleaved. */
|
|
35
10
|
type GateMode = "standalone" | "native";
|
|
36
11
|
/**
|
|
37
12
|
* Gate SDK configuration. Returned by `getGateConfig()` and consumed
|
|
@@ -40,11 +15,9 @@ type GateMode = "standalone" | "native";
|
|
|
40
15
|
*/
|
|
41
16
|
type GateConfig = {
|
|
42
17
|
mode: GateMode;
|
|
43
|
-
/** Project slug
|
|
44
|
-
* The only customer-facing project addressing form; routes resolve to
|
|
45
|
-
* `/@<slug>/gate/...` (`feedback_slug_addressing_v2_5_68`). Required. */
|
|
18
|
+
/** Project slug (e.g. `partner-console-staging`). Routes resolve to `/@<slug>/gate/...`. Required. */
|
|
46
19
|
projectSlug: string;
|
|
47
|
-
/** Gateway base URL
|
|
20
|
+
/** Gateway base URL, e.g. `https://<orgSlug>.api.<apex>` */
|
|
48
21
|
baseUrl: string;
|
|
49
22
|
/**
|
|
50
23
|
* Optional partner-credentialled JWT for Standalone mode — used as the
|
package/dist/mode.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,32 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@k9o/gate`
|
|
3
|
-
*
|
|
4
|
-
* The shapes here track verbatim. End-user IDs carry the `eu_`
|
|
5
|
-
* prefix; role and group identifiers compose against the
|
|
6
|
-
* role lattice (Trust-Zone-separate `PROJECT_END_USER` for project-
|
|
7
|
-
* level end-user identities). Sessions, passkeys, MFA factors, federation
|
|
8
|
-
* links, and the rest of the Gate-managed resources receive their per-
|
|
9
|
-
* resource prefixes as the end-user table family is specified.
|
|
10
|
-
*
|
|
11
|
-
* Envelope shape (`data` + `meta`) matches; failures land via
|
|
12
|
-
* the `GateError` family per `errors.ts`, which is `KError`-compatible
|
|
13
|
-
*.
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* Standard envelope. Generic over the `data` shape per function.
|
|
17
|
-
* Returned by every successful SDK call.
|
|
2
|
+
* `@k9o/gate` public types. They mirror what the end-user Gate routes answer; failures arrive as a `GateError`
|
|
3
|
+
* (see `errors.ts`).
|
|
18
4
|
*/
|
|
5
|
+
/** The gateway's response envelope, generic over the `data` shape. Returned by every successful SDK call. */
|
|
19
6
|
type Envelope<T> = {
|
|
20
7
|
data: T;
|
|
21
8
|
meta: Record<string, unknown>;
|
|
22
9
|
};
|
|
23
|
-
/**
|
|
24
|
-
* Project-level end-user identity (the customers OF the customer as
|
|
25
|
-
* the `PROJECT_END_USER` 7th canonical role). End-user IDs carry the
|
|
26
|
-
* `eu_<base62>` prefix.
|
|
27
|
-
*/
|
|
10
|
+
/** A project end-user (the customers of the project's customer). Ids carry the `eu_` prefix. */
|
|
28
11
|
type EndUser = {
|
|
29
|
-
/** `eu_<base62
|
|
12
|
+
/** `eu_<base62>`. */
|
|
30
13
|
id: string;
|
|
31
14
|
email: string;
|
|
32
15
|
status: "active" | "pending" | "disabled";
|
|
@@ -35,15 +18,12 @@ type EndUser = {
|
|
|
35
18
|
lastSigninAt: string | null;
|
|
36
19
|
/** Project-level roles; `PROJECT_END_USER` is the floor membership. */
|
|
37
20
|
roles: ReadonlyArray<string>;
|
|
38
|
-
/** Project-level group memberships
|
|
21
|
+
/** Project-level group memberships. */
|
|
39
22
|
groups: ReadonlyArray<string>;
|
|
40
23
|
};
|
|
41
24
|
/**
|
|
42
|
-
* Session tokens returned by sign-up
|
|
43
|
-
*
|
|
44
|
-
* for downstream Gate-aware services; `refreshToken` is opaque to the
|
|
45
|
-
* SDK consumer and is presented back to `gate.refreshToken({...})` to
|
|
46
|
-
* rotate the access token.
|
|
25
|
+
* Session tokens returned by sign-up, sign-in, OTP and magic-link redemption. The access token is a JWT; the refresh
|
|
26
|
+
* token is opaque and is presented to `gate.refreshToken` and `gate.signOut`.
|
|
47
27
|
*/
|
|
48
28
|
type Session = {
|
|
49
29
|
accessToken: string;
|
|
@@ -51,41 +31,26 @@ type Session = {
|
|
|
51
31
|
/** ISO 8601 instant the access token expires. */
|
|
52
32
|
expiresAt: string;
|
|
53
33
|
};
|
|
54
|
-
/**
|
|
55
|
-
* Marquee sign-up / sign-in shape:
|
|
56
|
-
* `{ user, session }`
|
|
57
|
-
*/
|
|
34
|
+
/** Sign-up and sign-in answer: `{ user, session }`. */
|
|
58
35
|
type AuthResult = {
|
|
59
36
|
user: EndUser;
|
|
60
37
|
session: Session;
|
|
61
38
|
};
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
39
|
+
/** `gate.whoami()` answers the end-user record itself (no wrapper). */
|
|
40
|
+
type WhoamiResult = EndUser;
|
|
41
|
+
/** `gate.refreshToken()` answers a new access token only; the refresh token in hand stays valid. */
|
|
42
|
+
type RefreshResult = {
|
|
43
|
+
accessToken: string;
|
|
44
|
+
/** ISO 8601 instant the new access token expires. */
|
|
45
|
+
expiresAt: string;
|
|
69
46
|
};
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
* when the bearer session is missing or expired (no throw on invalid;
|
|
74
|
-
* throw only on transport / upstream failures).
|
|
75
|
-
*/
|
|
76
|
-
type VerifySessionResult = {
|
|
77
|
-
valid: true;
|
|
78
|
-
user: EndUser;
|
|
79
|
-
session: Session;
|
|
80
|
-
} | {
|
|
81
|
-
valid: false;
|
|
82
|
-
user: null;
|
|
83
|
-
session: null;
|
|
47
|
+
/** `gate.signOut()` answer. `revoked` is false when the token was unknown or already revoked. */
|
|
48
|
+
type SignOutResult = {
|
|
49
|
+
revoked: boolean;
|
|
84
50
|
};
|
|
85
51
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* gateway with `K9O_INVALID_FIELD_VALUE` (`context.reason: "unknown_field"`), so this type has no escape hatch.
|
|
52
|
+
* `gate.signUp` takes a CLOSED body: `{ email, password, displayName? }`. `displayName` is 1 to 128 characters; any
|
|
53
|
+
* other field is refused by the gateway with `INVALID_FIELD_VALUE` (`context.reason: "unknown_field"`).
|
|
89
54
|
*/
|
|
90
55
|
type SignUpInput = {
|
|
91
56
|
email: string;
|
|
@@ -98,36 +63,39 @@ type SignInInput = {
|
|
|
98
63
|
password: string;
|
|
99
64
|
};
|
|
100
65
|
type SignOutInput = {
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
* the access token's bound refresh token only; explicit refresh tokens
|
|
104
|
-
* let callers revoke a specific device/session.
|
|
105
|
-
*/
|
|
106
|
-
refreshToken?: string;
|
|
66
|
+
/** The refresh token of the session to revoke (required by the route). */
|
|
67
|
+
refreshToken: string;
|
|
107
68
|
};
|
|
108
69
|
type RefreshTokenInput = {
|
|
109
70
|
refreshToken: string;
|
|
110
71
|
};
|
|
111
|
-
/**
|
|
112
|
-
* OTP purpose — scopes the one-time code to a flow. The gateway validates
|
|
113
|
-
* against this closed set (otp.ts) and defaults to "signin" when omitted.
|
|
114
|
-
*/
|
|
72
|
+
/** OTP purpose: scopes the one-time code to a flow. The gateway defaults to `"signin"` when omitted. */
|
|
115
73
|
type OtpPurpose = "signin" | "magic_link" | "password_reset" | "mfa_challenge";
|
|
116
|
-
/**
|
|
117
|
-
* `gate.passwordless.requestOtp` input — emails an OTP code to the
|
|
118
|
-
* given address. Idempotent at the per-email rate-limit ceiling per
|
|
119
|
-
* the platform anti-abuse policy.
|
|
120
|
-
*/
|
|
74
|
+
/** `gate.passwordless.requestOtp` input: emails a one-time code to the address. */
|
|
121
75
|
type RequestOtpInput = {
|
|
122
76
|
email: string;
|
|
123
77
|
/** OTP purpose; defaults to "signin" server-side when omitted. */
|
|
124
78
|
purpose?: OtpPurpose;
|
|
125
79
|
};
|
|
80
|
+
/** `gate.passwordless.verifyOtp` input: the 6-character code from the email plus the address it went to. */
|
|
81
|
+
/** `gate.passwordless.requestOtp` answer. */
|
|
82
|
+
type RequestOtpResult = {
|
|
83
|
+
otp: {
|
|
84
|
+
id: string;
|
|
85
|
+
expiresAt: string;
|
|
86
|
+
};
|
|
87
|
+
/** Only present on a deployment configured to return the code in the response (development). */
|
|
88
|
+
debugCode?: string;
|
|
89
|
+
};
|
|
126
90
|
/**
|
|
127
|
-
* `gate.passwordless.verifyOtp`
|
|
128
|
-
*
|
|
129
|
-
* gateway can resolve the right OTP row.
|
|
91
|
+
* `gate.passwordless.verifyOtp` answer. `session` and `endUserId` are present for the `signin` purpose; other
|
|
92
|
+
* purposes only confirm the code (`endUserId` when the email belongs to an end-user).
|
|
130
93
|
*/
|
|
94
|
+
type VerifyOtpResult = {
|
|
95
|
+
verified: true;
|
|
96
|
+
session?: Session;
|
|
97
|
+
endUserId?: string;
|
|
98
|
+
};
|
|
131
99
|
type VerifyOtpInput = {
|
|
132
100
|
email: string;
|
|
133
101
|
code: string;
|
|
@@ -135,11 +103,7 @@ type VerifyOtpInput = {
|
|
|
135
103
|
* so the gateway resolves the right OTP row. */
|
|
136
104
|
purpose?: OtpPurpose;
|
|
137
105
|
};
|
|
138
|
-
/**
|
|
139
|
-
* `gate.passwordless.requestMagicLink` input — emails a magic-link
|
|
140
|
-
* token to the given address; the customer's frontend handles the
|
|
141
|
-
* post-redirect exchange via `verifyMagicLink`.
|
|
142
|
-
*/
|
|
106
|
+
/** `gate.passwordless.requestMagicLink` input: emails a magic link to the address. */
|
|
143
107
|
type RequestMagicLinkInput = {
|
|
144
108
|
email: string;
|
|
145
109
|
/**
|
|
@@ -150,10 +114,22 @@ type RequestMagicLinkInput = {
|
|
|
150
114
|
*/
|
|
151
115
|
redirectUrl?: string;
|
|
152
116
|
};
|
|
153
|
-
/**
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
117
|
+
/** `gate.passwordless.verifyMagicLink` input: the opaque token from the magic-link query string. */
|
|
118
|
+
/** `gate.passwordless.requestMagicLink` answer. The same shape comes back for an unknown email. */
|
|
119
|
+
type RequestMagicLinkResult = {
|
|
120
|
+
magicLink: {
|
|
121
|
+
sent: true;
|
|
122
|
+
expiresAt?: string;
|
|
123
|
+
};
|
|
124
|
+
/** Only present on a deployment configured to return the link in the response (development). */
|
|
125
|
+
debugLink?: string;
|
|
126
|
+
};
|
|
127
|
+
/** `gate.passwordless.verifyMagicLink` answer: the new session and the end-user's id. */
|
|
128
|
+
type VerifyMagicLinkResult = {
|
|
129
|
+
redeemed: true;
|
|
130
|
+
session: Session;
|
|
131
|
+
endUserId: string;
|
|
132
|
+
};
|
|
157
133
|
type VerifyMagicLinkInput = {
|
|
158
134
|
token: string;
|
|
159
135
|
};
|
|
@@ -171,10 +147,8 @@ type MagicLinkCallbackResult = {
|
|
|
171
147
|
error: string;
|
|
172
148
|
};
|
|
173
149
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* standard claims. Customer apps in Standalone mode consume this via the
|
|
177
|
-
* `verifyJwt` helper without needing the full SDK shape.
|
|
150
|
+
* Claims of a Gate access token that `verifyJwt` resolves. Custom claims arrive FLAT under keys of the form
|
|
151
|
+
* `ext.<namespace>.<name>`; they are available through the index signature.
|
|
178
152
|
*/
|
|
179
153
|
type GateJwtClaims = {
|
|
180
154
|
/** Issuer — the Kilimanjaro gateway base URL or project-bound issuer. */
|
|
@@ -193,7 +167,7 @@ type GateJwtClaims = {
|
|
|
193
167
|
roles?: ReadonlyArray<string>;
|
|
194
168
|
/** Project-level group memberships. */
|
|
195
169
|
groups?: ReadonlyArray<string>;
|
|
196
|
-
/**
|
|
170
|
+
/** Scope strings, when the token carries them; used by `requireScope`. */
|
|
197
171
|
scopes?: ReadonlyArray<string>;
|
|
198
172
|
/** Email of the underlying end-user; convenience copy of the database column. */
|
|
199
173
|
email?: string;
|
|
@@ -236,15 +210,5 @@ type SignInCallbackResult = {
|
|
|
236
210
|
error: string;
|
|
237
211
|
errorDescription: string;
|
|
238
212
|
};
|
|
239
|
-
/**
|
|
240
|
-
* The project's hosted sign-in page and WebAuthn relying party (`GET /@<projectSlug>/gate/hosted`). Both values come from the platform: an SDK never composes a Gate host from a slug (the label is a hash of
|
|
241
|
-
* ids the SDK does not hold).
|
|
242
|
-
*/
|
|
243
|
-
type HostedGateInfo = {
|
|
244
|
-
/** Absolute hosted sign-in URL: the verified custom domain's when there is one, else the project's Gate host. */
|
|
245
|
-
signInUrl: string;
|
|
246
|
-
/** The WebAuthn relying-party id (the project's Gate host). Use it as given; never hard-code it. */
|
|
247
|
-
rpId: string;
|
|
248
|
-
};
|
|
249
213
|
|
|
250
|
-
export type { AuthResult, EndUser, Envelope, GateJwtClaims,
|
|
214
|
+
export type { AuthResult, EndUser, Envelope, GateJwtClaims, MagicLinkCallbackResult, OtpPurpose, RefreshResult, RefreshTokenInput, RequestMagicLinkInput, RequestMagicLinkResult, RequestOtpInput, RequestOtpResult, Session, SignInCallbackResult, SignInInput, SignOutInput, SignOutResult, SignUpInput, VerifyJwtFailure, VerifyJwtResult, VerifyMagicLinkInput, VerifyMagicLinkResult, VerifyOtpInput, VerifyOtpResult, WhoamiResult };
|
package/package.json
CHANGED
package/src/api-host.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the structure of a gateway API host without knowing its apex.
|
|
3
|
+
*
|
|
4
|
+
* The gateway label is `api` or `api-staging`, optionally followed by `-<rgn>` (a 4-character region code). The label
|
|
5
|
+
* before it is the org slug and whatever follows it is the apex, which is never matched against a list: a public-cloud,
|
|
6
|
+
* internal-apex, air-gapped or localhost deployment all work.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** The structure of a gateway API host, read from the host itself. */
|
|
10
|
+
export type ApiHost = {
|
|
11
|
+
/** First label of an org host (`acme` in `acme.api.corp.internal`); null for a platform host (`api.corp.internal`). */
|
|
12
|
+
orgSlug: string | null;
|
|
13
|
+
/** `api` or `api-staging`: the label that marks a gateway host. */
|
|
14
|
+
family: "api" | "api-staging";
|
|
15
|
+
/** 4-character region code when the host is regional (`api-de01`), else null (region-free). */
|
|
16
|
+
region: string | null;
|
|
17
|
+
/** Everything after the gateway label (`corp.internal`, `k9o.io`). */
|
|
18
|
+
apex: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const API_LABEL = /^(api|api-staging)(?:-([a-z]{2}\d{2}))?$/;
|
|
22
|
+
const SLUG_RE = /^[a-z][a-z0-9-]{1,62}$/;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns null for any other shape (localhost, an IP, a single-label host, a host with no gateway label), which callers
|
|
26
|
+
* use exactly as given.
|
|
27
|
+
*/
|
|
28
|
+
export function parseApiHost(host: string): ApiHost | null {
|
|
29
|
+
const labels = host.toLowerCase().split(".");
|
|
30
|
+
if (labels.some((l) => l.length === 0)) return null;
|
|
31
|
+
const mk = (gatewayLabel: string, orgSlug: string | null, rest: string[]): ApiHost | null => {
|
|
32
|
+
const m = API_LABEL.exec(gatewayLabel);
|
|
33
|
+
if (m === null || rest.length === 0) return null;
|
|
34
|
+
return { orgSlug, family: m[1] as "api" | "api-staging", region: m[2] ?? null, apex: rest.join(".") };
|
|
35
|
+
};
|
|
36
|
+
// Org host first: `<org>.<api[-rgn]>.<apex>`.
|
|
37
|
+
if (labels.length >= 3 && SLUG_RE.test(labels[0]!) && !labels[0]!.endsWith("-") && API_LABEL.test(labels[1]!)) {
|
|
38
|
+
return mk(labels[1]!, labels[0]!, labels.slice(2));
|
|
39
|
+
}
|
|
40
|
+
// Platform host: `api.<apex>`.
|
|
41
|
+
if (labels.length >= 2 && API_LABEL.test(labels[0]!)) return mk(labels[0]!, null, labels.slice(1));
|
|
42
|
+
return null;
|
|
43
|
+
}
|
package/src/client-factory.ts
CHANGED
|
@@ -8,11 +8,8 @@
|
|
|
8
8
|
* incoming request's resolved tenant) or for apps that maintain both a
|
|
9
9
|
* Standalone and Native binding against the same SDK instance.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* project addressing form — project ids are internal-only
|
|
14
|
-
* (`feedback_slug_addressing_v2_5_68`); the retired `/v2/projects/...`
|
|
15
|
-
* mounts were dropped at pass17 (`feedback_v2_path_is_an_error`).
|
|
11
|
+
* Supply `projectSlug` for
|
|
12
|
+
* `/@<slug>/gate/...` routing. The slug is the only project addressing form.
|
|
16
13
|
*
|
|
17
14
|
* Example:
|
|
18
15
|
* const client = createGateClient({
|
package/src/client.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Centralises:
|
|
5
5
|
* - URL construction against the configured `baseUrl` + per-call path
|
|
6
|
-
* - the standard envelope
|
|
7
|
-
* - the
|
|
6
|
+
* - the standard response envelope check on success
|
|
7
|
+
* - the `KError` rehydration on failure via `gateErrorFromWire`
|
|
8
8
|
* - mode-routing via the `X-Gate-Mode` header (so the gateway routes
|
|
9
9
|
* consumption to the right billing surface without per-function
|
|
10
10
|
* branching at the SDK)
|
|
@@ -47,7 +47,7 @@ export type RequestInit = {
|
|
|
47
47
|
* keeps the SDK portable across Bun + Node 18+ + browsers + test
|
|
48
48
|
* fixtures that supply a stub.
|
|
49
49
|
* - JSON parsing of non-JSON responses throws — gateway always returns
|
|
50
|
-
* JSON
|
|
50
|
+
* JSON, so a parse failure is an upstream defect.
|
|
51
51
|
*/
|
|
52
52
|
export async function request<T>(init: RequestInit): Promise<Envelope<T>> {
|
|
53
53
|
return requestWith<T>(getGateConfig(), init);
|
package/src/errors.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type GateErrorCode =
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Surface error thrown by the SDK. Mirrors the gateway's `KError` shape
|
|
39
|
-
*
|
|
39
|
+
* `code` (closed taxonomy), `message` (human-readable),
|
|
40
40
|
* `context` (structured per-code payload), `recommendation` (action the
|
|
41
41
|
* caller can take, or null when there's no canonical recommendation).
|
|
42
42
|
*/
|