@typeship-ax/mcp 0.8.0 → 0.9.1

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.
Files changed (103) hide show
  1. package/AGENTS.md +31 -0
  2. package/README.md +66 -9
  3. package/api.json +1617 -712
  4. package/api.md +8292 -382
  5. package/dist/api-identity.d.ts +40 -0
  6. package/dist/api-identity.d.ts.map +1 -0
  7. package/dist/api-identity.js +128 -0
  8. package/dist/auth-profiles.d.ts +30 -0
  9. package/dist/auth-profiles.d.ts.map +1 -0
  10. package/dist/auth-profiles.js +138 -0
  11. package/dist/core/http.d.ts +17 -2
  12. package/dist/core/http.d.ts.map +1 -1
  13. package/dist/core/http.js +78 -17
  14. package/dist/credential-storage.d.ts +24 -0
  15. package/dist/credential-storage.d.ts.map +1 -0
  16. package/dist/credential-storage.js +207 -0
  17. package/dist/docs.d.ts +25 -0
  18. package/dist/docs.d.ts.map +1 -1
  19. package/dist/docs.js +144 -0
  20. package/dist/errors.d.ts +18 -10
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +24 -14
  23. package/dist/index.d.ts +10 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +20 -4
  26. package/dist/mcp-authorization.d.ts +52 -0
  27. package/dist/mcp-authorization.d.ts.map +1 -0
  28. package/dist/mcp-authorization.js +232 -0
  29. package/dist/mcp-protocol.d.ts +51 -2
  30. package/dist/mcp-protocol.d.ts.map +1 -1
  31. package/dist/mcp-protocol.js +249 -37
  32. package/dist/mcp.d.ts +21 -3
  33. package/dist/mcp.d.ts.map +1 -1
  34. package/dist/mcp.js +185 -68
  35. package/dist/named-credentials.d.ts +21 -0
  36. package/dist/named-credentials.d.ts.map +1 -0
  37. package/dist/named-credentials.js +86 -0
  38. package/dist/oauth-request.d.ts +21 -0
  39. package/dist/oauth-request.d.ts.map +1 -0
  40. package/dist/oauth-request.js +119 -0
  41. package/dist/oauth-session.d.ts +106 -0
  42. package/dist/oauth-session.d.ts.map +1 -0
  43. package/dist/oauth-session.js +244 -0
  44. package/dist/ops.d.ts +14 -1
  45. package/dist/ops.d.ts.map +1 -1
  46. package/dist/ops.js +30 -30
  47. package/dist/resources/account.d.ts +2 -2
  48. package/dist/resources/account.d.ts.map +1 -1
  49. package/dist/resources/account.js +1 -0
  50. package/dist/resources/api-keys.d.ts +3 -3
  51. package/dist/resources/api-keys.d.ts.map +1 -1
  52. package/dist/resources/api-keys.js +2 -0
  53. package/dist/resources/definition-revisions.d.ts +5 -5
  54. package/dist/resources/definition-revisions.d.ts.map +1 -1
  55. package/dist/resources/definition-revisions.js +4 -0
  56. package/dist/resources/definitions.d.ts +15 -4
  57. package/dist/resources/definitions.d.ts.map +1 -1
  58. package/dist/resources/definitions.js +11 -2
  59. package/dist/resources/generate.d.ts +14 -3
  60. package/dist/resources/generate.d.ts.map +1 -1
  61. package/dist/resources/generate.js +10 -2
  62. package/dist/resources/generations.d.ts +3 -3
  63. package/dist/resources/generations.d.ts.map +1 -1
  64. package/dist/resources/generations.js +2 -0
  65. package/dist/resources/projects.d.ts +56 -20
  66. package/dist/resources/projects.d.ts.map +1 -1
  67. package/dist/resources/projects.js +40 -4
  68. package/dist/resources/targets.d.ts +20 -9
  69. package/dist/resources/targets.d.ts.map +1 -1
  70. package/dist/resources/targets.js +14 -1
  71. package/dist/schemas.d.ts.map +1 -1
  72. package/dist/schemas.js +38 -22
  73. package/dist/types.d.ts +385 -119
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js +11 -0
  76. package/dist/worker.js +4 -4
  77. package/package.json +11 -1
  78. package/server.json +42 -0
  79. package/src/api-identity.ts +98 -0
  80. package/src/auth-profiles.ts +114 -0
  81. package/src/core/http.ts +88 -19
  82. package/src/credential-storage.ts +183 -0
  83. package/src/docs.ts +138 -0
  84. package/src/errors.ts +26 -15
  85. package/src/index.ts +29 -4
  86. package/src/mcp-authorization.ts +211 -0
  87. package/src/mcp-protocol.ts +287 -38
  88. package/src/mcp.ts +186 -72
  89. package/src/named-credentials.ts +74 -0
  90. package/src/oauth-request.ts +90 -0
  91. package/src/oauth-session.ts +258 -0
  92. package/src/ops.ts +44 -31
  93. package/src/resources/account.ts +3 -0
  94. package/src/resources/api-keys.ts +5 -0
  95. package/src/resources/definition-revisions.ts +9 -0
  96. package/src/resources/definitions.ts +25 -0
  97. package/src/resources/generate.ts +23 -0
  98. package/src/resources/generations.ts +5 -0
  99. package/src/resources/projects.ts +95 -7
  100. package/src/resources/targets.ts +32 -0
  101. package/src/schemas.ts +38 -22
  102. package/src/types.ts +404 -119
  103. package/src/worker.ts +4 -4
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  HttpCore,
6
6
  formatDebugEvent,
