tiny-http-mcp-server 0.1.34 → 0.1.36
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/dist/composition.json +1 -1
- package/node_modules/mcp-oauth/README.md +23 -2
- package/node_modules/mcp-oauth/dist/client/client-registration.d.ts +2 -0
- package/node_modules/mcp-oauth/dist/client/client-registration.js +45 -0
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +58 -7
- package/node_modules/mcp-oauth/dist/client/types.d.ts +11 -1
- package/node_modules/mcp-oauth/dist/index.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +11 -1
- package/node_modules/tiny-mcp-client/dist/index.js +607 -518
- package/package.json +1 -1
package/dist/composition.json
CHANGED
|
@@ -106,8 +106,13 @@ Select a separate persistence namespace for another scope profile. No scope is
|
|
|
106
106
|
invented when the client does not configure one.
|
|
107
107
|
|
|
108
108
|
Imported `initialGrant.tokens` use `accessToken`, optional `refreshToken`,
|
|
109
|
-
`tokenType: "Bearer"`, `expiresAt` (Unix epoch milliseconds or `null`
|
|
110
|
-
and optional `scope`.
|
|
109
|
+
`tokenType: "Bearer"`, optional `expiresAt` (Unix epoch milliseconds or `null`
|
|
110
|
+
if unknown), and optional `scope`. `expiresIn` is a lifetime in seconds and is
|
|
111
|
+
anchored once at import. For a delayed import, provide the original `issuedAt`
|
|
112
|
+
in epoch milliseconds or its real absolute `expiresAt`; a numeric absolute
|
|
113
|
+
expiry takes precedence. Without either, the relative value means remaining
|
|
114
|
+
lifetime at import. Omitted expiry stays unknown. A fresh imported token is
|
|
115
|
+
used only for its resource.
|
|
111
116
|
Discovery binds an expired or explicitly rejected grant before silent refresh,
|
|
112
117
|
using the original configured client. Persisted sessions take precedence,
|
|
113
118
|
including sessions whose tokens have been cleared; an import cannot revive them.
|
|
@@ -119,6 +124,22 @@ Explicit ID/secret values must agree with the imported response. Sessions and
|
|
|
119
124
|
native registration stores retain arrays, issuance/expiry timestamps and JSON
|
|
120
125
|
provider metadata. Registration input is copied, bounded to 64 KiB and 64
|
|
121
126
|
levels, and rejects invalid standard field types and non-JSON extensions.
|
|
127
|
+
An optional registration `issuer` must match discovery and the persisted
|
|
128
|
+
authorization server exactly. Contradictory metadata fails without activating
|
|
129
|
+
or redeeming the grant. `client_secret_expires_at` uses Unix epoch seconds;
|
|
130
|
+
zero means no expiry. Live access tokens remain usable after secret expiry,
|
|
131
|
+
but an expired secret is never submitted for refresh. Native DCR can replace
|
|
132
|
+
an expired registration during explicit authorization; caller-owned imports
|
|
133
|
+
must be updated. Headless requests retain the old record and report recovery
|
|
134
|
+
is required without creating a pending refresh marker.
|
|
135
|
+
Native registrations retain `requestedRedirectUri`, the actual listener URI
|
|
136
|
+
submitted to DCR, separately from the full response metadata. Fresh responses
|
|
137
|
+
may normalize a loopback port or represent IPv4 loopback as portless localhost;
|
|
138
|
+
host, path, scheme, query and fragment differences outside that boundary fail.
|
|
139
|
+
Authorization and code exchange always use the actual listener URI. Silent
|
|
140
|
+
refresh keeps its original client regardless of callback changes. At interactive
|
|
141
|
+
authorization, a native registration with an obsolete captured callback is
|
|
142
|
+
replaced; caller-owned full registration imports retain their original identity.
|
|
122
143
|
Set `client.tokenEndpointAuthMethod` to `none`, `client_secret_post` or
|
|
123
144
|
`client_secret_basic`; a full registration can supply the same field as
|
|
124
145
|
`token_endpoint_auth_method`. Public clients never transmit a stored secret.
|
|
@@ -2,3 +2,5 @@ import type { OAuthClientRegistration, StoredOAuthClient } from "./types.js";
|
|
|
2
2
|
/** Validate and copy a bounded JSON DCR response without quoting credential input. */
|
|
3
3
|
export declare function parseOAuthClientRegistration(value: unknown): OAuthClientRegistration;
|
|
4
4
|
export declare function normalizeStoredOAuthClient(value: unknown): StoredOAuthClient | null;
|
|
5
|
+
/** Fresh DCR may describe a normalized loopback port; saved identity stays exact. */
|
|
6
|
+
export declare function registrationMatchesRedirect(client: StoredOAuthClient, requestedUri: string, fresh?: boolean): boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { loopbackTarget } from "./loopback-authorization.js";
|
|
1
2
|
import { normalizeOAuthScope } from "./scope.js";
|
|
2
3
|
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
3
4
|
/** Validate and copy a bounded JSON DCR response without quoting credential input. */
|
|
@@ -84,6 +85,18 @@ export function normalizeStoredOAuthClient(value) {
|
|
|
84
85
|
(clientSecret !== undefined && (typeof clientSecret !== "string" || clientSecret.trim() === "")))
|
|
85
86
|
return null;
|
|
86
87
|
const client = { clientId: clientId.trim(), ...(clientSecret === undefined ? {} : { clientSecret: clientSecret.trim() }) };
|
|
88
|
+
const requestedRedirectUri = Object.hasOwn(record, "requestedRedirectUri") ? record.requestedRedirectUri : undefined;
|
|
89
|
+
if (requestedRedirectUri !== undefined) {
|
|
90
|
+
try {
|
|
91
|
+
if (typeof requestedRedirectUri !== "string")
|
|
92
|
+
throw new Error("Invalid redirect identity");
|
|
93
|
+
loopbackTarget({ redirectUri: requestedRedirectUri });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new Error("Invalid stored OAuth registration redirect identity");
|
|
97
|
+
}
|
|
98
|
+
client.requestedRedirectUri = requestedRedirectUri;
|
|
99
|
+
}
|
|
87
100
|
const method = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(record, "tokenEndpointAuthMethod") ? record.tokenEndpointAuthMethod : undefined);
|
|
88
101
|
if (Object.hasOwn(record, "registration") && record.registration !== undefined) {
|
|
89
102
|
const registration = parseOAuthClientRegistration(record.registration);
|
|
@@ -101,3 +114,35 @@ export function normalizeStoredOAuthClient(value) {
|
|
|
101
114
|
client.tokenEndpointAuthMethod = method;
|
|
102
115
|
return client;
|
|
103
116
|
}
|
|
117
|
+
/** Fresh DCR may describe a normalized loopback port; saved identity stays exact. */
|
|
118
|
+
export function registrationMatchesRedirect(client, requestedUri, fresh = false) {
|
|
119
|
+
if (client.requestedRedirectUri !== undefined)
|
|
120
|
+
return client.requestedRedirectUri === requestedUri;
|
|
121
|
+
const redirects = client.registration?.redirect_uris;
|
|
122
|
+
if (redirects === undefined || redirects === null || redirects.length === 0)
|
|
123
|
+
return true;
|
|
124
|
+
return redirects.some(returnedUri => {
|
|
125
|
+
if (returnedUri === requestedUri)
|
|
126
|
+
return true;
|
|
127
|
+
if (!fresh)
|
|
128
|
+
return false;
|
|
129
|
+
let returned, requested;
|
|
130
|
+
try {
|
|
131
|
+
returned = new URL(returnedUri);
|
|
132
|
+
requested = new URL(requestedUri);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (requested.protocol !== "http:" || returned.protocol !== "http:" ||
|
|
138
|
+
!["127.0.0.1", "[::1]", "localhost"].includes(requested.hostname))
|
|
139
|
+
return false;
|
|
140
|
+
const sameHost = returned.hostname === requested.hostname;
|
|
141
|
+
const normalizedIpv4 = requested.hostname === "127.0.0.1" && returned.hostname === "localhost" && returned.port === "";
|
|
142
|
+
if (!sameHost && !normalizedIpv4)
|
|
143
|
+
return false;
|
|
144
|
+
returned.hostname = requested.hostname;
|
|
145
|
+
returned.port = requested.port;
|
|
146
|
+
return returned.href === requested.href;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normalizeStoredOAuthClient, parseOAuthClientRegistration } from "./client-registration.js";
|
|
1
|
+
import { normalizeStoredOAuthClient, parseOAuthClientRegistration, registrationMatchesRedirect } from "./client-registration.js";
|
|
2
2
|
import { normalizeOAuthScope } from "./scope.js";
|
|
3
3
|
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
4
4
|
import { isIP } from "node:net";
|
|
@@ -43,7 +43,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
43
43
|
}
|
|
44
44
|
const initialGrant = options.initialGrant === undefined ? undefined : {
|
|
45
45
|
resource: canonicalizeResourceIndicator(options.initialGrant.resource),
|
|
46
|
-
tokens:
|
|
46
|
+
tokens: normalizeImportedTokens(options.initialGrant.tokens, now),
|
|
47
47
|
client: configuredClient
|
|
48
48
|
};
|
|
49
49
|
if (initialGrant !== undefined && (initialGrant.tokens === undefined || initialGrant.client === null))
|
|
@@ -127,6 +127,10 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
127
127
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
128
128
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
129
129
|
let session = await loadSession(canonicalResource);
|
|
130
|
+
if (session !== null)
|
|
131
|
+
assertRegistrationIssuer(session.client, session.authorizationServer);
|
|
132
|
+
if (configuredClient !== null && discovery !== undefined)
|
|
133
|
+
assertRegistrationIssuer(configuredClient, discovery.authorizationServer);
|
|
130
134
|
if (session !== null && initialGrant?.resource === canonicalResource)
|
|
131
135
|
initialGrantConsumed = true;
|
|
132
136
|
signal?.throwIfAborted();
|
|
@@ -173,6 +177,11 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
173
177
|
if (session?.tokens?.refreshToken !== undefined &&
|
|
174
178
|
sessionDiscovery !== undefined &&
|
|
175
179
|
(forceRefresh || isExpired(session.tokens, now))) {
|
|
180
|
+
if (hasExpiredClientSecret(session.client, now)) {
|
|
181
|
+
if (!allowInteractive || options.allowInteractive === false || options.client.mode === "static" || configuredClient?.registration !== undefined)
|
|
182
|
+
throw new Error("OAuth client secret has expired; authorize again or update the imported registration");
|
|
183
|
+
return authorizeSession(canonicalResource, clearSessionTokens(session), sessionDiscovery, fetch, signal);
|
|
184
|
+
}
|
|
176
185
|
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch, signal);
|
|
177
186
|
if (session?.tokens !== undefined && !isExpired(session.tokens, now)) {
|
|
178
187
|
return session;
|
|
@@ -343,6 +352,9 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
343
352
|
if (configuredClient === null) {
|
|
344
353
|
throw new Error("OAuth client_id must not be blank");
|
|
345
354
|
}
|
|
355
|
+
assertRegistrationIssuer(configuredClient, discovery.authorizationServer);
|
|
356
|
+
if (hasExpiredClientSecret(configuredClient, now))
|
|
357
|
+
throw new Error("OAuth client secret has expired; update the imported registration");
|
|
346
358
|
return {
|
|
347
359
|
kind: "static",
|
|
348
360
|
fromStoredRegistration: false,
|
|
@@ -357,7 +369,14 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
357
369
|
client: configuredClient
|
|
358
370
|
};
|
|
359
371
|
}
|
|
360
|
-
|
|
372
|
+
let storedClient = await loadRegisteredClient(discovery.authorizationServer);
|
|
373
|
+
if (storedClient !== null) {
|
|
374
|
+
assertRegistrationIssuer(storedClient, discovery.authorizationServer);
|
|
375
|
+
if (hasExpiredClientSecret(storedClient, now) || !registrationMatchesRedirect(storedClient, redirectUri)) {
|
|
376
|
+
await clearRegisteredClient(discovery.authorizationServer);
|
|
377
|
+
storedClient = null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
361
380
|
if (storedClient !== null) {
|
|
362
381
|
return {
|
|
363
382
|
kind: "dynamic",
|
|
@@ -366,7 +385,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
366
385
|
};
|
|
367
386
|
}
|
|
368
387
|
if (registrationEndpoint === undefined) {
|
|
369
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
388
|
+
if (existingSession !== null && existingSession.client.clientId.length > 0 && !hasExpiredClientSecret(existingSession.client, now) && registrationMatchesRedirect(existingSession.client, redirectUri)) {
|
|
370
389
|
return {
|
|
371
390
|
kind: "dynamic",
|
|
372
391
|
fromStoredRegistration: true,
|
|
@@ -375,7 +394,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
375
394
|
}
|
|
376
395
|
throw new Error("Authorization server metadata is missing registration_endpoint");
|
|
377
396
|
}
|
|
378
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
397
|
+
if (existingSession !== null && existingSession.client.clientId.length > 0 && !hasExpiredClientSecret(existingSession.client, now) && registrationMatchesRedirect(existingSession.client, redirectUri)) {
|
|
379
398
|
const isConfiguredStaticFallback = configuredClient !== null &&
|
|
380
399
|
existingSession.client.clientId === configuredClient.clientId &&
|
|
381
400
|
existingSession.client.clientSecret === configuredClient.clientSecret;
|
|
@@ -415,11 +434,17 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
415
434
|
...(responseMethod === undefined ? {} : { tokenEndpointAuthMethod: responseMethod }),
|
|
416
435
|
registration
|
|
417
436
|
};
|
|
418
|
-
|
|
437
|
+
assertRegistrationIssuer(registeredClient, discovery.authorizationServer);
|
|
438
|
+
if (hasExpiredClientSecret(registeredClient, now))
|
|
439
|
+
throw new Error("OAuth client secret has expired in the registration response");
|
|
440
|
+
if (!registrationMatchesRedirect(registeredClient, redirectUri, true))
|
|
441
|
+
throw new Error("OAuth registration response does not match the requested redirect URI");
|
|
442
|
+
const clientWithRedirect = { ...registeredClient, requestedRedirectUri: redirectUri };
|
|
443
|
+
await saveRegisteredClient(discovery.authorizationServer, clientWithRedirect);
|
|
419
444
|
return {
|
|
420
445
|
kind: "dynamic",
|
|
421
446
|
fromStoredRegistration: false,
|
|
422
|
-
client:
|
|
447
|
+
client: clientWithRedirect
|
|
423
448
|
};
|
|
424
449
|
}
|
|
425
450
|
async function loadSession(resource) {
|
|
@@ -528,6 +553,21 @@ function normalizeLoadedSession(session) {
|
|
|
528
553
|
tokens: normalizeStoredTokens(getOwnEntry(session, "tokens"))
|
|
529
554
|
};
|
|
530
555
|
}
|
|
556
|
+
function normalizeImportedTokens(value, now) {
|
|
557
|
+
if (!isObjectRecord(value))
|
|
558
|
+
return undefined;
|
|
559
|
+
const absolute = getOwnEntry(value, "expiresAt");
|
|
560
|
+
const lifetime = getOwnEntry(value, "expiresIn");
|
|
561
|
+
const issuedAt = getOwnEntry(value, "issuedAt");
|
|
562
|
+
if (lifetime !== undefined && (typeof lifetime !== "number" || !Number.isSafeInteger(lifetime) || lifetime < 0))
|
|
563
|
+
throw new Error("OAuth initial grant has invalid relative expiry");
|
|
564
|
+
if (issuedAt !== undefined && (typeof issuedAt !== "number" || !Number.isSafeInteger(issuedAt) ||
|
|
565
|
+
Math.abs(issuedAt) > MAX_JS_DATE_MS))
|
|
566
|
+
throw new Error("OAuth initial grant has invalid issuance time");
|
|
567
|
+
const expiresAt = absolute !== undefined && absolute !== null ? absolute :
|
|
568
|
+
lifetime === undefined ? null : (issuedAt === undefined ? now() : issuedAt) + lifetime * 1000;
|
|
569
|
+
return normalizeStoredTokens({ ...value, expiresAt });
|
|
570
|
+
}
|
|
531
571
|
function normalizeStoredTokens(value) {
|
|
532
572
|
if (value === undefined || !isObjectRecord(value)) {
|
|
533
573
|
return undefined;
|
|
@@ -689,6 +729,17 @@ function assertRequestMatchesResource(requestUrl, resource) {
|
|
|
689
729
|
throw new Error(`OAuth request URL ${requestUrl} does not match discovered resource ${resource}`);
|
|
690
730
|
}
|
|
691
731
|
}
|
|
732
|
+
function assertRegistrationIssuer(client, issuer) {
|
|
733
|
+
const registrationIssuer = client.registration === undefined ? undefined : getOwnString(client.registration, "issuer");
|
|
734
|
+
if (registrationIssuer !== undefined && registrationIssuer !== issuer)
|
|
735
|
+
throw new Error("OAuth client registration issuer does not match the authorization server");
|
|
736
|
+
}
|
|
737
|
+
function hasExpiredClientSecret(client, now) {
|
|
738
|
+
if (client.clientSecret === undefined || client.tokenEndpointAuthMethod === "none" || client.registration === undefined)
|
|
739
|
+
return false;
|
|
740
|
+
const expiry = getOwnEntry(client.registration, "client_secret_expires_at");
|
|
741
|
+
return typeof expiry === "number" && expiry !== 0 && expiry <= now() / 1000;
|
|
742
|
+
}
|
|
692
743
|
function getSupportedTokenAuthMethods(metadata) {
|
|
693
744
|
const value = getOwnEntry(metadata, "token_endpoint_auth_methods_supported");
|
|
694
745
|
if (value === undefined)
|
|
@@ -70,6 +70,14 @@ export interface StoredOAuthTokens {
|
|
|
70
70
|
expiresAt: number | null;
|
|
71
71
|
scope?: string;
|
|
72
72
|
}
|
|
73
|
+
/** Import-time lifetimes are normalized once into persisted epoch milliseconds. */
|
|
74
|
+
export interface ImportedOAuthTokens extends Omit<StoredOAuthTokens, "expiresAt"> {
|
|
75
|
+
expiresAt?: number | null;
|
|
76
|
+
/** Remaining lifetime at import, or total lifetime when issuedAt is provided. */
|
|
77
|
+
expiresIn?: number;
|
|
78
|
+
/** Original issuance time in Unix epoch milliseconds. */
|
|
79
|
+
issuedAt?: number;
|
|
80
|
+
}
|
|
73
81
|
/** Full RFC 7591 response, including JSON provider extensions. */
|
|
74
82
|
export interface OAuthClientRegistration extends Record<string, unknown> {
|
|
75
83
|
client_id: string;
|
|
@@ -84,6 +92,8 @@ export interface OAuthClientRegistration extends Record<string, unknown> {
|
|
|
84
92
|
}
|
|
85
93
|
export interface StoredOAuthClient {
|
|
86
94
|
clientId: string;
|
|
95
|
+
/** Actual listener URI submitted when this native client was registered. */
|
|
96
|
+
requestedRedirectUri?: string;
|
|
87
97
|
clientSecret?: string;
|
|
88
98
|
registration?: OAuthClientRegistration;
|
|
89
99
|
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
@@ -140,7 +150,7 @@ export interface DefaultOAuthClientProviderOptions {
|
|
|
140
150
|
/** Import an existing grant for one resource. Persisted sessions take precedence. */
|
|
141
151
|
initialGrant?: {
|
|
142
152
|
resource: string;
|
|
143
|
-
tokens:
|
|
153
|
+
tokens: ImportedOAuthTokens;
|
|
144
154
|
};
|
|
145
155
|
browser: {
|
|
146
156
|
openBrowser?(url: string): Promise<void>;
|
|
@@ -6,7 +6,7 @@ export { generateCodeChallenge, generateCodeVerifier, } from "./client/pkce.js";
|
|
|
6
6
|
export { OAuthError, } from "./client/token-endpoint.js";
|
|
7
7
|
export { canonicalizeResourceIndicator, } from "./resource-indicator.js";
|
|
8
8
|
export { createJwksTokenVerifier, } from "./server/jwks-token-verifier.js";
|
|
9
|
-
export type { DefaultOAuthClientProviderOptions, OAuthAuthorizationServerMetadata, OAuthClientMetadata, OAuthClientRegistration, OAuthClientProvider, OAuthClientProviderOptions, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthSessionStore, OAuthUnauthorizedChallenge, OAuthTokenEndpointAuthMethod, StoredOAuthSession, StoredOAuthClient, StoredOAuthTokens, } from "./client/types.js";
|
|
9
|
+
export type { DefaultOAuthClientProviderOptions, OAuthAuthorizationServerMetadata, OAuthClientMetadata, OAuthClientRegistration, OAuthClientProvider, OAuthClientProviderOptions, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthSessionStore, OAuthUnauthorizedChallenge, OAuthTokenEndpointAuthMethod, StoredOAuthSession, StoredOAuthClient, StoredOAuthTokens, ImportedOAuthTokens, } from "./client/types.js";
|
|
10
10
|
export type { JwksTokenVerifier, JwksTokenVerifierOptions, JwksVerifiedAccessToken, } from "./server/jwks-token-verifier.js";
|
|
11
11
|
export type { LoopbackAuthorizationOptions, LoopbackAuthorizationSession, OAuthLandingPage, } from "./client/loopback-authorization.js";
|
|
12
12
|
export { readBoundedResponseText } from "./http-response.js";
|
|
@@ -173,6 +173,14 @@ interface StoredOAuthTokens {
|
|
|
173
173
|
expiresAt: number | null;
|
|
174
174
|
scope?: string;
|
|
175
175
|
}
|
|
176
|
+
/** Import-time lifetimes are normalized once into persisted epoch milliseconds. */
|
|
177
|
+
interface ImportedOAuthTokens extends Omit<StoredOAuthTokens, "expiresAt"> {
|
|
178
|
+
expiresAt?: number | null;
|
|
179
|
+
/** Remaining lifetime at import, or total lifetime when issuedAt is provided. */
|
|
180
|
+
expiresIn?: number;
|
|
181
|
+
/** Original issuance time in Unix epoch milliseconds. */
|
|
182
|
+
issuedAt?: number;
|
|
183
|
+
}
|
|
176
184
|
/** Full RFC 7591 response, including JSON provider extensions. */
|
|
177
185
|
interface OAuthClientRegistration extends Record<string, unknown> {
|
|
178
186
|
client_id: string;
|
|
@@ -187,6 +195,8 @@ interface OAuthClientRegistration extends Record<string, unknown> {
|
|
|
187
195
|
}
|
|
188
196
|
interface StoredOAuthClient {
|
|
189
197
|
clientId: string;
|
|
198
|
+
/** Actual listener URI submitted when this native client was registered. */
|
|
199
|
+
requestedRedirectUri?: string;
|
|
190
200
|
clientSecret?: string;
|
|
191
201
|
registration?: OAuthClientRegistration;
|
|
192
202
|
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
@@ -243,7 +253,7 @@ interface DefaultOAuthClientProviderOptions {
|
|
|
243
253
|
/** Import an existing grant for one resource. Persisted sessions take precedence. */
|
|
244
254
|
initialGrant?: {
|
|
245
255
|
resource: string;
|
|
246
|
-
tokens:
|
|
256
|
+
tokens: ImportedOAuthTokens;
|
|
247
257
|
};
|
|
248
258
|
browser: {
|
|
249
259
|
openBrowser?(url: string): Promise<void>;
|