@sema-ai/registry-core 0.10.3
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 +131 -0
- package/README.md +84 -0
- package/dist/api/auth-bridge.d.ts +332 -0
- package/dist/api/auth-bridge.d.ts.map +1 -0
- package/dist/api/auth-bridge.js +211 -0
- package/dist/api/auth-bridge.js.map +1 -0
- package/dist/api/auth.d.ts +258 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +204 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/scopes.d.ts +353 -0
- package/dist/api/scopes.d.ts.map +1 -0
- package/dist/api/scopes.js +229 -0
- package/dist/api/scopes.js.map +1 -0
- package/dist/bundle.d.ts +14 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +68 -0
- package/dist/bundle.js.map +1 -0
- package/dist/config-fns.d.ts +236 -0
- package/dist/config-fns.d.ts.map +1 -0
- package/dist/config-fns.js +272 -0
- package/dist/config-fns.js.map +1 -0
- package/dist/cross-domain.d.ts +35 -0
- package/dist/cross-domain.d.ts.map +1 -0
- package/dist/cross-domain.js +119 -0
- package/dist/cross-domain.js.map +1 -0
- package/dist/file-edit.d.ts +37 -0
- package/dist/file-edit.d.ts.map +1 -0
- package/dist/file-edit.js +126 -0
- package/dist/file-edit.js.map +1 -0
- package/dist/file-store.d.ts +39 -0
- package/dist/file-store.d.ts.map +1 -0
- package/dist/file-store.js +142 -0
- package/dist/file-store.js.map +1 -0
- package/dist/fleet.d.ts +479 -0
- package/dist/fleet.d.ts.map +1 -0
- package/dist/fleet.js +303 -0
- package/dist/fleet.js.map +1 -0
- package/dist/hash.d.ts +33 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +60 -0
- package/dist/hash.js.map +1 -0
- package/dist/hooks.d.ts +5478 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +628 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/local-load.d.ts +33 -0
- package/dist/local-load.d.ts.map +1 -0
- package/dist/local-load.js +164 -0
- package/dist/local-load.js.map +1 -0
- package/dist/migrate.d.ts +245 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +543 -0
- package/dist/migrate.js.map +1 -0
- package/dist/node.d.ts +12 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +12 -0
- package/dist/node.js.map +1 -0
- package/dist/reader.d.ts +19 -0
- package/dist/reader.d.ts.map +1 -0
- package/dist/reader.js +2 -0
- package/dist/reader.js.map +1 -0
- package/dist/remote-exec.d.ts +275 -0
- package/dist/remote-exec.d.ts.map +1 -0
- package/dist/remote-exec.js +183 -0
- package/dist/remote-exec.js.map +1 -0
- package/dist/resolve-roster.d.ts +29 -0
- package/dist/resolve-roster.d.ts.map +1 -0
- package/dist/resolve-roster.js +105 -0
- package/dist/resolve-roster.js.map +1 -0
- package/dist/safety-merge-spec.d.ts +328 -0
- package/dist/safety-merge-spec.d.ts.map +1 -0
- package/dist/safety-merge-spec.js +65 -0
- package/dist/safety-merge-spec.js.map +1 -0
- package/dist/scheduler-store-node.d.ts +15 -0
- package/dist/scheduler-store-node.d.ts.map +1 -0
- package/dist/scheduler-store-node.js +43 -0
- package/dist/scheduler-store-node.js.map +1 -0
- package/dist/scheduler-store.d.ts +65 -0
- package/dist/scheduler-store.d.ts.map +1 -0
- package/dist/scheduler-store.js +59 -0
- package/dist/scheduler-store.js.map +1 -0
- package/dist/secret-refs.d.ts +34 -0
- package/dist/secret-refs.d.ts.map +1 -0
- package/dist/secret-refs.js +49 -0
- package/dist/secret-refs.js.map +1 -0
- package/dist/sha256.d.ts +17 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +115 -0
- package/dist/sha256.js.map +1 -0
- package/dist/skills-manifest.d.ts +13 -0
- package/dist/skills-manifest.d.ts.map +1 -0
- package/dist/skills-manifest.js +55 -0
- package/dist/skills-manifest.js.map +1 -0
- package/dist/types.d.ts +9911 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1727 -0
- package/dist/types.js.map +1 -0
- package/package.json +130 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/auth-bridge` — the 鉴权桥 contract: how a WORKER (sema-server / any data-plane service) verifies a
|
|
3
|
+
* registry-minted USER access JWT and maps it into its existing `x-agent-principal` identity system.
|
|
4
|
+
* (REGISTRY-REBUILD-DESIGN §5, M3 — registry half = JWKS already shipped; service half = ONE verifier
|
|
5
|
+
* middleware implementing the rules below. This file is the schema + normative spec the service copies.)
|
|
6
|
+
*
|
|
7
|
+
* TRUST MODEL (single trust root, zero new worker config): the worker already trusts the registry for its
|
|
8
|
+
* effective config; it extends the SAME trust to the registry's JWKS. A bearer token that is a well-formed
|
|
9
|
+
* JWT and verifies against that JWKS with the checks below IS the user — `sub` feeds x-agent-principal
|
|
10
|
+
* directly (owner semantics / costQuota / entitlement are already principal-keyed). Existing
|
|
11
|
+
* SERVICE_AUTH_TOKENS / wpt_ bearer branches are untouched; this is an ADDITIONAL branch tried when the
|
|
12
|
+
* bearer LOOKS like a JWT (three dot-separated base64url segments).
|
|
13
|
+
*
|
|
14
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
15
|
+
* NORMATIVE VERIFICATION RULES — the service verifier MUST implement ALL of these (copy as its spec):
|
|
16
|
+
*
|
|
17
|
+
* R1 ALGORITHM WHITELIST. Accept `alg: RS256` ONLY (AUTH_BRIDGE_ALGS). Reject `none`, every HS* (an
|
|
18
|
+
* HS-signed token verified against a PUBLIC key = classic alg-confusion forgery), the ES and PS families, and any
|
|
19
|
+
* token whose header alg is absent. Pin the list in the JWT library call — never read alg from the
|
|
20
|
+
* token to decide how to verify.
|
|
21
|
+
*
|
|
22
|
+
* R2 KEY SOURCE. Verification keys come from the registry JWKS endpoint `GET {registryBaseUrl}` +
|
|
23
|
+
* REGISTRY_JWKS_PATH ("/api/auth/sso/jwks"; response shape = `Jwks` below, public members only:
|
|
24
|
+
* kty/n/e/kid/use/alg). Match the token header `kid` against cached keys.
|
|
25
|
+
*
|
|
26
|
+
* R3 KID-MISS FORCED REFRESH + THROTTLE. kid found in cache → use the cached key (no fetch on the hot
|
|
27
|
+
* path). kid NOT in cache → force ONE JWKS refetch, then retry the match (this is the key-ROTATION
|
|
28
|
+
* window: a freshly rotated registry would otherwise reject every new token until the cache expired).
|
|
29
|
+
* Forced refreshes are throttled to at most one per `jwksRefreshMinIntervalSec` (default 60s) —
|
|
30
|
+
* otherwise an attacker spraying random-kid tokens turns the verifier into a JWKS-fetch DoS cannon.
|
|
31
|
+
* Within the throttle window an unknown kid fails as `invalid_signature` WITHOUT fetching.
|
|
32
|
+
* (Decision function: `shouldForceJwksRefresh`.)
|
|
33
|
+
*
|
|
34
|
+
* R4 ISSUER PIN. `iss` MUST equal the CONFIGURED issuer exactly (VerifierConfig.issuer = the registry's
|
|
35
|
+
* SSO_ISSUER, trailing slash stripped on both sides). A missing or different iss → `unknown_issuer`.
|
|
36
|
+
* Never derive the expected issuer from the token or from request headers.
|
|
37
|
+
*
|
|
38
|
+
* R5 TIME WINDOW WITH SKEW. `exp` is REQUIRED: reject when nowSec > exp + clockSkewSec. `nbf`, when
|
|
39
|
+
* present: reject when nowSec < nbf - clockSkewSec. clockSkewSec default 60 (DEFAULT_CLOCK_SKEW_SEC).
|
|
40
|
+
* Both failures classify as `expired`. (Decision function: `classifyTimeClaims`.)
|
|
41
|
+
*
|
|
42
|
+
* R6 PRINCIPAL. `sub` MUST be a non-empty string — it becomes `VerifiedIdentity.principal` and feeds the
|
|
43
|
+
* existing x-agent-principal pipeline verbatim. Empty/missing sub → `malformed`.
|
|
44
|
+
*
|
|
45
|
+
* R7 AUDIENCE (optional). When VerifierConfig.audience is set, `aud` must contain it (string equal or
|
|
46
|
+
* array member); mismatch → `invalid_signature`-class rejection is WRONG — classify as `malformed`
|
|
47
|
+
* (the token is valid but not FOR this worker). When audience is unset, `aud` is ignored.
|
|
48
|
+
*
|
|
49
|
+
* R8 OFFLINE GRACE / FAIL-CLOSED. JWKS fetch fails (network/5xx/malformed body): keys already cached
|
|
50
|
+
* remain USABLE for up to `offlineGraceSec` (default 24h, DEFAULT_OFFLINE_GRACE_SEC) measured from the
|
|
51
|
+
* last SUCCESSFUL fetch — a registry blip must not take the whole data plane down. Past the grace, or
|
|
52
|
+
* with no cache at all, verification fails CLOSED as `jwks_unavailable` (never fail-open, never skip
|
|
53
|
+
* signature checks). (Decision function: `jwksWithinOfflineGrace`.)
|
|
54
|
+
*
|
|
55
|
+
* R9 OUTPUT. Success → `VerifiedIdentity { principal: sub, role: token.role, grantId: token.jti,
|
|
56
|
+
* scope: token.scope }` (`scope` = the multi-tenant active scope claim, `api/scopes`; absent = "global").
|
|
57
|
+
* `role` is the registry RBAC role claim (string, informational for the worker's own authz mapping);
|
|
58
|
+
* `grantId` is the jti when the registry stamps one (absent today — optional forever). Failure → one
|
|
59
|
+
* of AUTH_BRIDGE_ERROR_CODES; the HTTP surface answers 401 with a GENERIC message (no error detail
|
|
60
|
+
* disclosure to the caller; the code goes to logs/metrics only).
|
|
61
|
+
*
|
|
62
|
+
* R10 NO CREDENTIAL LOGGING. Never log the token, any signature segment, or JWKS key material. Log
|
|
63
|
+
* kid + error code + iss at most.
|
|
64
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
65
|
+
*
|
|
66
|
+
* Pure zod contract + pure decision functions — zero IO, zero crypto here (the service brings its own JWT
|
|
67
|
+
* library; THESE shapes pin down its configuration), browser-safe.
|
|
68
|
+
*/
|
|
69
|
+
import { z } from "zod";
|
|
70
|
+
// ── constants ────────────────────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
/** The registry JWKS endpoint, relative to the registry origin (shipped; public by design, RFC 7517 §5). */
|
|
72
|
+
export const REGISTRY_JWKS_PATH = "/api/auth/sso/jwks";
|
|
73
|
+
/** R1 — the ONLY acceptable JWS algorithms. */
|
|
74
|
+
export const AUTH_BRIDGE_ALGS = ["RS256"];
|
|
75
|
+
/** R5 — default tolerated clock skew (seconds) on exp/nbf. */
|
|
76
|
+
export const DEFAULT_CLOCK_SKEW_SEC = 60;
|
|
77
|
+
/** R3 — default minimum seconds between FORCED (kid-miss) JWKS refetches. */
|
|
78
|
+
export const DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC = 60;
|
|
79
|
+
/** R8 — default seconds a cached JWKS stays usable after fetches start failing (24h), then fail-closed. */
|
|
80
|
+
export const DEFAULT_OFFLINE_GRACE_SEC = 24 * 60 * 60;
|
|
81
|
+
// ── JWKS wire shape (what GET /api/auth/sso/jwks returns) ────────────────────────────────────────────────
|
|
82
|
+
/** One RSA public JWK as the registry publishes it (public members ONLY — a `d`/`p`/`q` here is a registry
|
|
83
|
+
* bug, but the verifier MUST still never use/echo private members). */
|
|
84
|
+
export const RegistryJwk = z
|
|
85
|
+
.object({
|
|
86
|
+
kty: z.literal("RSA"),
|
|
87
|
+
/** modulus, base64url */
|
|
88
|
+
n: z.string().min(1),
|
|
89
|
+
/** exponent, base64url */
|
|
90
|
+
e: z.string().min(1),
|
|
91
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
92
|
+
kid: z.string().min(1),
|
|
93
|
+
use: z.literal("sig").optional(),
|
|
94
|
+
alg: z.enum(AUTH_BRIDGE_ALGS).optional(),
|
|
95
|
+
})
|
|
96
|
+
.passthrough();
|
|
97
|
+
/** The JWKS document. `{keys: []}` is VALID (registry with SSO disabled) — every token then fails as
|
|
98
|
+
* `invalid_signature` (there is a registry, it just mints nothing). */
|
|
99
|
+
export const RegistryJwks = z.object({ keys: z.array(RegistryJwk) }).passthrough();
|
|
100
|
+
// ── verifier configuration ───────────────────────────────────────────────────────────────────────────────
|
|
101
|
+
/** Everything the service verifier is allowed to be configured with. In the zero-new-config deployment the
|
|
102
|
+
* worker derives `jwksUrl` from the registry base URL it already has (config pull) + REGISTRY_JWKS_PATH,
|
|
103
|
+
* and `issuer` from the same registry identity — but both stay explicit here so a split deployment
|
|
104
|
+
* (issuer behind a proxy ≠ fetch URL) is expressible. */
|
|
105
|
+
export const VerifierConfig = z.object({
|
|
106
|
+
/** R4 — the EXACT expected `iss` (the registry's SSO_ISSUER). Compare with trailing slash stripped. */
|
|
107
|
+
issuer: z.string().min(1),
|
|
108
|
+
/** R2 — absolute JWKS URL. Default: issuer (or the worker's registry base URL) + REGISTRY_JWKS_PATH. */
|
|
109
|
+
jwksUrl: z.string().min(1).optional(),
|
|
110
|
+
/** R7 — when set, `aud` must contain this value; unset = aud ignored. */
|
|
111
|
+
audience: z.string().min(1).optional(),
|
|
112
|
+
/** R5 — seconds of exp/nbf tolerance. Default DEFAULT_CLOCK_SKEW_SEC (60). */
|
|
113
|
+
clockSkewSec: z.number().int().nonnegative().default(DEFAULT_CLOCK_SKEW_SEC),
|
|
114
|
+
/** R3 — min seconds between forced kid-miss refetches. Default 60. */
|
|
115
|
+
jwksRefreshMinIntervalSec: z.number().int().nonnegative().default(DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC),
|
|
116
|
+
/** R8 — seconds a stale cache survives fetch failures before fail-closed. Default 86400 (24h). */
|
|
117
|
+
offlineGraceSec: z.number().int().nonnegative().default(DEFAULT_OFFLINE_GRACE_SEC),
|
|
118
|
+
});
|
|
119
|
+
// ── verification outcome ─────────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
/** R9 failure classification — for logs/metrics/tests; the HTTP surface stays a generic 401. */
|
|
121
|
+
export const AUTH_BRIDGE_ERROR_CODES = [
|
|
122
|
+
/** exp in the past / nbf in the future (beyond clockSkewSec) — R5. */
|
|
123
|
+
"expired",
|
|
124
|
+
/** signature does not verify against any acceptable key, or kid unknown after the R3 refresh path. */
|
|
125
|
+
"invalid_signature",
|
|
126
|
+
/** iss missing or ≠ the configured issuer — R4. */
|
|
127
|
+
"unknown_issuer",
|
|
128
|
+
/** no usable JWKS: never fetched successfully, or offline grace exhausted — R8. Fail-closed. */
|
|
129
|
+
"jwks_unavailable",
|
|
130
|
+
/** not a JWT / disallowed alg (R1) / missing sub (R6) / aud mismatch (R7) / claim shape garbage. */
|
|
131
|
+
"malformed",
|
|
132
|
+
];
|
|
133
|
+
/** R9 success output — what the verifier middleware hands the existing principal pipeline. */
|
|
134
|
+
export const VerifiedIdentity = z.object({
|
|
135
|
+
/** = token `sub`. Feeds x-agent-principal / owner semantics / costQuota / entitlement keying verbatim. */
|
|
136
|
+
principal: z.string().min(1),
|
|
137
|
+
/** = token `role` claim (registry RBAC: admin/editor/viewer/…) — informational; the worker maps it to
|
|
138
|
+
* its own authz, it does NOT replace worker-side entitlement checks. */
|
|
139
|
+
role: z.string().optional(),
|
|
140
|
+
/** = token `jti` when present (the auth_grants grant id — audit correlation). Absent today; optional forever. */
|
|
141
|
+
grantId: z.string().optional(),
|
|
142
|
+
/** = token `scope` claim when present (the active tenant scope, `api/scopes`) — transparently forwarded
|
|
143
|
+
* to the worker's principal pipeline. ABSENT means the pre-scope/default token: semantically "global"
|
|
144
|
+
* (use `tokenScopeOf` from api/scopes to normalize). Optional forever — backward compatible. */
|
|
145
|
+
scope: z.string().optional(),
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* R3 — should a token bearing `kid` trigger a forced JWKS refetch?
|
|
149
|
+
* true ⇔ the kid is not cached AND we are outside the refresh throttle window.
|
|
150
|
+
* false ⇒ verify against the cache as-is (cached kid hit), or fail `invalid_signature` (unknown kid,
|
|
151
|
+
* throttled) without fetching.
|
|
152
|
+
*/
|
|
153
|
+
export function shouldForceJwksRefresh(kid, cache, cfg) {
|
|
154
|
+
if (cache.cachedKids.includes(kid))
|
|
155
|
+
return false;
|
|
156
|
+
if (cache.lastFetchAttemptMs === undefined)
|
|
157
|
+
return true;
|
|
158
|
+
return cache.nowMs - cache.lastFetchAttemptMs >= cfg.jwksRefreshMinIntervalSec * 1000;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* R8 — with the LATEST fetch having failed, may the cached keys still be used?
|
|
162
|
+
* true ⇔ there WAS a successful fetch and it is at most offlineGraceSec old.
|
|
163
|
+
* false ⇒ fail closed: `jwks_unavailable`.
|
|
164
|
+
*/
|
|
165
|
+
export function jwksWithinOfflineGrace(cache, cfg) {
|
|
166
|
+
if (cache.lastFetchSuccessMs === undefined)
|
|
167
|
+
return false;
|
|
168
|
+
return cache.nowMs - cache.lastFetchSuccessMs <= cfg.offlineGraceSec * 1000;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* R5 — classify the token's time window. `exp` REQUIRED (a JWT without exp is not acceptable here —
|
|
172
|
+
* registry tokens always carry one; classify a missing exp as "expired" so it can never live forever).
|
|
173
|
+
* All values in SECONDS (JWT NumericDate).
|
|
174
|
+
*/
|
|
175
|
+
export function classifyTimeClaims(claims, nowSec, clockSkewSec) {
|
|
176
|
+
if (typeof claims.exp !== "number" || nowSec > claims.exp + clockSkewSec)
|
|
177
|
+
return "expired";
|
|
178
|
+
if (typeof claims.nbf === "number" && nowSec < claims.nbf - clockSkewSec)
|
|
179
|
+
return "expired";
|
|
180
|
+
return "ok";
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* R6/R7/R9 — map a signature-verified payload to the identity, or reject. Pure: call AFTER the JWT
|
|
184
|
+
* library has verified signature (R1/R2/R3), issuer (R4) and time (R5).
|
|
185
|
+
*/
|
|
186
|
+
export function toVerifiedIdentity(payload, cfg) {
|
|
187
|
+
if (typeof payload.sub !== "string" || payload.sub.length === 0)
|
|
188
|
+
return { ok: false, error: "malformed" };
|
|
189
|
+
if (cfg?.audience) {
|
|
190
|
+
const aud = payload.aud;
|
|
191
|
+
const match = typeof aud === "string" ? aud === cfg.audience : Array.isArray(aud) && aud.includes(cfg.audience);
|
|
192
|
+
if (!match)
|
|
193
|
+
return { ok: false, error: "malformed" };
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
ok: true,
|
|
197
|
+
identity: {
|
|
198
|
+
principal: payload.sub,
|
|
199
|
+
role: typeof payload.role === "string" && payload.role.length > 0 ? payload.role : undefined,
|
|
200
|
+
grantId: typeof payload.jti === "string" && payload.jti.length > 0 ? payload.jti : undefined,
|
|
201
|
+
scope: typeof payload.scope === "string" && payload.scope.length > 0 ? payload.scope : undefined,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/** Cheap pre-filter: does this bearer LOOK like a JWT (three non-empty dot-separated segments)? Use it to
|
|
206
|
+
* route between the existing static-token branch and this verifier — NOT as any kind of validation. */
|
|
207
|
+
export function looksLikeJwt(bearer) {
|
|
208
|
+
const parts = bearer.split(".");
|
|
209
|
+
return parts.length === 3 && parts.every((p) => p.length > 0);
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=auth-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-bridge.js","sourceRoot":"","sources":["../../src/api/auth-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/auth` — the /api/v1/auth/* WIRE CONTRACT (REGISTRY-REBUILD-DESIGN §2.1/§2.2, M1 shipped in
|
|
3
|
+
* sema-registry; this file freezes the shipped shapes so registry, orchestrator, service and the sema
|
|
4
|
+
* shell import ONE source of truth — design §3 "契约先行").
|
|
5
|
+
*
|
|
6
|
+
* The flow is RFC 8628 (OAuth 2.0 Device Authorization Grant) + a rotating opaque refresh token:
|
|
7
|
+
*
|
|
8
|
+
* 1. client POST /api/v1/auth/device/code → DeviceCodeResponse (device_code + user_code)
|
|
9
|
+
* 2. user opens verification_uri (/activate) in ANY logged-in browser, approves the user_code
|
|
10
|
+
* (GET/POST /api/v1/auth/device/approve — session-cookie-gated, consent screen)
|
|
11
|
+
* 3. client POLLS POST /api/v1/auth/device/token every `interval` seconds
|
|
12
|
+
* → 400 OAuthErrorResponse (authorization_pending / slow_down / expired_token / access_denied)
|
|
13
|
+
* → 200 TokenGrantResponse exactly ONCE (the handshake row is consumed atomically)
|
|
14
|
+
* 4. client silently renews via POST /api/v1/auth/token/refresh (refresh rotation: the presented
|
|
15
|
+
* refresh token is single-use; a replay answers invalid_grant)
|
|
16
|
+
* 5. client POST /api/v1/auth/logout revokes the refresh grant (RFC 7009 discipline: always 200 ok,
|
|
17
|
+
* no token-existence disclosure)
|
|
18
|
+
*
|
|
19
|
+
* WIRE CASING: these are OAuth-standard endpoints — fields are snake_case ON THE WIRE (access_token,
|
|
20
|
+
* device_code, …), deliberately breaking this package's camelCase habit. Do not "fix" it.
|
|
21
|
+
*
|
|
22
|
+
* SECRET DISCIPLINE (registry side, informational for consumers): device_code and refresh_token are
|
|
23
|
+
* ≥256-bit opaque strings persisted ONLY as SHA-256 hashes; access_token is the standard issueSsoToken
|
|
24
|
+
* RS256 JWT (1h) — see `api/auth-bridge` for how a worker verifies it.
|
|
25
|
+
*
|
|
26
|
+
* Pure zod contract — zero IO, browser-safe, same rules as the rest of this package.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
/** The five auth endpoints, relative to the registry origin. */
|
|
30
|
+
export declare const AUTH_V1_PATHS: {
|
|
31
|
+
readonly deviceCode: "/api/v1/auth/device/code";
|
|
32
|
+
readonly deviceToken: "/api/v1/auth/device/token";
|
|
33
|
+
readonly deviceApprove: "/api/v1/auth/device/approve";
|
|
34
|
+
readonly tokenRefresh: "/api/v1/auth/token/refresh";
|
|
35
|
+
readonly logout: "/api/v1/auth/logout";
|
|
36
|
+
};
|
|
37
|
+
/** The RFC 8628 grant_type the device token endpoint requires. */
|
|
38
|
+
export declare const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
39
|
+
/** Minimum seconds between token-endpoint polls (RFC 8628 `interval`); polling faster → `slow_down`. */
|
|
40
|
+
export declare const DEVICE_POLL_INTERVAL_SECONDS = 5;
|
|
41
|
+
/** Device-code handshake lifetime (seconds) — the user has 15 minutes to open /activate and approve. */
|
|
42
|
+
export declare const DEVICE_CODE_TTL_SECONDS: number;
|
|
43
|
+
/** Access-token (RS256 JWT) lifetime in seconds — 1h, re-minted via refresh. */
|
|
44
|
+
export declare const ACCESS_TOKEN_TTL_SECONDS: number;
|
|
45
|
+
/** Refresh-grant lifetime (seconds) — 30d SLIDING: every rotation opens a fresh 30d window. */
|
|
46
|
+
export declare const REFRESH_TOKEN_TTL_SECONDS: number;
|
|
47
|
+
/** Every error code the /api/v1/auth/* endpoints answer (HTTP 400 unless noted). */
|
|
48
|
+
export declare const OAUTH_ERROR_CODES: readonly ["authorization_pending", "slow_down", "expired_token", "access_denied", "invalid_request", "unsupported_grant_type", "invalid_grant", "server_error"];
|
|
49
|
+
export type OAuthErrorCode = (typeof OAUTH_ERROR_CODES)[number];
|
|
50
|
+
/** The uniform error body. `error_description` is HUMAN diagnostic text — clients branch on `error` only. */
|
|
51
|
+
export declare const OAuthErrorResponse: z.ZodObject<{
|
|
52
|
+
error: z.ZodEnum<["authorization_pending", "slow_down", "expired_token", "access_denied", "invalid_request", "unsupported_grant_type", "invalid_grant", "server_error"]>;
|
|
53
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
54
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
55
|
+
error: z.ZodEnum<["authorization_pending", "slow_down", "expired_token", "access_denied", "invalid_request", "unsupported_grant_type", "invalid_grant", "server_error"]>;
|
|
56
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
57
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
58
|
+
error: z.ZodEnum<["authorization_pending", "slow_down", "expired_token", "access_denied", "invalid_request", "unsupported_grant_type", "invalid_grant", "server_error"]>;
|
|
59
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
60
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
61
|
+
export type OAuthErrorResponse = z.infer<typeof OAuthErrorResponse>;
|
|
62
|
+
/** Unauthenticated by design (the client has no credentials yet). `client_name` is a NON-secret display
|
|
63
|
+
* hint for the consent screen ("sema CLI on macOS"); absent → the server falls back to the User-Agent.
|
|
64
|
+
* Body may be JSON or form-encoded; an empty body is legal. */
|
|
65
|
+
export declare const DeviceCodeRequest: z.ZodObject<{
|
|
66
|
+
client_name: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
68
|
+
client_name: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
70
|
+
client_name: z.ZodOptional<z.ZodString>;
|
|
71
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
72
|
+
export type DeviceCodeRequest = z.infer<typeof DeviceCodeRequest>;
|
|
73
|
+
/** RFC 8628 §3.2 device-authorization response. `device_code` is the client's polling secret (keep it in
|
|
74
|
+
* memory, never log it); `user_code` is the short human code ("XXXX-XXXX") the user approves. */
|
|
75
|
+
export declare const DeviceCodeResponse: z.ZodObject<{
|
|
76
|
+
/** ≥256-bit opaque polling secret — returned ONCE, hash-persisted server-side. */
|
|
77
|
+
device_code: z.ZodString;
|
|
78
|
+
/** Human approval code, canonical shape "XXXX-XXXX" (base-20 alphabet: no vowels, no 0/O/1/I). */
|
|
79
|
+
user_code: z.ZodString;
|
|
80
|
+
/** The /activate page the user opens (any device, any logged-in browser). */
|
|
81
|
+
verification_uri: z.ZodString;
|
|
82
|
+
/** verification_uri with ?user_code= pre-filled — one-click / QR path. */
|
|
83
|
+
verification_uri_complete: z.ZodString;
|
|
84
|
+
/** Seconds until the handshake expires (DEVICE_CODE_TTL_SECONDS). */
|
|
85
|
+
expires_in: z.ZodNumber;
|
|
86
|
+
/** Minimum seconds between polls (DEVICE_POLL_INTERVAL_SECONDS). */
|
|
87
|
+
interval: z.ZodNumber;
|
|
88
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
89
|
+
/** ≥256-bit opaque polling secret — returned ONCE, hash-persisted server-side. */
|
|
90
|
+
device_code: z.ZodString;
|
|
91
|
+
/** Human approval code, canonical shape "XXXX-XXXX" (base-20 alphabet: no vowels, no 0/O/1/I). */
|
|
92
|
+
user_code: z.ZodString;
|
|
93
|
+
/** The /activate page the user opens (any device, any logged-in browser). */
|
|
94
|
+
verification_uri: z.ZodString;
|
|
95
|
+
/** verification_uri with ?user_code= pre-filled — one-click / QR path. */
|
|
96
|
+
verification_uri_complete: z.ZodString;
|
|
97
|
+
/** Seconds until the handshake expires (DEVICE_CODE_TTL_SECONDS). */
|
|
98
|
+
expires_in: z.ZodNumber;
|
|
99
|
+
/** Minimum seconds between polls (DEVICE_POLL_INTERVAL_SECONDS). */
|
|
100
|
+
interval: z.ZodNumber;
|
|
101
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
102
|
+
/** ≥256-bit opaque polling secret — returned ONCE, hash-persisted server-side. */
|
|
103
|
+
device_code: z.ZodString;
|
|
104
|
+
/** Human approval code, canonical shape "XXXX-XXXX" (base-20 alphabet: no vowels, no 0/O/1/I). */
|
|
105
|
+
user_code: z.ZodString;
|
|
106
|
+
/** The /activate page the user opens (any device, any logged-in browser). */
|
|
107
|
+
verification_uri: z.ZodString;
|
|
108
|
+
/** verification_uri with ?user_code= pre-filled — one-click / QR path. */
|
|
109
|
+
verification_uri_complete: z.ZodString;
|
|
110
|
+
/** Seconds until the handshake expires (DEVICE_CODE_TTL_SECONDS). */
|
|
111
|
+
expires_in: z.ZodNumber;
|
|
112
|
+
/** Minimum seconds between polls (DEVICE_POLL_INTERVAL_SECONDS). */
|
|
113
|
+
interval: z.ZodNumber;
|
|
114
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
115
|
+
export type DeviceCodeResponse = z.infer<typeof DeviceCodeResponse>;
|
|
116
|
+
/** RFC 8628 §3.4 token request. Form or JSON. */
|
|
117
|
+
export declare const DeviceTokenRequest: z.ZodObject<{
|
|
118
|
+
grant_type: z.ZodLiteral<"urn:ietf:params:oauth:grant-type:device_code">;
|
|
119
|
+
device_code: z.ZodString;
|
|
120
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
121
|
+
grant_type: z.ZodLiteral<"urn:ietf:params:oauth:grant-type:device_code">;
|
|
122
|
+
device_code: z.ZodString;
|
|
123
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
124
|
+
grant_type: z.ZodLiteral<"urn:ietf:params:oauth:grant-type:device_code">;
|
|
125
|
+
device_code: z.ZodString;
|
|
126
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
127
|
+
export type DeviceTokenRequest = z.infer<typeof DeviceTokenRequest>;
|
|
128
|
+
/** The ONE token success shape — shared by device/token AND token/refresh (both mint through the same
|
|
129
|
+
* exit: access = issueSsoToken RS256 JWT, refresh = fresh opaque rotating grant). */
|
|
130
|
+
export declare const TokenGrantResponse: z.ZodObject<{
|
|
131
|
+
/** RS256 JWT — claims: sub (principal), iss, iat, exp, role; optional aud. Verify per api/auth-bridge. */
|
|
132
|
+
access_token: z.ZodString;
|
|
133
|
+
/** NEW opaque rotating refresh token — replaces any previous one the client held. Store 0600/keychain. */
|
|
134
|
+
refresh_token: z.ZodString;
|
|
135
|
+
token_type: z.ZodLiteral<"Bearer">;
|
|
136
|
+
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS). */
|
|
137
|
+
expires_in: z.ZodNumber;
|
|
138
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
139
|
+
/** RS256 JWT — claims: sub (principal), iss, iat, exp, role; optional aud. Verify per api/auth-bridge. */
|
|
140
|
+
access_token: z.ZodString;
|
|
141
|
+
/** NEW opaque rotating refresh token — replaces any previous one the client held. Store 0600/keychain. */
|
|
142
|
+
refresh_token: z.ZodString;
|
|
143
|
+
token_type: z.ZodLiteral<"Bearer">;
|
|
144
|
+
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS). */
|
|
145
|
+
expires_in: z.ZodNumber;
|
|
146
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
147
|
+
/** RS256 JWT — claims: sub (principal), iss, iat, exp, role; optional aud. Verify per api/auth-bridge. */
|
|
148
|
+
access_token: z.ZodString;
|
|
149
|
+
/** NEW opaque rotating refresh token — replaces any previous one the client held. Store 0600/keychain. */
|
|
150
|
+
refresh_token: z.ZodString;
|
|
151
|
+
token_type: z.ZodLiteral<"Bearer">;
|
|
152
|
+
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS). */
|
|
153
|
+
expires_in: z.ZodNumber;
|
|
154
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
155
|
+
export type TokenGrantResponse = z.infer<typeof TokenGrantResponse>;
|
|
156
|
+
/** Rotation is STRICT single-use: success revokes the presented grant and returns a NEW refresh_token;
|
|
157
|
+
* a replayed (or raced) old token answers `invalid_grant`. Clients MUST persist the new token before
|
|
158
|
+
* discarding the old request state. */
|
|
159
|
+
export declare const TokenRefreshRequest: z.ZodObject<{
|
|
160
|
+
refresh_token: z.ZodString;
|
|
161
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
162
|
+
refresh_token: z.ZodString;
|
|
163
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
164
|
+
refresh_token: z.ZodString;
|
|
165
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
166
|
+
export type TokenRefreshRequest = z.infer<typeof TokenRefreshRequest>;
|
|
167
|
+
/** Revoke the refresh grant. The already-minted access JWT stays valid until its 1h exp — revocation kills
|
|
168
|
+
* the RENEWAL path, which is the durable one. */
|
|
169
|
+
export declare const LogoutRequest: z.ZodObject<{
|
|
170
|
+
refresh_token: z.ZodString;
|
|
171
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
172
|
+
refresh_token: z.ZodString;
|
|
173
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
174
|
+
refresh_token: z.ZodString;
|
|
175
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
176
|
+
export type LogoutRequest = z.infer<typeof LogoutRequest>;
|
|
177
|
+
/** Always `{ok:true}` with HTTP 200 whether or not the token matched a live grant (RFC 7009: a revocation
|
|
178
|
+
* endpoint never discloses token existence). 400 only for a malformed request. */
|
|
179
|
+
export declare const LogoutResponse: z.ZodObject<{
|
|
180
|
+
ok: z.ZodLiteral<true>;
|
|
181
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
182
|
+
ok: z.ZodLiteral<true>;
|
|
183
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
184
|
+
ok: z.ZodLiteral<true>;
|
|
185
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
186
|
+
export type LogoutResponse = z.infer<typeof LogoutResponse>;
|
|
187
|
+
/** Handshake states a consent-screen lookup can see. (`approved` rows vanish once exchanged/consumed.)
|
|
188
|
+
* `denied` is contract-reserved for an explicit deny button (pairs with the `access_denied` poll error);
|
|
189
|
+
* the shipped M1 store carries pending|approved only — adding deny later is implementation, not schema. */
|
|
190
|
+
export declare const DEVICE_AUTH_STATUSES: readonly ["pending", "approved", "denied"];
|
|
191
|
+
export type DeviceAuthStatus = (typeof DEVICE_AUTH_STATUSES)[number];
|
|
192
|
+
/** GET ?user_code=XXXX-XXXX — the consent screen's lookup, WITHOUT approving. 401 when not signed in;
|
|
193
|
+
* invalid_grant for an unknown/expired code. */
|
|
194
|
+
export declare const DeviceApproveLookupResponse: z.ZodObject<{
|
|
195
|
+
user_code: z.ZodString;
|
|
196
|
+
status: z.ZodEnum<["pending", "approved", "denied"]>;
|
|
197
|
+
/** The client_name/User-Agent display hint from device/code — null when the client sent none. */
|
|
198
|
+
client: z.ZodNullable<z.ZodString>;
|
|
199
|
+
/** Handshake expiry, epoch MILLISECONDS. */
|
|
200
|
+
expires_at: z.ZodNumber;
|
|
201
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
202
|
+
user_code: z.ZodString;
|
|
203
|
+
status: z.ZodEnum<["pending", "approved", "denied"]>;
|
|
204
|
+
/** The client_name/User-Agent display hint from device/code — null when the client sent none. */
|
|
205
|
+
client: z.ZodNullable<z.ZodString>;
|
|
206
|
+
/** Handshake expiry, epoch MILLISECONDS. */
|
|
207
|
+
expires_at: z.ZodNumber;
|
|
208
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
209
|
+
user_code: z.ZodString;
|
|
210
|
+
status: z.ZodEnum<["pending", "approved", "denied"]>;
|
|
211
|
+
/** The client_name/User-Agent display hint from device/code — null when the client sent none. */
|
|
212
|
+
client: z.ZodNullable<z.ZodString>;
|
|
213
|
+
/** Handshake expiry, epoch MILLISECONDS. */
|
|
214
|
+
expires_at: z.ZodNumber;
|
|
215
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
216
|
+
export type DeviceApproveLookupResponse = z.infer<typeof DeviceApproveLookupResponse>;
|
|
217
|
+
/** POST body (JSON ONLY — the JSON content type is part of the cross-site-request defence; a cross-site
|
|
218
|
+
* form cannot send application/json without a CORS preflight). The approving identity comes from the
|
|
219
|
+
* session cookie exclusively — NEVER from this body. */
|
|
220
|
+
export declare const DeviceApproveRequest: z.ZodObject<{
|
|
221
|
+
/** Case-insensitive; dashes/spaces optional — see normalizeUserCode. */
|
|
222
|
+
user_code: z.ZodString;
|
|
223
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
224
|
+
/** Case-insensitive; dashes/spaces optional — see normalizeUserCode. */
|
|
225
|
+
user_code: z.ZodString;
|
|
226
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
227
|
+
/** Case-insensitive; dashes/spaces optional — see normalizeUserCode. */
|
|
228
|
+
user_code: z.ZodString;
|
|
229
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
230
|
+
export type DeviceApproveRequest = z.infer<typeof DeviceApproveRequest>;
|
|
231
|
+
export declare const DeviceApproveResponse: z.ZodObject<{
|
|
232
|
+
ok: z.ZodLiteral<true>;
|
|
233
|
+
user_code: z.ZodString;
|
|
234
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
235
|
+
ok: z.ZodLiteral<true>;
|
|
236
|
+
user_code: z.ZodString;
|
|
237
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
238
|
+
ok: z.ZodLiteral<true>;
|
|
239
|
+
user_code: z.ZodString;
|
|
240
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
241
|
+
export type DeviceApproveResponse = z.infer<typeof DeviceApproveResponse>;
|
|
242
|
+
/**
|
|
243
|
+
* Canonicalize a human-typed user code to the wire shape "XXXX-XXXX": uppercase, dashes/spaces optional.
|
|
244
|
+
* null = not a plausible code (wrong length/charset after stripping separators). Same normalization the
|
|
245
|
+
* server applies — run it client-side for instant feedback.
|
|
246
|
+
*/
|
|
247
|
+
export declare function normalizeUserCode(input: unknown): string | null;
|
|
248
|
+
/**
|
|
249
|
+
* The RFC 8628 §3.5 poll-loop step: what interval to poll with next, and whether to keep going.
|
|
250
|
+
* - authorization_pending → keep polling at the current interval
|
|
251
|
+
* - slow_down → keep polling, interval + 5s (the RFC-mandated increment)
|
|
252
|
+
* - anything else → stop (expired_token/access_denied = terminal; invalid_* = client bug)
|
|
253
|
+
*/
|
|
254
|
+
export declare function nextPollInterval(currentIntervalSeconds: number, error: OAuthErrorCode): {
|
|
255
|
+
continue: boolean;
|
|
256
|
+
intervalSeconds: number;
|
|
257
|
+
};
|
|
258
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"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"}
|