7
7
  type AuthValue,
8
+ type SecurityCredential,
8
9
  type DebugEvent,
9
10
  type RequestContext,
10
11
  bearerAuth,
@@ -21,8 +22,8 @@ import { AccountResource } from "./resources/account.js";
21
22
  import { ApiKeysResource } from "./resources/api-keys.js";
22
23
 
23
24
  /** This package's version, also sent as the `User-Agent`. */
24
- export const VERSION = "0.8.0";
25
- const USER_AGENT = "@typeship-ax/mcp/0.8.0 (typeship)";
25
+ export const VERSION = "0.9.1";
26
+ const USER_AGENT = "@typeship-ax/mcp/0.9.1 (typeship)";
26
27
 
27
28
  export interface ClientOptions {
28
29
  /** Override the server URL. Default: `https://typeship.dev/api/v1` */
@@ -32,6 +33,13 @@ export interface ClientOptions {
32
33
  * before every attempt.
33
34
  */
34
35
  bearerToken?: string | (() => string | Promise<string>);
36
+ /**
37
+ * Credentials keyed by the Definition's security scheme names. Only one complete alternative is
38
+ * sent for each operation. Named values take precedence over convenience options.
39
+ */
40
+ credentials?: {
41
+ "apiKey"?: AuthValue;
42
+ };
35
43
  /** Per-attempt timeout in milliseconds. Default: 30000. */
36
44
  timeoutMs?: number;
37
45
  /** Retries after the first attempt (retryable failures only). Default: 2. */
@@ -71,7 +79,7 @@ export interface ClientOptions {
71
79
  }
72
80
 
73
81
  /**
74
- * typeship — v0.8.0
82
+ * typeship — v0.9.1
75
83
  *
76
84
  * Resolve an OpenAPI or GraphQL Definition, diagnose it, and keep every
77
85
  * selected SDK, CLI, and MCP Target current.
@@ -105,7 +113,23 @@ export class TypeshipClient {
105
113
  ? (event: DebugEvent) => console.error(formatDebugEvent("typeship", event))
106
114
  : undefined;
107
115
  const query: Record<string, AuthValue> = {};
108
- if (options.bearerToken !== undefined) headers["Authorization"] = bearerAuth(options.bearerToken);
116
+ const authHeaders: Record<string, AuthValue> = {};
117
+ const authQuery: Record<string, AuthValue> = {};
118
+
119
+ let bearerCredential: AuthValue | undefined;
120
+
121
+ if (options.bearerToken !== undefined) bearerCredential = bearerAuth(options.bearerToken);
122
+
123
+ const allowedCredentials = new Set<string>(["apiKey"]);
124
+ for (const name of Object.keys(options.credentials ?? {})) {
125
+ if (!allowedCredentials.has(name)) throw new Error("Unknown security scheme: " + name);
126
+ }
127
+ const credentials: Record<string, SecurityCredential> = Object.create(null);
128
+ {
129
+ const named = options.credentials && Object.hasOwn(options.credentials, "apiKey") ? options.credentials["apiKey"] : undefined;
130
+ const value = named !== undefined ? bearerAuth(named) : bearerCredential;
131
+ if (value !== undefined) credentials["apiKey"] = { headers: { ["Authorization"]: value } };
132
+ }
109
133
  const v = options.validate;
110
134
  const validate = v
111
135
  ? {
@@ -118,6 +142,7 @@ export class TypeshipClient {
118
142
  baseUrl: options.baseUrl ?? "https://typeship.dev/api/v1",
119
143
  headers,
120
144
  query,
145
+ credentials,
121
146
  fetch: options.fetch ?? fetch,
122
147
  timeoutMs: options.timeoutMs ?? 30_000,
123
148
  maxRetries: options.maxRetries ?? 2,
@@ -0,0 +1,211 @@
1
+ import { oauthJsonRequest } from "./oauth-request.js";
2
+
3
+ export interface McpAuthorizationConfiguration {
4
+ /** Exact authorization-server issuer. Never inferred from an incoming token. */
5
+ issuer: string;
6
+ /** Canonical public MCP endpoint, including its path. */
7
+ resource: string;
8
+ /** Optional public signing-key endpoint; otherwise use issuer discovery. */
9
+ jwksUrl?: string;
10
+ /** Minimum connection scopes; upstream API permissions are independent. */
11
+ scopes?: readonly string[];
12
+ }
13
+
14
+ export interface McpPrincipal {
15
+ readonly issuer: string;
16
+ readonly subject: string;
17
+ readonly resource: string;
18
+ readonly scopes: readonly string[];
19
+ /** Provider claims verified by signature or authenticated introspection. */
20
+ readonly claims: Readonly<Record<string, unknown>>;
21
+ }
22
+
23
+ /** Server-only credentials. Supply at runtime, never in generation config. */
24
+ export interface McpTokenIntrospectionConfiguration {
25
+ /** Fixed provider endpoint authorized to receive this issuer's tokens. */
26
+ url: string;
27
+ clientId: string;
28
+ clientSecret: string;
29
+ authMethod?: "client_secret_basic" | "client_secret_post";
30
+ /** Entire response deadline, including the body; defaults to 10 seconds. */
31
+ timeoutMs?: number;
32
+ }
33
+
34
+ export class McpAuthorizationError extends Error {
35
+ constructor(readonly status: 401 | 403 | 503, readonly code: "invalid_token" | "insufficient_scope" | "authorization_unavailable") {
36
+ super(code === "invalid_token" ? "A valid MCP access token is required." : code === "insufficient_scope" ? "The access token lacks the required MCP permissions." : "MCP authorization is unavailable. Check the server's authorization configuration.");
37
+ this.name = "McpAuthorizationError";
38
+ }
39
+ }
40
+
41
+ const invalid = () => new McpAuthorizationError(401, "invalid_token");
42
+ const unavailable = () => new McpAuthorizationError(503, "authorization_unavailable");
43
+ const scopePattern = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
44
+
45
+ function endpoint(value: string, query = false): string {
46
+ try {
47
+ const url = new URL(value);
48
+ if (value !== value.trim() || /[\u0000-\u0020\u007F"\\]/.test(value) || url.username || url.password || url.hash || (!query && url.search) ||
49
+ !(url.protocol === "https:" || url.protocol === "http:" && ["127.0.0.1", "[::1]", "localhost"].includes(url.hostname))) throw unavailable();
50
+ return value;
51
+ } catch { throw unavailable(); }
52
+ }
53
+
54
+ /** Validate configuration independently of any request or token claims. */
55
+ export function mcpAuthorizationConfiguration(value: McpAuthorizationConfiguration): McpAuthorizationConfiguration {
56
+ if (!value || typeof value.issuer !== "string" || typeof value.resource !== "string") throw unavailable();
57
+ const issuer = endpoint(value.issuer), resource = endpoint(value.resource);
58
+ const scopes = value.scopes ?? [];
59
+ if (!Array.isArray(scopes) || scopes.length > 20 || scopes.some(s => typeof s !== "string" || s.length > 500 || !scopePattern.test(s))) throw unavailable();
60
+ return Object.freeze({ issuer, resource, ...(value.jwksUrl ? { jwksUrl: endpoint(value.jwksUrl, true) } : {}), scopes: Object.freeze([...new Set(scopes)]) });
61
+ }
62
+
63
+ function bytes(value: string): Uint8Array<ArrayBuffer> {
64
+ if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) throw invalid();
65
+ try {
66
+ const decoded = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), ch => ch.charCodeAt(0));
67
+ if (btoa(String.fromCharCode(...decoded)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") !== value) throw invalid();
68
+ return decoded;
69
+ } catch { throw invalid(); }
70
+ }
71
+
72
+ function object(value: unknown): value is Record<string, unknown> { return !!value && typeof value === "object" && !Array.isArray(value); }
73
+ function decode(value: string): Record<string, unknown> {
74
+ try { const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes(value))); if (!object(parsed)) throw invalid(); return parsed; }
75
+ catch { throw invalid(); }
76
+ }
77
+
78
+ interface SigningKey { kty?: string; kid?: string; alg?: string; use?: string; key_ops?: string[]; n?: string; e?: string; crv?: string; x?: string; y?: string; d?: string; k?: string }
79
+
80
+ function principalFor(claims: Record<string, unknown>, configuration: McpAuthorizationConfiguration): McpPrincipal {
81
+ const now = Date.now() / 1000;
82
+ const audiences = typeof claims.aud === "string" ? [claims.aud] : claims.aud;
83
+ if (claims.cnf !== undefined || claims.iss !== configuration.issuer || typeof claims.sub !== "string" || !claims.sub || claims.sub.length > 1000 ||
84
+ !Array.isArray(audiences) || !audiences.every(a => typeof a === "string") || !audiences.includes(configuration.resource) ||
85
+ typeof claims.exp !== "number" || !Number.isFinite(claims.exp) || claims.exp <= now ||
86
+ (claims.nbf !== undefined && (typeof claims.nbf !== "number" || !Number.isFinite(claims.nbf) || claims.nbf > now)) ||
87
+ (claims.iat !== undefined && (typeof claims.iat !== "number" || !Number.isFinite(claims.iat) || claims.iat > now + 60)) ||
88
+ (claims.scope !== undefined && (typeof claims.scope !== "string" || claims.scope.length > 10_000))) throw invalid();
89
+ const scopes = typeof claims.scope === "string" ? claims.scope.split(" ").filter(Boolean) : [];
90
+ if (scopes.some(s => !scopePattern.test(s))) throw invalid();
91
+ if (configuration.scopes!.some(s => !scopes.includes(s))) throw new McpAuthorizationError(403, "insufficient_scope");
92
+ return Object.freeze({ issuer: configuration.issuer, subject: claims.sub, resource: configuration.resource, scopes: Object.freeze([...new Set(scopes)]), claims: Object.freeze(claims) });
93
+ }
94
+
95
+ function introspector(input: McpTokenIntrospectionConfiguration, configuration: McpAuthorizationConfiguration) {
96
+ const url = endpoint(input.url);
97
+ const method = input.authMethod ?? "client_secret_basic", timeoutMs = input.timeoutMs ?? 10_000;
98
+ if (!["client_secret_basic", "client_secret_post"].includes(method) || !Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 30_000 ||
99
+ [input.clientId, input.clientSecret].some(value => typeof value !== "string" || !value || value.length > 16_000 || /[\u0000-\u001F\u007F]/.test(value))) throw unavailable();
100
+ // Snapshot secrets in a private closure. Public metadata/config never exposes them.
101
+ const { clientId, clientSecret } = input;
102
+ const form = (value: string) => new URLSearchParams({ v: value }).toString().slice(2);
103
+ const authorization = method === "client_secret_basic" ? "Basic " + btoa(form(clientId) + ":" + form(clientSecret)) : undefined;
104
+ return async (token: string): Promise<McpPrincipal> => {
105
+ const body = new URLSearchParams({ token, token_type_hint: "access_token" });
106
+ if (method === "client_secret_post") { body.set("client_id", clientId); body.set("client_secret", clientSecret); }
107
+ let data: Record<string, unknown> | undefined;
108
+ try {
109
+ const response = await oauthJsonRequest(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json", ...(authorization ? { Authorization: authorization } : {}) }, body }, timeoutMs);
110
+ if (response.status !== 200) throw unavailable();
111
+ data = response.data;
112
+ } catch { throw unavailable(); }
113
+ if (!data || typeof data.active !== "boolean") throw unavailable();
114
+ if (!data.active || (data.token_type !== undefined && (typeof data.token_type !== "string" || data.token_type.toLowerCase() !== "bearer"))) throw invalid();
115
+ // RFC 7662 permits omitted issuer: the owner-pinned authenticated endpoint
116
+ // establishes it. Audience, subject and expiry are mandatory for this handler.
117
+ // Never substitute client_id for the MCP resource audience.
118
+ const claims = { ...data, iss: data.iss === undefined ? configuration.issuer : data.iss };
119
+ for (const key of ["token", "access_token", "refresh_token", "id_token", "client_secret"]) delete (claims as Record<string, unknown>)[key];
120
+ return principalFor(claims, configuration);
121
+ };
122
+ }
123
+
124
+ /** Resource-server validation using platform crypto or explicit introspection. The caller supplies
125
+ * only a token; issuer, resource, discovery and key destinations are fixed by
126
+ * the owner. No Authorization header is sent during discovery/key fetching.
127
+ * Introspection replaces JWT verification for every request: no fallback and
128
+ * no token-result cache, so provider revocation is checked on the next request. */
129
+ export function createMcpAuthorizer(input: McpAuthorizationConfiguration, introspection?: McpTokenIntrospectionConfiguration) {
130
+ const configuration = mcpAuthorizationConfiguration(input);
131
+ const introspect = introspection === undefined ? undefined : introspector(introspection, configuration);
132
+ const cacheMs = 300_000, retryMs = 30_000;
133
+ let keys: SigningKey[] = [], expiresAt = 0, lastAttempt = -Infinity;
134
+ let pending: Promise<void> | undefined;
135
+ async function refresh(): Promise<void> {
136
+ if (pending) return pending;
137
+ if (Date.now() - lastAttempt < retryMs) return;
138
+ lastAttempt = Date.now();
139
+ pending = (async () => {
140
+ try {
141
+ let jwksUrl = configuration.jwksUrl;
142
+ if (!jwksUrl) {
143
+ const issuer = new URL(configuration.issuer), path = issuer.pathname.replace(/\/$/, "");
144
+ const candidates = [...new Set([
145
+ issuer.origin + "/.well-known/oauth-authorization-server" + path,
146
+ issuer.origin + "/.well-known/openid-configuration" + path,
147
+ issuer.origin + path + "/.well-known/openid-configuration",
148
+ ])];
149
+ for (const url of candidates) {
150
+ const response = await oauthJsonRequest(url, {}, 10_000);
151
+ if (response.status === 404) continue;
152
+ if (response.status !== 200 || response.data?.issuer !== configuration.issuer || typeof response.data.jwks_uri !== "string") throw unavailable();
153
+ jwksUrl = endpoint(response.data.jwks_uri, true);
154
+ break;
155
+ }
156
+ if (!jwksUrl) throw unavailable();
157
+ }
158
+ const response = await oauthJsonRequest(jwksUrl, {}, 10_000);
159
+ if (response.status !== 200 || !Array.isArray(response.data?.keys) || response.data.keys.length === 0 || response.data.keys.length > 100) throw unavailable();
160
+ keys = response.data.keys.filter(object) as SigningKey[];
161
+ expiresAt = Date.now() + cacheMs;
162
+ } catch { throw unavailable(); }
163
+ })().finally(() => { pending = undefined; });
164
+ return pending;
165
+ }
166
+ function eligible(header: Record<string, unknown>): SigningKey[] {
167
+ return keys.filter(key => (!header.kid || key.kid === header.kid) && (!key.alg || key.alg === header.alg) &&
168
+ (!key.use || key.use === "sig") && (!key.key_ops || Array.isArray(key.key_ops) && key.key_ops.includes("verify")) && !key.d && !key.k &&
169
+ (header.alg === "RS256" && key.kty === "RSA" || header.alg === "ES256" && key.kty === "EC" && key.crv === "P-256" || header.alg === "EdDSA" && key.kty === "OKP" && key.crv === "Ed25519"));
170
+ }
171
+ async function verifySignature(header: Record<string, unknown>, data: Uint8Array<ArrayBuffer>, signature: Uint8Array<ArrayBuffer>): Promise<boolean> {
172
+ const matches = eligible(header);
173
+ if (matches.length !== 1) return false;
174
+ const key = matches[0]!;
175
+ try {
176
+ const algorithm = header.alg === "RS256" ? { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } : header.alg === "ES256" ? { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" } : { name: "Ed25519" };
177
+ if (header.alg === "RS256") {
178
+ if (!key.n || bytes(key.n).byteLength < 256) return false;
179
+ }
180
+ const imported = await crypto.subtle.importKey("jwk", key, algorithm, false, ["verify"]);
181
+ if (header.alg === "RS256" && ((imported.algorithm as { modulusLength?: number }).modulusLength ?? 0) < 2048) return false;
182
+ return await crypto.subtle.verify(algorithm, imported, signature, data);
183
+ } catch { return false; }
184
+ }
185
+ return {
186
+ configuration,
187
+ metadata() { return { resource: configuration.resource, authorization_servers: [configuration.issuer], bearer_methods_supported: ["header"], scopes_supported: [...configuration.scopes!] }; },
188
+ challenge(error?: "invalid_token" | "insufficient_scope") {
189
+ const resource = new URL(configuration.resource);
190
+ const metadata = resource.origin + "/.well-known/oauth-protected-resource" + resource.pathname.replace(/\/$/, "");
191
+ return 'Bearer resource_metadata="' + metadata + '"' + (configuration.scopes!.length ? ', scope="' + configuration.scopes!.join(" ") + '"' : "") + (error ? ', error="' + error + '"' : "");
192
+ },
193
+ async authorize(header: string | null): Promise<McpPrincipal> {
194
+ if (!header || header.length > 16_400 || !/^Bearer [A-Za-z0-9._~+/-]+=*$/i.test(header)) throw invalid();
195
+ const token = header.slice(7), parts = token.split(".");
196
+ if (introspect) return introspect(token);
197
+ if (parts.length !== 3) throw invalid();
198
+ const protectedHeader = decode(parts[0]!), claims = decode(parts[1]!);
199
+ if (!["RS256", "ES256", "EdDSA"].includes(String(protectedHeader.alg)) || protectedHeader.crit !== undefined || protectedHeader.jku !== undefined || protectedHeader.x5u !== undefined ||
200
+ (protectedHeader.typ !== undefined && !["at+jwt", "JWT"].includes(String(protectedHeader.typ))) ||
201
+ (protectedHeader.kid !== undefined && (typeof protectedHeader.kid !== "string" || !protectedHeader.kid || protectedHeader.kid.length > 200))) throw invalid();
202
+ const signed = new TextEncoder().encode(parts[0] + "." + parts[1]), signature = bytes(parts[2]!);
203
+ if (expiresAt <= Date.now()) { await refresh(); if (expiresAt <= Date.now()) throw unavailable(); }
204
+ let verified = await verifySignature(protectedHeader, signed, signature);
205
+ if (!verified && Date.now() - lastAttempt >= retryMs) { await refresh(); verified = await verifySignature(protectedHeader, signed, signature); }
206
+ if (!verified) throw invalid();
207
+ // No bearer token or unverified claims cross into credential resolution.
208
+ return principalFor(claims, configuration);
209
+ },
210
+ };
211
+ }