@mcp-z/oauth-google 1.0.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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/cjs/index.d.cts +16 -0
- package/dist/cjs/index.d.ts +16 -0
- package/dist/cjs/index.js +112 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lib/dcr-router.d.cts +44 -0
- package/dist/cjs/lib/dcr-router.d.ts +44 -0
- package/dist/cjs/lib/dcr-router.js +1189 -0
- package/dist/cjs/lib/dcr-router.js.map +1 -0
- package/dist/cjs/lib/dcr-utils.d.cts +160 -0
- package/dist/cjs/lib/dcr-utils.d.ts +160 -0
- package/dist/cjs/lib/dcr-utils.js +860 -0
- package/dist/cjs/lib/dcr-utils.js.map +1 -0
- package/dist/cjs/lib/dcr-verify.d.cts +53 -0
- package/dist/cjs/lib/dcr-verify.d.ts +53 -0
- package/dist/cjs/lib/dcr-verify.js +193 -0
- package/dist/cjs/lib/dcr-verify.js.map +1 -0
- package/dist/cjs/lib/fetch-with-timeout.d.cts +14 -0
- package/dist/cjs/lib/fetch-with-timeout.d.ts +14 -0
- package/dist/cjs/lib/fetch-with-timeout.js +257 -0
- package/dist/cjs/lib/fetch-with-timeout.js.map +1 -0
- package/dist/cjs/lib/token-verifier.d.cts +44 -0
- package/dist/cjs/lib/token-verifier.d.ts +44 -0
- package/dist/cjs/lib/token-verifier.js +253 -0
- package/dist/cjs/lib/token-verifier.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/providers/dcr.d.cts +107 -0
- package/dist/cjs/providers/dcr.d.ts +107 -0
- package/dist/cjs/providers/dcr.js +584 -0
- package/dist/cjs/providers/dcr.js.map +1 -0
- package/dist/cjs/providers/loopback-oauth.d.cts +119 -0
- package/dist/cjs/providers/loopback-oauth.d.ts +119 -0
- package/dist/cjs/providers/loopback-oauth.js +1334 -0
- package/dist/cjs/providers/loopback-oauth.js.map +1 -0
- package/dist/cjs/providers/service-account.d.cts +131 -0
- package/dist/cjs/providers/service-account.d.ts +131 -0
- package/dist/cjs/providers/service-account.js +800 -0
- package/dist/cjs/providers/service-account.js.map +1 -0
- package/dist/cjs/schemas/index.d.cts +20 -0
- package/dist/cjs/schemas/index.d.ts +20 -0
- package/dist/cjs/schemas/index.js +37 -0
- package/dist/cjs/schemas/index.js.map +1 -0
- package/dist/cjs/setup/config.d.cts +112 -0
- package/dist/cjs/setup/config.d.ts +112 -0
- package/dist/cjs/setup/config.js +236 -0
- package/dist/cjs/setup/config.js.map +1 -0
- package/dist/cjs/types.d.cts +173 -0
- package/dist/cjs/types.d.ts +173 -0
- package/dist/cjs/types.js +16 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/lib/dcr-router.d.ts +44 -0
- package/dist/esm/lib/dcr-router.js +515 -0
- package/dist/esm/lib/dcr-router.js.map +1 -0
- package/dist/esm/lib/dcr-utils.d.ts +160 -0
- package/dist/esm/lib/dcr-utils.js +270 -0
- package/dist/esm/lib/dcr-utils.js.map +1 -0
- package/dist/esm/lib/dcr-verify.d.ts +53 -0
- package/dist/esm/lib/dcr-verify.js +53 -0
- package/dist/esm/lib/dcr-verify.js.map +1 -0
- package/dist/esm/lib/fetch-with-timeout.d.ts +14 -0
- package/dist/esm/lib/fetch-with-timeout.js +30 -0
- package/dist/esm/lib/fetch-with-timeout.js.map +1 -0
- package/dist/esm/lib/token-verifier.d.ts +44 -0
- package/dist/esm/lib/token-verifier.js +53 -0
- package/dist/esm/lib/token-verifier.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/providers/dcr.d.ts +107 -0
- package/dist/esm/providers/dcr.js +242 -0
- package/dist/esm/providers/dcr.js.map +1 -0
- package/dist/esm/providers/loopback-oauth.d.ts +119 -0
- package/dist/esm/providers/loopback-oauth.js +639 -0
- package/dist/esm/providers/loopback-oauth.js.map +1 -0
- package/dist/esm/providers/service-account.d.ts +131 -0
- package/dist/esm/providers/service-account.js +353 -0
- package/dist/esm/providers/service-account.js.map +1 -0
- package/dist/esm/schemas/index.d.ts +20 -0
- package/dist/esm/schemas/index.js +18 -0
- package/dist/esm/schemas/index.js.map +1 -0
- package/dist/esm/setup/config.d.ts +112 -0
- package/dist/esm/setup/config.js +258 -0
- package/dist/esm/setup/config.js.map +1 -0
- package/dist/esm/types.d.ts +173 -0
- package/dist/esm/types.js +6 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Storage Utilities
|
|
3
|
+
*
|
|
4
|
+
* Keyv-based storage utilities for Dynamic Client Registration.
|
|
5
|
+
* Follows @mcp-z/oauth pattern: single Keyv store with compound keys.
|
|
6
|
+
*
|
|
7
|
+
* Key Patterns:
|
|
8
|
+
* - dcr:client:{clientId} -> RegisteredClient
|
|
9
|
+
* - dcr:provider:{dcrToken} -> ProviderTokens
|
|
10
|
+
* - dcr:authcode:{code} -> AuthorizationCode
|
|
11
|
+
* - dcr:access:{token} -> AccessToken
|
|
12
|
+
* - dcr:refresh:{token} -> AccessToken
|
|
13
|
+
*/
|
|
14
|
+
import type { DcrClientInformation, DcrClientMetadata, ProviderTokens } from '@mcp-z/oauth';
|
|
15
|
+
import type { Keyv } from 'keyv';
|
|
16
|
+
import type { AccessToken, AuthorizationCode, RegisteredClient } from '../types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Register a new OAuth client (RFC 7591 Section 3.1)
|
|
19
|
+
*
|
|
20
|
+
* @param store - Keyv store for all DCR data
|
|
21
|
+
* @param metadata - Client registration metadata
|
|
22
|
+
* @returns Registered client with credentials
|
|
23
|
+
* @throws Error if validation fails
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerClient(store: Keyv, metadata: DcrClientMetadata): Promise<DcrClientInformation>;
|
|
26
|
+
/**
|
|
27
|
+
* Get a registered client by ID
|
|
28
|
+
*
|
|
29
|
+
* @param store - Keyv store for all DCR data
|
|
30
|
+
* @param clientId - Client identifier
|
|
31
|
+
* @returns Registered client or undefined if not found
|
|
32
|
+
*/
|
|
33
|
+
export declare function getClient(store: Keyv, clientId: string): Promise<RegisteredClient | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Validate client credentials
|
|
36
|
+
*
|
|
37
|
+
* @param store - Keyv store for all DCR data
|
|
38
|
+
* @param clientId - Client identifier
|
|
39
|
+
* @param clientSecret - Client secret
|
|
40
|
+
* @returns True if credentials are valid
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateClient(store: Keyv, clientId: string, clientSecret: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Validate redirect URI for a client
|
|
45
|
+
*
|
|
46
|
+
* @param store - Keyv store for all DCR data
|
|
47
|
+
* @param clientId - Client identifier
|
|
48
|
+
* @param redirectUri - Redirect URI to validate
|
|
49
|
+
* @returns True if redirect URI is registered
|
|
50
|
+
*/
|
|
51
|
+
export declare function validateRedirectUri(store: Keyv, clientId: string, redirectUri: string): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* List all registered clients (for debugging)
|
|
54
|
+
*
|
|
55
|
+
* Note: This method uses Keyv's iterator which may not be available on all storage adapters.
|
|
56
|
+
* For production use, consider maintaining a separate index of client IDs.
|
|
57
|
+
*
|
|
58
|
+
* @param store - Keyv store for all DCR data
|
|
59
|
+
* @returns Array of all registered clients
|
|
60
|
+
*/
|
|
61
|
+
export declare function listClients(store: Keyv): Promise<RegisteredClient[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Delete a registered client
|
|
64
|
+
*
|
|
65
|
+
* @param store - Keyv store for all DCR data
|
|
66
|
+
* @param clientId - Client identifier
|
|
67
|
+
*/
|
|
68
|
+
export declare function deleteClient(store: Keyv, clientId: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Store provider tokens for a DCR access token
|
|
71
|
+
*
|
|
72
|
+
* @param store - Keyv store for all DCR data
|
|
73
|
+
* @param dcrToken - DCR-issued access token (used as key)
|
|
74
|
+
* @param tokens - Google provider tokens (access, refresh, expiry)
|
|
75
|
+
*/
|
|
76
|
+
export declare function setProviderTokens(store: Keyv, dcrToken: string, tokens: ProviderTokens): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Retrieve provider tokens for a DCR access token
|
|
79
|
+
*
|
|
80
|
+
* @param store - Keyv store for all DCR data
|
|
81
|
+
* @param dcrToken - DCR-issued access token
|
|
82
|
+
* @returns Provider tokens or undefined if not found
|
|
83
|
+
*/
|
|
84
|
+
export declare function getProviderTokens(store: Keyv, dcrToken: string): Promise<ProviderTokens | undefined>;
|
|
85
|
+
/**
|
|
86
|
+
* Delete provider tokens for a DCR access token
|
|
87
|
+
*
|
|
88
|
+
* @param store - Keyv store for all DCR data
|
|
89
|
+
* @param dcrToken - DCR-issued access token
|
|
90
|
+
*/
|
|
91
|
+
export declare function deleteProviderTokens(store: Keyv, dcrToken: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Store an authorization code
|
|
94
|
+
*
|
|
95
|
+
* @param store - Keyv store for all DCR data
|
|
96
|
+
* @param code - Authorization code
|
|
97
|
+
* @param authCode - Authorization code data
|
|
98
|
+
*/
|
|
99
|
+
export declare function setAuthCode(store: Keyv, code: string, authCode: AuthorizationCode): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Get an authorization code
|
|
102
|
+
*
|
|
103
|
+
* @param store - Keyv store for all DCR data
|
|
104
|
+
* @param code - Authorization code
|
|
105
|
+
* @returns Authorization code data or undefined if not found
|
|
106
|
+
*/
|
|
107
|
+
export declare function getAuthCode(store: Keyv, code: string): Promise<AuthorizationCode | undefined>;
|
|
108
|
+
/**
|
|
109
|
+
* Delete an authorization code
|
|
110
|
+
*
|
|
111
|
+
* @param store - Keyv store for all DCR data
|
|
112
|
+
* @param code - Authorization code
|
|
113
|
+
*/
|
|
114
|
+
export declare function deleteAuthCode(store: Keyv, code: string): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Store an access token
|
|
117
|
+
*
|
|
118
|
+
* @param store - Keyv store for all DCR data
|
|
119
|
+
* @param token - Access token
|
|
120
|
+
* @param tokenData - Access token data
|
|
121
|
+
*/
|
|
122
|
+
export declare function setAccessToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Get an access token
|
|
125
|
+
*
|
|
126
|
+
* @param store - Keyv store for all DCR data
|
|
127
|
+
* @param token - Access token
|
|
128
|
+
* @returns Access token data or undefined if not found
|
|
129
|
+
*/
|
|
130
|
+
export declare function getAccessToken(store: Keyv, token: string): Promise<AccessToken | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* Delete an access token
|
|
133
|
+
*
|
|
134
|
+
* @param store - Keyv store for all DCR data
|
|
135
|
+
* @param token - Access token
|
|
136
|
+
*/
|
|
137
|
+
export declare function deleteAccessToken(store: Keyv, token: string): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* Store a refresh token
|
|
140
|
+
*
|
|
141
|
+
* @param store - Keyv store for all DCR data
|
|
142
|
+
* @param token - Refresh token
|
|
143
|
+
* @param tokenData - Access token data (contains refresh token context)
|
|
144
|
+
*/
|
|
145
|
+
export declare function setRefreshToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Get a refresh token
|
|
148
|
+
*
|
|
149
|
+
* @param store - Keyv store for all DCR data
|
|
150
|
+
* @param token - Refresh token
|
|
151
|
+
* @returns Access token data or undefined if not found
|
|
152
|
+
*/
|
|
153
|
+
export declare function getRefreshToken(store: Keyv, token: string): Promise<AccessToken | undefined>;
|
|
154
|
+
/**
|
|
155
|
+
* Delete a refresh token
|
|
156
|
+
*
|
|
157
|
+
* @param store - Keyv store for all DCR data
|
|
158
|
+
* @param token - Refresh token
|
|
159
|
+
*/
|
|
160
|
+
export declare function deleteRefreshToken(store: Keyv, token: string): Promise<void>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Storage Utilities
|
|
3
|
+
*
|
|
4
|
+
* Keyv-based storage utilities for Dynamic Client Registration.
|
|
5
|
+
* Follows @mcp-z/oauth pattern: single Keyv store with compound keys.
|
|
6
|
+
*
|
|
7
|
+
* Key Patterns:
|
|
8
|
+
* - dcr:client:{clientId} -> RegisteredClient
|
|
9
|
+
* - dcr:provider:{dcrToken} -> ProviderTokens
|
|
10
|
+
* - dcr:authcode:{code} -> AuthorizationCode
|
|
11
|
+
* - dcr:access:{token} -> AccessToken
|
|
12
|
+
* - dcr:refresh:{token} -> AccessToken
|
|
13
|
+
*/ import { randomUUID } from 'crypto';
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Client Operations
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Register a new OAuth client (RFC 7591 Section 3.1)
|
|
19
|
+
*
|
|
20
|
+
* @param store - Keyv store for all DCR data
|
|
21
|
+
* @param metadata - Client registration metadata
|
|
22
|
+
* @returns Registered client with credentials
|
|
23
|
+
* @throws Error if validation fails
|
|
24
|
+
*/ export async function registerClient(store, metadata) {
|
|
25
|
+
var _metadata_grant_types, _metadata_response_types, _metadata_token_endpoint_auth_method;
|
|
26
|
+
// Validate redirect URIs (required per RFC 7591)
|
|
27
|
+
if (!metadata.redirect_uris || metadata.redirect_uris.length === 0) {
|
|
28
|
+
throw new Error('redirect_uris is required');
|
|
29
|
+
}
|
|
30
|
+
// Generate client credentials
|
|
31
|
+
const client_id = `dcr_${randomUUID()}`;
|
|
32
|
+
const client_secret = randomUUID();
|
|
33
|
+
// Default grant types and response types per RFC 7591 Section 2
|
|
34
|
+
const grant_types = (_metadata_grant_types = metadata.grant_types) !== null && _metadata_grant_types !== void 0 ? _metadata_grant_types : [
|
|
35
|
+
'authorization_code',
|
|
36
|
+
'refresh_token'
|
|
37
|
+
];
|
|
38
|
+
const response_types = (_metadata_response_types = metadata.response_types) !== null && _metadata_response_types !== void 0 ? _metadata_response_types : [
|
|
39
|
+
'code'
|
|
40
|
+
];
|
|
41
|
+
// Build registered client - only include optional fields if they have values
|
|
42
|
+
const client = {
|
|
43
|
+
client_id,
|
|
44
|
+
client_secret,
|
|
45
|
+
client_id_issued_at: Math.floor(Date.now() / 1000),
|
|
46
|
+
client_secret_expires_at: 0,
|
|
47
|
+
redirect_uris: metadata.redirect_uris,
|
|
48
|
+
token_endpoint_auth_method: (_metadata_token_endpoint_auth_method = metadata.token_endpoint_auth_method) !== null && _metadata_token_endpoint_auth_method !== void 0 ? _metadata_token_endpoint_auth_method : 'client_secret_basic',
|
|
49
|
+
grant_types,
|
|
50
|
+
response_types,
|
|
51
|
+
...metadata.client_name !== undefined && {
|
|
52
|
+
client_name: metadata.client_name
|
|
53
|
+
},
|
|
54
|
+
...metadata.client_uri !== undefined && {
|
|
55
|
+
client_uri: metadata.client_uri
|
|
56
|
+
},
|
|
57
|
+
...metadata.logo_uri !== undefined && {
|
|
58
|
+
logo_uri: metadata.logo_uri
|
|
59
|
+
},
|
|
60
|
+
...metadata.scope !== undefined && {
|
|
61
|
+
scope: metadata.scope
|
|
62
|
+
},
|
|
63
|
+
...metadata.contacts !== undefined && {
|
|
64
|
+
contacts: metadata.contacts
|
|
65
|
+
},
|
|
66
|
+
...metadata.tos_uri !== undefined && {
|
|
67
|
+
tos_uri: metadata.tos_uri
|
|
68
|
+
},
|
|
69
|
+
...metadata.policy_uri !== undefined && {
|
|
70
|
+
policy_uri: metadata.policy_uri
|
|
71
|
+
},
|
|
72
|
+
...metadata.jwks_uri !== undefined && {
|
|
73
|
+
jwks_uri: metadata.jwks_uri
|
|
74
|
+
},
|
|
75
|
+
...metadata.jwks !== undefined && {
|
|
76
|
+
jwks: metadata.jwks
|
|
77
|
+
},
|
|
78
|
+
...metadata.software_id !== undefined && {
|
|
79
|
+
software_id: metadata.software_id
|
|
80
|
+
},
|
|
81
|
+
...metadata.software_version !== undefined && {
|
|
82
|
+
software_version: metadata.software_version
|
|
83
|
+
},
|
|
84
|
+
created_at: Date.now()
|
|
85
|
+
};
|
|
86
|
+
// Store client
|
|
87
|
+
await store.set(`dcr:client:${client_id}`, client);
|
|
88
|
+
// Return client information (excluding internal created_at)
|
|
89
|
+
const { created_at, ...clientInfo } = client;
|
|
90
|
+
return clientInfo;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get a registered client by ID
|
|
94
|
+
*
|
|
95
|
+
* @param store - Keyv store for all DCR data
|
|
96
|
+
* @param clientId - Client identifier
|
|
97
|
+
* @returns Registered client or undefined if not found
|
|
98
|
+
*/ export async function getClient(store, clientId) {
|
|
99
|
+
return await store.get(`dcr:client:${clientId}`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate client credentials
|
|
103
|
+
*
|
|
104
|
+
* @param store - Keyv store for all DCR data
|
|
105
|
+
* @param clientId - Client identifier
|
|
106
|
+
* @param clientSecret - Client secret
|
|
107
|
+
* @returns True if credentials are valid
|
|
108
|
+
*/ export async function validateClient(store, clientId, clientSecret) {
|
|
109
|
+
const client = await getClient(store, clientId);
|
|
110
|
+
if (!client) return false;
|
|
111
|
+
return client.client_secret === clientSecret;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Validate redirect URI for a client
|
|
115
|
+
*
|
|
116
|
+
* @param store - Keyv store for all DCR data
|
|
117
|
+
* @param clientId - Client identifier
|
|
118
|
+
* @param redirectUri - Redirect URI to validate
|
|
119
|
+
* @returns True if redirect URI is registered
|
|
120
|
+
*/ export async function validateRedirectUri(store, clientId, redirectUri) {
|
|
121
|
+
const client = await getClient(store, clientId);
|
|
122
|
+
if (!client || !client.redirect_uris) return false;
|
|
123
|
+
return client.redirect_uris.includes(redirectUri);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List all registered clients (for debugging)
|
|
127
|
+
*
|
|
128
|
+
* Note: This method uses Keyv's iterator which may not be available on all storage adapters.
|
|
129
|
+
* For production use, consider maintaining a separate index of client IDs.
|
|
130
|
+
*
|
|
131
|
+
* @param store - Keyv store for all DCR data
|
|
132
|
+
* @returns Array of all registered clients
|
|
133
|
+
*/ export async function listClients(store) {
|
|
134
|
+
const clients = [];
|
|
135
|
+
// Check if iterator is available on the store
|
|
136
|
+
if (store.iterator) {
|
|
137
|
+
// Use iterator with namespace to iterate through dcr:client: keys
|
|
138
|
+
const iterator = store.iterator('dcr:client:');
|
|
139
|
+
for await (const [_key, value] of iterator){
|
|
140
|
+
if (value !== undefined) {
|
|
141
|
+
clients.push(value);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return clients;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Delete a registered client
|
|
149
|
+
*
|
|
150
|
+
* @param store - Keyv store for all DCR data
|
|
151
|
+
* @param clientId - Client identifier
|
|
152
|
+
*/ export async function deleteClient(store, clientId) {
|
|
153
|
+
await store.delete(`dcr:client:${clientId}`);
|
|
154
|
+
}
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Provider Token Operations
|
|
157
|
+
// ============================================================================
|
|
158
|
+
/**
|
|
159
|
+
* Store provider tokens for a DCR access token
|
|
160
|
+
*
|
|
161
|
+
* @param store - Keyv store for all DCR data
|
|
162
|
+
* @param dcrToken - DCR-issued access token (used as key)
|
|
163
|
+
* @param tokens - Google provider tokens (access, refresh, expiry)
|
|
164
|
+
*/ export async function setProviderTokens(store, dcrToken, tokens) {
|
|
165
|
+
await store.set(`dcr:provider:${dcrToken}`, tokens);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Retrieve provider tokens for a DCR access token
|
|
169
|
+
*
|
|
170
|
+
* @param store - Keyv store for all DCR data
|
|
171
|
+
* @param dcrToken - DCR-issued access token
|
|
172
|
+
* @returns Provider tokens or undefined if not found
|
|
173
|
+
*/ export async function getProviderTokens(store, dcrToken) {
|
|
174
|
+
return await store.get(`dcr:provider:${dcrToken}`);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Delete provider tokens for a DCR access token
|
|
178
|
+
*
|
|
179
|
+
* @param store - Keyv store for all DCR data
|
|
180
|
+
* @param dcrToken - DCR-issued access token
|
|
181
|
+
*/ export async function deleteProviderTokens(store, dcrToken) {
|
|
182
|
+
await store.delete(`dcr:provider:${dcrToken}`);
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Authorization Code Operations
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Store an authorization code
|
|
189
|
+
*
|
|
190
|
+
* @param store - Keyv store for all DCR data
|
|
191
|
+
* @param code - Authorization code
|
|
192
|
+
* @param authCode - Authorization code data
|
|
193
|
+
*/ export async function setAuthCode(store, code, authCode) {
|
|
194
|
+
await store.set(`dcr:authcode:${code}`, authCode);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get an authorization code
|
|
198
|
+
*
|
|
199
|
+
* @param store - Keyv store for all DCR data
|
|
200
|
+
* @param code - Authorization code
|
|
201
|
+
* @returns Authorization code data or undefined if not found
|
|
202
|
+
*/ export async function getAuthCode(store, code) {
|
|
203
|
+
return await store.get(`dcr:authcode:${code}`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Delete an authorization code
|
|
207
|
+
*
|
|
208
|
+
* @param store - Keyv store for all DCR data
|
|
209
|
+
* @param code - Authorization code
|
|
210
|
+
*/ export async function deleteAuthCode(store, code) {
|
|
211
|
+
await store.delete(`dcr:authcode:${code}`);
|
|
212
|
+
}
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// Access Token Operations
|
|
215
|
+
// ============================================================================
|
|
216
|
+
/**
|
|
217
|
+
* Store an access token
|
|
218
|
+
*
|
|
219
|
+
* @param store - Keyv store for all DCR data
|
|
220
|
+
* @param token - Access token
|
|
221
|
+
* @param tokenData - Access token data
|
|
222
|
+
*/ export async function setAccessToken(store, token, tokenData) {
|
|
223
|
+
await store.set(`dcr:access:${token}`, tokenData);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get an access token
|
|
227
|
+
*
|
|
228
|
+
* @param store - Keyv store for all DCR data
|
|
229
|
+
* @param token - Access token
|
|
230
|
+
* @returns Access token data or undefined if not found
|
|
231
|
+
*/ export async function getAccessToken(store, token) {
|
|
232
|
+
return await store.get(`dcr:access:${token}`);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Delete an access token
|
|
236
|
+
*
|
|
237
|
+
* @param store - Keyv store for all DCR data
|
|
238
|
+
* @param token - Access token
|
|
239
|
+
*/ export async function deleteAccessToken(store, token) {
|
|
240
|
+
await store.delete(`dcr:access:${token}`);
|
|
241
|
+
}
|
|
242
|
+
// ============================================================================
|
|
243
|
+
// Refresh Token Operations
|
|
244
|
+
// ============================================================================
|
|
245
|
+
/**
|
|
246
|
+
* Store a refresh token
|
|
247
|
+
*
|
|
248
|
+
* @param store - Keyv store for all DCR data
|
|
249
|
+
* @param token - Refresh token
|
|
250
|
+
* @param tokenData - Access token data (contains refresh token context)
|
|
251
|
+
*/ export async function setRefreshToken(store, token, tokenData) {
|
|
252
|
+
await store.set(`dcr:refresh:${token}`, tokenData);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get a refresh token
|
|
256
|
+
*
|
|
257
|
+
* @param store - Keyv store for all DCR data
|
|
258
|
+
* @param token - Refresh token
|
|
259
|
+
* @returns Access token data or undefined if not found
|
|
260
|
+
*/ export async function getRefreshToken(store, token) {
|
|
261
|
+
return await store.get(`dcr:refresh:${token}`);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Delete a refresh token
|
|
265
|
+
*
|
|
266
|
+
* @param store - Keyv store for all DCR data
|
|
267
|
+
* @param token - Refresh token
|
|
268
|
+
*/ export async function deleteRefreshToken(store, token) {
|
|
269
|
+
await store.delete(`dcr:refresh:${token}`);
|
|
270
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/dcr-utils.ts"],"sourcesContent":["/**\n * DCR Storage Utilities\n *\n * Keyv-based storage utilities for Dynamic Client Registration.\n * Follows @mcp-z/oauth pattern: single Keyv store with compound keys.\n *\n * Key Patterns:\n * - dcr:client:{clientId} -> RegisteredClient\n * - dcr:provider:{dcrToken} -> ProviderTokens\n * - dcr:authcode:{code} -> AuthorizationCode\n * - dcr:access:{token} -> AccessToken\n * - dcr:refresh:{token} -> AccessToken\n */\n\nimport type { DcrClientInformation, DcrClientMetadata, ProviderTokens } from '@mcp-z/oauth';\nimport { randomUUID } from 'crypto';\nimport type { Keyv } from 'keyv';\nimport type { AccessToken, AuthorizationCode, RegisteredClient } from '../types.ts';\n\n// ============================================================================\n// Client Operations\n// ============================================================================\n\n/**\n * Register a new OAuth client (RFC 7591 Section 3.1)\n *\n * @param store - Keyv store for all DCR data\n * @param metadata - Client registration metadata\n * @returns Registered client with credentials\n * @throws Error if validation fails\n */\nexport async function registerClient(store: Keyv, metadata: DcrClientMetadata): Promise<DcrClientInformation> {\n // Validate redirect URIs (required per RFC 7591)\n if (!metadata.redirect_uris || metadata.redirect_uris.length === 0) {\n throw new Error('redirect_uris is required');\n }\n\n // Generate client credentials\n const client_id = `dcr_${randomUUID()}`;\n const client_secret = randomUUID();\n\n // Default grant types and response types per RFC 7591 Section 2\n const grant_types = metadata.grant_types ?? ['authorization_code', 'refresh_token'];\n const response_types = metadata.response_types ?? ['code'];\n\n // Build registered client - only include optional fields if they have values\n const client: RegisteredClient = {\n client_id,\n client_secret,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_secret_expires_at: 0, // Never expires\n redirect_uris: metadata.redirect_uris,\n token_endpoint_auth_method: metadata.token_endpoint_auth_method ?? 'client_secret_basic',\n grant_types,\n response_types,\n ...(metadata.client_name !== undefined && { client_name: metadata.client_name }),\n ...(metadata.client_uri !== undefined && { client_uri: metadata.client_uri }),\n ...(metadata.logo_uri !== undefined && { logo_uri: metadata.logo_uri }),\n ...(metadata.scope !== undefined && { scope: metadata.scope }),\n ...(metadata.contacts !== undefined && { contacts: metadata.contacts }),\n ...(metadata.tos_uri !== undefined && { tos_uri: metadata.tos_uri }),\n ...(metadata.policy_uri !== undefined && { policy_uri: metadata.policy_uri }),\n ...(metadata.jwks_uri !== undefined && { jwks_uri: metadata.jwks_uri }),\n ...(metadata.jwks !== undefined && { jwks: metadata.jwks }),\n ...(metadata.software_id !== undefined && { software_id: metadata.software_id }),\n ...(metadata.software_version !== undefined && { software_version: metadata.software_version }),\n created_at: Date.now(),\n };\n\n // Store client\n await store.set(`dcr:client:${client_id}`, client);\n\n // Return client information (excluding internal created_at)\n const { created_at, ...clientInfo } = client;\n return clientInfo;\n}\n\n/**\n * Get a registered client by ID\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @returns Registered client or undefined if not found\n */\nexport async function getClient(store: Keyv, clientId: string): Promise<RegisteredClient | undefined> {\n return await store.get(`dcr:client:${clientId}`);\n}\n\n/**\n * Validate client credentials\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param clientSecret - Client secret\n * @returns True if credentials are valid\n */\nexport async function validateClient(store: Keyv, clientId: string, clientSecret: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client) return false;\n return client.client_secret === clientSecret;\n}\n\n/**\n * Validate redirect URI for a client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param redirectUri - Redirect URI to validate\n * @returns True if redirect URI is registered\n */\nexport async function validateRedirectUri(store: Keyv, clientId: string, redirectUri: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client || !client.redirect_uris) return false;\n return client.redirect_uris.includes(redirectUri);\n}\n\n/**\n * List all registered clients (for debugging)\n *\n * Note: This method uses Keyv's iterator which may not be available on all storage adapters.\n * For production use, consider maintaining a separate index of client IDs.\n *\n * @param store - Keyv store for all DCR data\n * @returns Array of all registered clients\n */\nexport async function listClients(store: Keyv): Promise<RegisteredClient[]> {\n const clients: RegisteredClient[] = [];\n\n // Check if iterator is available on the store\n if (store.iterator) {\n // Use iterator with namespace to iterate through dcr:client: keys\n const iterator = store.iterator('dcr:client:');\n for await (const [_key, value] of iterator) {\n if (value !== undefined) {\n clients.push(value as RegisteredClient);\n }\n }\n }\n\n return clients;\n}\n\n/**\n * Delete a registered client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n */\nexport async function deleteClient(store: Keyv, clientId: string): Promise<void> {\n await store.delete(`dcr:client:${clientId}`);\n}\n\n// ============================================================================\n// Provider Token Operations\n// ============================================================================\n\n/**\n * Store provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token (used as key)\n * @param tokens - Google provider tokens (access, refresh, expiry)\n */\nexport async function setProviderTokens(store: Keyv, dcrToken: string, tokens: ProviderTokens): Promise<void> {\n await store.set(`dcr:provider:${dcrToken}`, tokens);\n}\n\n/**\n * Retrieve provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n * @returns Provider tokens or undefined if not found\n */\nexport async function getProviderTokens(store: Keyv, dcrToken: string): Promise<ProviderTokens | undefined> {\n return await store.get(`dcr:provider:${dcrToken}`);\n}\n\n/**\n * Delete provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n */\nexport async function deleteProviderTokens(store: Keyv, dcrToken: string): Promise<void> {\n await store.delete(`dcr:provider:${dcrToken}`);\n}\n\n// ============================================================================\n// Authorization Code Operations\n// ============================================================================\n\n/**\n * Store an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @param authCode - Authorization code data\n */\nexport async function setAuthCode(store: Keyv, code: string, authCode: AuthorizationCode): Promise<void> {\n await store.set(`dcr:authcode:${code}`, authCode);\n}\n\n/**\n * Get an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @returns Authorization code data or undefined if not found\n */\nexport async function getAuthCode(store: Keyv, code: string): Promise<AuthorizationCode | undefined> {\n return await store.get(`dcr:authcode:${code}`);\n}\n\n/**\n * Delete an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n */\nexport async function deleteAuthCode(store: Keyv, code: string): Promise<void> {\n await store.delete(`dcr:authcode:${code}`);\n}\n\n// ============================================================================\n// Access Token Operations\n// ============================================================================\n\n/**\n * Store an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @param tokenData - Access token data\n */\nexport async function setAccessToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:access:${token}`, tokenData);\n}\n\n/**\n * Get an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @returns Access token data or undefined if not found\n */\nexport async function getAccessToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:access:${token}`);\n}\n\n/**\n * Delete an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n */\nexport async function deleteAccessToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:access:${token}`);\n}\n\n// ============================================================================\n// Refresh Token Operations\n// ============================================================================\n\n/**\n * Store a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @param tokenData - Access token data (contains refresh token context)\n */\nexport async function setRefreshToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:refresh:${token}`, tokenData);\n}\n\n/**\n * Get a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @returns Access token data or undefined if not found\n */\nexport async function getRefreshToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:refresh:${token}`);\n}\n\n/**\n * Delete a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n */\nexport async function deleteRefreshToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:refresh:${token}`);\n}\n"],"names":["randomUUID","registerClient","store","metadata","redirect_uris","length","Error","client_id","client_secret","grant_types","response_types","client","client_id_issued_at","Math","floor","Date","now","client_secret_expires_at","token_endpoint_auth_method","client_name","undefined","client_uri","logo_uri","scope","contacts","tos_uri","policy_uri","jwks_uri","jwks","software_id","software_version","created_at","set","clientInfo","getClient","clientId","get","validateClient","clientSecret","validateRedirectUri","redirectUri","includes","listClients","clients","iterator","_key","value","push","deleteClient","delete","setProviderTokens","dcrToken","tokens","getProviderTokens","deleteProviderTokens","setAuthCode","code","authCode","getAuthCode","deleteAuthCode","setAccessToken","token","tokenData","getAccessToken","deleteAccessToken","setRefreshToken","getRefreshToken","deleteRefreshToken"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Token Verification Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides token verification for both self-hosted and external DCR modes:
|
|
5
|
+
* - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)
|
|
6
|
+
* - External: Verifies tokens against Auth0/Stitch verification endpoint
|
|
7
|
+
*/
|
|
8
|
+
import type { ProviderTokens } from '@mcp-z/oauth';
|
|
9
|
+
/**
|
|
10
|
+
* Verification result from DCR authorization server
|
|
11
|
+
*/
|
|
12
|
+
export interface VerificationResult {
|
|
13
|
+
/** Bearer token that was verified */
|
|
14
|
+
token: string;
|
|
15
|
+
/** Client ID associated with the token */
|
|
16
|
+
clientId: string;
|
|
17
|
+
/** OAuth scopes granted to the token */
|
|
18
|
+
scopes: string[];
|
|
19
|
+
/** Token expiration timestamp (milliseconds since epoch) */
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
/** Provider tokens (Google access/refresh tokens) */
|
|
22
|
+
providerTokens: ProviderTokens;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Verify bearer token against DCR authorization server
|
|
26
|
+
*
|
|
27
|
+
* Supports both self-hosted and external DCR modes by calling the
|
|
28
|
+
* /oauth/verify endpoint (or equivalent external URL).
|
|
29
|
+
*
|
|
30
|
+
* @param bearerToken - Bearer token to verify (without "Bearer " prefix)
|
|
31
|
+
* @param verifyUrl - Verification endpoint URL (self-hosted or external)
|
|
32
|
+
* @returns Verification result with provider tokens
|
|
33
|
+
* @throws Error if verification fails
|
|
34
|
+
*
|
|
35
|
+
* @example Self-hosted mode
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const result = await verifyBearerToken(
|
|
38
|
+
* token,
|
|
39
|
+
* 'http://localhost:3456/oauth/verify'
|
|
40
|
+
* );
|
|
41
|
+
* const auth = provider.toAuth(result.providerTokens);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example External mode (Auth0/Stitch)
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const result = await verifyBearerToken(
|
|
47
|
+
* token,
|
|
48
|
+
* 'https://auth.example.com/oauth/verify'
|
|
49
|
+
* );
|
|
50
|
+
* const auth = provider.toAuth(result.providerTokens);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function verifyBearerToken(bearerToken: string, verifyUrl: string): Promise<VerificationResult>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCR Token Verification Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides token verification for both self-hosted and external DCR modes:
|
|
5
|
+
* - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)
|
|
6
|
+
* - External: Verifies tokens against Auth0/Stitch verification endpoint
|
|
7
|
+
*/ import { fetchWithTimeout } from './fetch-with-timeout.js';
|
|
8
|
+
/**
|
|
9
|
+
* Verify bearer token against DCR authorization server
|
|
10
|
+
*
|
|
11
|
+
* Supports both self-hosted and external DCR modes by calling the
|
|
12
|
+
* /oauth/verify endpoint (or equivalent external URL).
|
|
13
|
+
*
|
|
14
|
+
* @param bearerToken - Bearer token to verify (without "Bearer " prefix)
|
|
15
|
+
* @param verifyUrl - Verification endpoint URL (self-hosted or external)
|
|
16
|
+
* @returns Verification result with provider tokens
|
|
17
|
+
* @throws Error if verification fails
|
|
18
|
+
*
|
|
19
|
+
* @example Self-hosted mode
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const result = await verifyBearerToken(
|
|
22
|
+
* token,
|
|
23
|
+
* 'http://localhost:3456/oauth/verify'
|
|
24
|
+
* );
|
|
25
|
+
* const auth = provider.toAuth(result.providerTokens);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example External mode (Auth0/Stitch)
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const result = await verifyBearerToken(
|
|
31
|
+
* token,
|
|
32
|
+
* 'https://auth.example.com/oauth/verify'
|
|
33
|
+
* );
|
|
34
|
+
* const auth = provider.toAuth(result.providerTokens);
|
|
35
|
+
* ```
|
|
36
|
+
*/ export async function verifyBearerToken(bearerToken, verifyUrl) {
|
|
37
|
+
const response = await fetchWithTimeout(verifyUrl, {
|
|
38
|
+
method: 'GET',
|
|
39
|
+
headers: {
|
|
40
|
+
Authorization: `Bearer ${bearerToken}`
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
const errorText = await response.text();
|
|
45
|
+
throw new Error(`Token verification failed: ${response.status} ${errorText}`);
|
|
46
|
+
}
|
|
47
|
+
const result = await response.json();
|
|
48
|
+
// Validate required fields
|
|
49
|
+
if (!result.providerTokens || !result.providerTokens.accessToken) {
|
|
50
|
+
throw new Error('Verification response missing required provider tokens');
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/dcr-verify.ts"],"sourcesContent":["/**\n * DCR Token Verification Utilities\n *\n * Provides token verification for both self-hosted and external DCR modes:\n * - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)\n * - External: Verifies tokens against Auth0/Stitch verification endpoint\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { fetchWithTimeout } from './fetch-with-timeout.ts';\n\n/**\n * Verification result from DCR authorization server\n */\nexport interface VerificationResult {\n /** Bearer token that was verified */\n token: string;\n /** Client ID associated with the token */\n clientId: string;\n /** OAuth scopes granted to the token */\n scopes: string[];\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n /** Provider tokens (Google access/refresh tokens) */\n providerTokens: ProviderTokens;\n}\n\n/**\n * Verify bearer token against DCR authorization server\n *\n * Supports both self-hosted and external DCR modes by calling the\n * /oauth/verify endpoint (or equivalent external URL).\n *\n * @param bearerToken - Bearer token to verify (without \"Bearer \" prefix)\n * @param verifyUrl - Verification endpoint URL (self-hosted or external)\n * @returns Verification result with provider tokens\n * @throws Error if verification fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'http://localhost:3456/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n *\n * @example External mode (Auth0/Stitch)\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'https://auth.example.com/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n */\nexport async function verifyBearerToken(bearerToken: string, verifyUrl: string): Promise<VerificationResult> {\n const response = await fetchWithTimeout(verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token verification failed: ${response.status} ${errorText}`);\n }\n\n const result = (await response.json()) as VerificationResult;\n\n // Validate required fields\n if (!result.providerTokens || !result.providerTokens.accessToken) {\n throw new Error('Verification response missing required provider tokens');\n }\n\n return result;\n}\n"],"names":["fetchWithTimeout","verifyBearerToken","bearerToken","verifyUrl","response","method","headers","Authorization","ok","errorText","text","Error","status","result","json","providerTokens","accessToken"],"mappings":"AAAA;;;;;;CAMC,GAGD,SAASA,gBAAgB,QAAQ,0BAA0B;AAkB3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,kBAAkBC,WAAmB,EAAEC,SAAiB;IAC5E,MAAMC,WAAW,MAAMJ,iBAAiBG,WAAW;QACjDE,QAAQ;QACRC,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEL,aAAa;QACxC;IACF;IAEA,IAAI,CAACE,SAASI,EAAE,EAAE;QAChB,MAAMC,YAAY,MAAML,SAASM,IAAI;QACrC,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEP,SAASQ,MAAM,CAAC,CAAC,EAAEH,WAAW;IAC9E;IAEA,MAAMI,SAAU,MAAMT,SAASU,IAAI;IAEnC,2BAA2B;IAC3B,IAAI,CAACD,OAAOE,cAAc,IAAI,CAACF,OAAOE,cAAc,CAACC,WAAW,EAAE;QAChE,MAAM,IAAIL,MAAM;IAClB;IAEA,OAAOE;AACT"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch with timeout to prevent hanging requests
|
|
3
|
+
*
|
|
4
|
+
* This utility wraps the native fetch API with an AbortController to ensure
|
|
5
|
+
* requests don't hang indefinitely. This is critical for OAuth token operations
|
|
6
|
+
* where expired tokens might cause Google's servers to hang or respond slowly.
|
|
7
|
+
*
|
|
8
|
+
* @param url - URL to fetch
|
|
9
|
+
* @param options - Fetch options
|
|
10
|
+
* @param timeoutMs - Timeout in milliseconds (default: 30000)
|
|
11
|
+
* @returns Fetch response
|
|
12
|
+
* @throws Error if request times out
|
|
13
|
+
*/
|
|
14
|
+
export declare function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs?: number): Promise<Response>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch with timeout to prevent hanging requests
|
|
3
|
+
*
|
|
4
|
+
* This utility wraps the native fetch API with an AbortController to ensure
|
|
5
|
+
* requests don't hang indefinitely. This is critical for OAuth token operations
|
|
6
|
+
* where expired tokens might cause Google's servers to hang or respond slowly.
|
|
7
|
+
*
|
|
8
|
+
* @param url - URL to fetch
|
|
9
|
+
* @param options - Fetch options
|
|
10
|
+
* @param timeoutMs - Timeout in milliseconds (default: 30000)
|
|
11
|
+
* @returns Fetch response
|
|
12
|
+
* @throws Error if request times out
|
|
13
|
+
*/ export async function fetchWithTimeout(url, options, timeoutMs = 30000) {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timeoutId = setTimeout(()=>controller.abort(), timeoutMs);
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
...options,
|
|
19
|
+
signal: controller.signal
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
return response;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
clearTimeout(timeoutId);
|
|
25
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
26
|
+
throw new Error(`Request timeout after ${timeoutMs}ms`);
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/fetch-with-timeout.ts"],"sourcesContent":["/**\n * Fetch with timeout to prevent hanging requests\n *\n * This utility wraps the native fetch API with an AbortController to ensure\n * requests don't hang indefinitely. This is critical for OAuth token operations\n * where expired tokens might cause Google's servers to hang or respond slowly.\n *\n * @param url - URL to fetch\n * @param options - Fetch options\n * @param timeoutMs - Timeout in milliseconds (default: 30000)\n * @returns Fetch response\n * @throws Error if request times out\n */\nexport async function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs = 30000): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${timeoutMs}ms`);\n }\n throw error;\n }\n}\n"],"names":["fetchWithTimeout","url","options","timeoutMs","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","signal","clearTimeout","error","Error","name"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeA,iBAAiBC,GAAW,EAAEC,OAAqB,EAAEC,YAAY,KAAK;IAC1F,MAAMC,aAAa,IAAIC;IACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IAEvD,IAAI;QACF,MAAMM,WAAW,MAAMC,MAAMT,KAAK;YAChC,GAAGC,OAAO;YACVS,QAAQP,WAAWO,MAAM;QAC3B;QACAC,aAAaN;QACb,OAAOG;IACT,EAAE,OAAOI,OAAO;QACdD,aAAaN;QACb,IAAIO,iBAAiBC,SAASD,MAAME,IAAI,KAAK,cAAc;YACzD,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEX,UAAU,EAAE,CAAC;QACxD;QACA,MAAMU;IACR;AACF"}
|