@gydr-ai/mcp-oauth 0.1.2 → 0.1.4
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/README.md +86 -44
- package/dist/index.js +15 -15
- package/dist/index.js.map +4 -4
- package/dist/types/core/grant-expiry.d.ts +1 -0
- package/dist/types/helpers/responses.d.ts +5 -1
- package/dist/types/interfaces/options.d.ts +29 -2
- package/dist/types/interfaces/records.d.ts +16 -1
- package/dist/types/interfaces/store.d.ts +21 -4
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -57,6 +57,13 @@ export interface Lifetimes {
|
|
|
57
57
|
* (90 days).
|
|
58
58
|
*/
|
|
59
59
|
readonly grant?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Sliding limit on a grant: it also ends when it has not been renewed with
|
|
62
|
+
* a refresh token for this long, and each renewal extends it again, never
|
|
63
|
+
* beyond `grant`. `0` (off), or from `accessToken` up to 34,560,000.
|
|
64
|
+
* Default: 0.
|
|
65
|
+
*/
|
|
66
|
+
readonly grantIdle?: number;
|
|
60
67
|
/** Authorization code lifetime, 1 to 600. Default: 60. */
|
|
61
68
|
readonly authorizationCode?: number;
|
|
62
69
|
/** Time allowed for sign-in plus consent, 1 to 3600. Default: 900 (15 minutes). */
|
|
@@ -291,6 +298,16 @@ export interface McpAuthOptions {
|
|
|
291
298
|
readonly scopes?: readonly string[];
|
|
292
299
|
/** Scopes granted when a client asks for none. Default: all `scopes`. */
|
|
293
300
|
readonly defaultScopes?: readonly string[];
|
|
301
|
+
/**
|
|
302
|
+
* Grant types a registered client or metadata document gets when it names
|
|
303
|
+
* none. RFC 7591 § 2 reads an absent `grant_types` as
|
|
304
|
+
* `["authorization_code"]`, so such a client receives no refresh token;
|
|
305
|
+
* `["authorization_code", "refresh_token"]` gives it one. Must include
|
|
306
|
+
* `authorization_code`. Applies when a client registers or a metadata
|
|
307
|
+
* document is fetched, not to clients already registered, and not to
|
|
308
|
+
* `clients`, which default to both. Default: `["authorization_code"]`.
|
|
309
|
+
*/
|
|
310
|
+
readonly defaultGrantTypes?: readonly ("authorization_code" | "refresh_token")[];
|
|
294
311
|
/**
|
|
295
312
|
* Plain-language description per scope, shown on the consent screen (and
|
|
296
313
|
* escaped). Keys must be listed in `scopes`. Default: none.
|
|
@@ -318,8 +335,10 @@ export interface McpAuthOptions {
|
|
|
318
335
|
readonly lifetimes?: Lifetimes;
|
|
319
336
|
/**
|
|
320
337
|
* Seconds after a refresh token is used during which presenting it again
|
|
321
|
-
*
|
|
322
|
-
* of ending the grant;
|
|
338
|
+
* (a lost response, a retried request) returns the pair the first use
|
|
339
|
+
* issued instead of ending the grant; with a store that lacks
|
|
340
|
+
* `putRefreshTokenReplacement`, a new access token only. Never a second
|
|
341
|
+
* refresh token chain. `0` turns the window off. Default: 10.
|
|
323
342
|
*/
|
|
324
343
|
readonly refreshReuseGraceSeconds?: number;
|
|
325
344
|
/** Accept Client ID Metadata Document client ids. Default: `true`. */
|
|
@@ -566,6 +585,14 @@ export interface McpAuthOptions {
|
|
|
566
585
|
* issuer and resource, for development. Default: `false`.
|
|
567
586
|
*/
|
|
568
587
|
readonly allowInsecureLocalhost?: boolean;
|
|
588
|
+
/**
|
|
589
|
+
* Send `Strict-Transport-Security: max-age=63072000; includeSubDomains`
|
|
590
|
+
* on every response this package builds (endpoints, pages, redirects and
|
|
591
|
+
* `authenticate` / `unauthorized` refusals). Set `false` only when a proxy
|
|
592
|
+
* in front already sets the header, or when the issuer's subdomains must
|
|
593
|
+
* stay reachable over plain HTTP. Default: `true`.
|
|
594
|
+
*/
|
|
595
|
+
readonly strictTransportSecurity?: boolean;
|
|
569
596
|
/**
|
|
570
597
|
* Clock in milliseconds since the epoch, for tests. A non-finite value
|
|
571
598
|
* makes the request fail instead of disabling expiry checks.
|
|
@@ -279,9 +279,14 @@ export interface GrantRecord {
|
|
|
279
279
|
* or `McpAuth.updateGrantData` (JSON, at most `limits.grantDataBytes`).
|
|
280
280
|
*/
|
|
281
281
|
readonly data?: unknown;
|
|
282
|
+
/** The redirect URI the approved authorization was sent to. */
|
|
283
|
+
readonly redirectUri?: string;
|
|
282
284
|
/** When the person approved. */
|
|
283
285
|
readonly createdAt: number;
|
|
284
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* End of the grant; no token outlives it. With `lifetimes.grantIdle` it
|
|
288
|
+
* moves forward on each refresh, never past `createdAt + lifetimes.grant`.
|
|
289
|
+
*/
|
|
285
290
|
readonly expiresAt: number;
|
|
286
291
|
}
|
|
287
292
|
/**
|
|
@@ -305,6 +310,8 @@ export interface GrantUpstreamUpdate {
|
|
|
305
310
|
readonly upstreamAccessTokenExpiresAt?: number;
|
|
306
311
|
/** New integrator data for the grant (see `GrantRecord.data`). */
|
|
307
312
|
readonly data?: unknown;
|
|
313
|
+
/** New end of the grant, after a refresh under `lifetimes.grantIdle`. */
|
|
314
|
+
readonly expiresAt?: number;
|
|
308
315
|
}
|
|
309
316
|
/**
|
|
310
317
|
* An issued access token, stored by hash.
|
|
@@ -359,6 +366,14 @@ export interface RefreshTokenRecord {
|
|
|
359
366
|
readonly expiresAt: number;
|
|
360
367
|
/** Set once, atomically, by `OAuthStore.useRefreshToken`. */
|
|
361
368
|
readonly usedAt?: number;
|
|
369
|
+
/**
|
|
370
|
+
* The access and refresh token pair this token was exchanged for,
|
|
371
|
+
* encrypted by the `SecretSealer`. Set by the optional
|
|
372
|
+
* `OAuthStore.putRefreshTokenReplacement` after the token is spent, and
|
|
373
|
+
* returned by `getRefreshToken` and `useRefreshToken` from then on, so a
|
|
374
|
+
* retry inside `refreshReuseGraceSeconds` receives that same pair.
|
|
375
|
+
*/
|
|
376
|
+
readonly sealedReplacement?: string;
|
|
362
377
|
}
|
|
363
378
|
/**
|
|
364
379
|
* Result of `OAuthStore.useRefreshToken`.
|
|
@@ -35,8 +35,9 @@ import type { AccessTokenRecord, AuthorizationCodeRecord, ClientRecord, ConsentR
|
|
|
35
35
|
* an idle period, and when a size cap is reached evict the least recently
|
|
36
36
|
* used grant-less dynamic client first; never evict `static` clients.
|
|
37
37
|
*
|
|
38
|
-
* Only hashes of tokens and codes reach the store. Upstream tokens
|
|
39
|
-
*
|
|
38
|
+
* Only hashes of tokens and codes reach the store. Upstream tokens, and the
|
|
39
|
+
* pair a refresh token was exchanged for (`putRefreshTokenReplacement`),
|
|
40
|
+
* arrive already encrypted by the `SecretSealer`.
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
43
|
* ```ts
|
|
@@ -147,8 +148,10 @@ export interface OAuthStore {
|
|
|
147
148
|
/** Read a grant. */
|
|
148
149
|
getGrant(grantId: string): Promise<GrantRecord | undefined>;
|
|
149
150
|
/**
|
|
150
|
-
* Update
|
|
151
|
-
*
|
|
151
|
+
* Update fields of an existing grant: the upstream tokens, `data`, or
|
|
152
|
+
* `expiresAt` (a refresh under `lifetimes.grantIdle`). Save every field
|
|
153
|
+
* given. Resolves `false`, writing nothing, when the grant no longer
|
|
154
|
+
* exists.
|
|
152
155
|
*/
|
|
153
156
|
updateGrant(grantId: string, update: GrantUpstreamUpdate): Promise<boolean>;
|
|
154
157
|
/**
|
|
@@ -177,6 +180,20 @@ export interface OAuthStore {
|
|
|
177
180
|
getRefreshToken(tokenHash: string): Promise<RefreshTokenRecord | undefined>;
|
|
178
181
|
/** Atomically set `usedAt` if it is not set yet. */
|
|
179
182
|
useRefreshToken(tokenHash: string, now: number): Promise<UsedRefreshToken | undefined>;
|
|
183
|
+
/**
|
|
184
|
+
* Optional, recommended: save `sealedReplacement` on the (already used)
|
|
185
|
+
* refresh token record `tokenHash`, keeping every other field, `usedAt`
|
|
186
|
+
* included. `getRefreshToken` and `useRefreshToken` must return it from
|
|
187
|
+
* then on. A later write replaces an earlier one; nothing happens when the
|
|
188
|
+
* record is gone.
|
|
189
|
+
*
|
|
190
|
+
* With it, a refresh token presented again inside
|
|
191
|
+
* `refreshReuseGraceSeconds` receives exactly the pair its first exchange
|
|
192
|
+
* issued, so a copied refresh token can never start a second token chain.
|
|
193
|
+
* Without it, such a retry receives a new access token only, with no
|
|
194
|
+
* refresh token.
|
|
195
|
+
*/
|
|
196
|
+
putRefreshTokenReplacement?(tokenHash: string, sealedReplacement: string): Promise<void>;
|
|
180
197
|
/**
|
|
181
198
|
* Optional: remember the answer to a consent submission for a few
|
|
182
199
|
* seconds, keyed by a hash of the browser cookie and the form token, so a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gydr-ai/mcp-oauth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "OAuth 2.1 authorization server and resource-server guard for remote MCP servers, in front of any OIDC identity provider. No token passthrough, consent on every authorization, DCR and Client ID Metadata Documents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|