@vercel/connect 0.2.0 → 0.2.2
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 +1 -1
- package/dist/eve/connection-authorization.d.ts +19 -0
- package/dist/eve/connection-authorization.js +14 -2
- package/dist/eve/github-credentials.d.ts +24 -0
- package/dist/eve/github-credentials.js +20 -0
- package/dist/eve/index.d.ts +3 -1
- package/dist/eve/index.js +3 -1
- package/dist/eve/linear-credentials.d.ts +22 -0
- package/dist/eve/linear-credentials.js +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/token.d.ts +20 -0
- package/dist/token.js +29 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -97,4 +97,4 @@ import { connect } from '@vercel/connect/authjs';
|
|
|
97
97
|
const providers = [connect({ connector: 'linear' })];
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
See the source under `src/` for the full API (additional helpers like `getTokenResponse`, `startAuthorization`, typed error classes, and per-adapter options).
|
|
100
|
+
See the source under `src/` for the full API (additional helpers like `revokeToken`, `getTokenResponse`, `startAuthorization`, typed error classes, and per-adapter options).
|
|
@@ -82,6 +82,25 @@ export interface EveAuthorizationOptions {
|
|
|
82
82
|
* and rely on `@vercel/oidc` auto-discovery.
|
|
83
83
|
*/
|
|
84
84
|
readonly connectOptions?: ConnectOptions;
|
|
85
|
+
/**
|
|
86
|
+
* Re-validate the grant against Vercel Connect on every `getToken`
|
|
87
|
+
* instead of trusting the in-process token cache.
|
|
88
|
+
*
|
|
89
|
+
* By default `getToken` returns a cached token as long as it has not
|
|
90
|
+
* expired, so a grant the user revoked server-side keeps reaching the
|
|
91
|
+
* tool until the bearer's natural expiry. With `validate: true` each
|
|
92
|
+
* `getToken` bypasses the local cache and re-checks Connect; a revoked
|
|
93
|
+
* grant comes back as `no_token` / `user_authorization_required`, which
|
|
94
|
+
* the adapter maps to {@link ConnectionAuthorizationRequiredError} so
|
|
95
|
+
* Eve re-runs the consent flow rather than calling the tool with a dead
|
|
96
|
+
* token.
|
|
97
|
+
*
|
|
98
|
+
* This trades a Connect round trip per call for freshness — leave it
|
|
99
|
+
* off for high-frequency tools where a short revocation window is
|
|
100
|
+
* acceptable, and turn it on for sensitive actions (payments, deletes,
|
|
101
|
+
* privileged writes) that must never run on a revoked grant.
|
|
102
|
+
*/
|
|
103
|
+
readonly validate?: boolean;
|
|
85
104
|
/**
|
|
86
105
|
* Custom call-to-action rendered on the
|
|
87
106
|
* `connection.authorization_required` event. When omitted, Eve
|
|
@@ -50,7 +50,7 @@ function buildInteractiveDefinition(options) {
|
|
|
50
50
|
principalType: 'user',
|
|
51
51
|
async getToken({ principal }) {
|
|
52
52
|
try {
|
|
53
|
-
const response = await getTokenResponse(options.connector, await buildTokenParams(options, principal), options
|
|
53
|
+
const response = await getTokenResponse(options.connector, await buildTokenParams(options, principal), getTokenConnectOptions(options));
|
|
54
54
|
return { token: response.token, expiresAt: response.expiresAt };
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
@@ -118,7 +118,7 @@ function buildNonInteractiveDefinition(options) {
|
|
|
118
118
|
principalType: 'app',
|
|
119
119
|
async getToken({ principal }) {
|
|
120
120
|
try {
|
|
121
|
-
const response = await getTokenResponse(options.connector, await buildTokenParams(options, principal), options
|
|
121
|
+
const response = await getTokenResponse(options.connector, await buildTokenParams(options, principal), getTokenConnectOptions(options));
|
|
122
122
|
return { token: response.token, expiresAt: response.expiresAt };
|
|
123
123
|
}
|
|
124
124
|
catch (error) {
|
|
@@ -127,6 +127,18 @@ function buildNonInteractiveDefinition(options) {
|
|
|
127
127
|
},
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Connect SDK options for `getToken` calls. When {@link
|
|
132
|
+
* EveAuthorizationOptions.validate} is set, forces a cache-bypassing
|
|
133
|
+
* re-fetch so a revoked-but-unexpired grant is caught instead of served
|
|
134
|
+
* from the in-process token cache.
|
|
135
|
+
*/
|
|
136
|
+
function getTokenConnectOptions(options) {
|
|
137
|
+
if (!options.validate) {
|
|
138
|
+
return options.connectOptions;
|
|
139
|
+
}
|
|
140
|
+
return { ...options.connectOptions, forceRefresh: true };
|
|
141
|
+
}
|
|
130
142
|
async function buildTokenParams(options, principal) {
|
|
131
143
|
const toSubject = options.principalToSubject ?? principalToSubject;
|
|
132
144
|
return {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GitHubChannelCredentials } from 'eve/channels/github';
|
|
2
|
+
import { type ConnectOptions, type ConnectTokenParams } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Token parameters accepted by {@link connectGitHubCredentials}.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors {@link ConnectTokenParams} from `@vercel/connect`, minus
|
|
7
|
+
* `subject` — GitHub installation tokens are app-scoped, so `subject`
|
|
8
|
+
* is pinned to `{ type: "app" }` by this helper and cannot be
|
|
9
|
+
* overridden.
|
|
10
|
+
*/
|
|
11
|
+
export type ConnectGitHubCredentialsParams = Omit<ConnectTokenParams, 'subject'>;
|
|
12
|
+
/**
|
|
13
|
+
* Build {@link GitHubChannelCredentials} backed by a Vercel Connect
|
|
14
|
+
* connector that stores a GitHub installation access token.
|
|
15
|
+
*
|
|
16
|
+
* Eve uses `installationToken` directly for authenticated GitHub API
|
|
17
|
+
* calls and skips its native GitHub App JWT exchange. The token is a
|
|
18
|
+
* function form so rotation, refresh, and multi-installation tenancy
|
|
19
|
+
* stay delegated to Vercel Connect.
|
|
20
|
+
*
|
|
21
|
+
* The webhook verifier accepts Connect-forwarded webhooks authenticated
|
|
22
|
+
* with Vercel OIDC instead of GitHub's webhook secret.
|
|
23
|
+
*/
|
|
24
|
+
export declare function connectGitHubCredentials(connector: string, params?: ConnectGitHubCredentialsParams, options?: ConnectOptions): GitHubChannelCredentials;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { vercelOidc } from 'eve/channels/auth';
|
|
2
|
+
import { getToken, } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build {@link GitHubChannelCredentials} backed by a Vercel Connect
|
|
5
|
+
* connector that stores a GitHub installation access token.
|
|
6
|
+
*
|
|
7
|
+
* Eve uses `installationToken` directly for authenticated GitHub API
|
|
8
|
+
* calls and skips its native GitHub App JWT exchange. The token is a
|
|
9
|
+
* function form so rotation, refresh, and multi-installation tenancy
|
|
10
|
+
* stay delegated to Vercel Connect.
|
|
11
|
+
*
|
|
12
|
+
* The webhook verifier accepts Connect-forwarded webhooks authenticated
|
|
13
|
+
* with Vercel OIDC instead of GitHub's webhook secret.
|
|
14
|
+
*/
|
|
15
|
+
export function connectGitHubCredentials(connector, params = {}, options) {
|
|
16
|
+
return {
|
|
17
|
+
installationToken: () => getToken(connector, { ...params, subject: { type: 'app' } }, options),
|
|
18
|
+
webhookVerifier: vercelOidc(),
|
|
19
|
+
};
|
|
20
|
+
}
|
package/dist/eve/index.d.ts
CHANGED
|
@@ -8,4 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { connect, type EveAuthorizationInput, type EveAuthorizationOptions, type EveConnectAuthorizationDefinition, type ConnectAuthorizationPhase, type VercelConnectMetadata, } from './connection-authorization.js';
|
|
10
10
|
export { CONNECT_OAUTH_ISSUER, connectOAuth, type ConnectOAuthAudienceEnvironment, type ConnectOAuthEnvironment, type ConnectOAuthOptions, } from './connect-oauth.js';
|
|
11
|
-
export {
|
|
11
|
+
export { connectGitHubCredentials, type ConnectGitHubCredentialsParams, } from './github-credentials.js';
|
|
12
|
+
export { connectLinearCredentials, type ConnectLinearCredentialsParams, } from './linear-credentials.js';
|
|
13
|
+
export { connectSlackCredentials, type ConnectSlackCredentialsParams, } from './slack-credentials.js';
|
package/dist/eve/index.js
CHANGED
|
@@ -8,4 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { connect, } from './connection-authorization.js';
|
|
10
10
|
export { CONNECT_OAUTH_ISSUER, connectOAuth, } from './connect-oauth.js';
|
|
11
|
-
export {
|
|
11
|
+
export { connectGitHubCredentials, } from './github-credentials.js';
|
|
12
|
+
export { connectLinearCredentials, } from './linear-credentials.js';
|
|
13
|
+
export { connectSlackCredentials, } from './slack-credentials.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LinearChannelCredentials } from 'eve/channels/linear';
|
|
2
|
+
import { type ConnectOptions, type ConnectTokenParams } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Token parameters accepted by {@link connectLinearCredentials}.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors {@link ConnectTokenParams} from `@vercel/connect`, minus
|
|
7
|
+
* `subject` — Linear Agent tokens are app-scoped, so `subject` is
|
|
8
|
+
* pinned to `{ type: "app" }` by this helper and cannot be overridden.
|
|
9
|
+
*/
|
|
10
|
+
export type ConnectLinearCredentialsParams = Omit<ConnectTokenParams, 'subject'>;
|
|
11
|
+
/**
|
|
12
|
+
* Build {@link LinearChannelCredentials} backed by a Vercel Connect
|
|
13
|
+
* connector that stores a Linear app access token.
|
|
14
|
+
*
|
|
15
|
+
* Eve uses `accessToken` for Linear GraphQL calls. The token is a
|
|
16
|
+
* function form so rotation, refresh, and multi-workspace tenancy stay
|
|
17
|
+
* delegated to Vercel Connect.
|
|
18
|
+
*
|
|
19
|
+
* The webhook verifier accepts Connect-forwarded webhooks authenticated
|
|
20
|
+
* with Vercel OIDC instead of Linear's webhook secret.
|
|
21
|
+
*/
|
|
22
|
+
export declare function connectLinearCredentials(connector: string, params?: ConnectLinearCredentialsParams, options?: ConnectOptions): LinearChannelCredentials;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { vercelOidc } from 'eve/channels/auth';
|
|
2
|
+
import { getToken, } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build {@link LinearChannelCredentials} backed by a Vercel Connect
|
|
5
|
+
* connector that stores a Linear app access token.
|
|
6
|
+
*
|
|
7
|
+
* Eve uses `accessToken` for Linear GraphQL calls. The token is a
|
|
8
|
+
* function form so rotation, refresh, and multi-workspace tenancy stay
|
|
9
|
+
* delegated to Vercel Connect.
|
|
10
|
+
*
|
|
11
|
+
* The webhook verifier accepts Connect-forwarded webhooks authenticated
|
|
12
|
+
* with Vercel OIDC instead of Linear's webhook secret.
|
|
13
|
+
*/
|
|
14
|
+
export function connectLinearCredentials(connector, params = {}, options) {
|
|
15
|
+
return {
|
|
16
|
+
accessToken: () => getToken(connector, { ...params, subject: { type: 'app' } }, options),
|
|
17
|
+
webhookVerifier: vercelOidc(),
|
|
18
|
+
};
|
|
19
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { getToken, getTokenResponse, ConnectError, NoValidTokenError, UserAuthorizationRequiredError, ConnectorInstallationRequiredError, type ConnectErrorOptions, type ConnectOptions, type ConnectTokenParams, type ConnectTokenResponse, type ConnectTokenSubject, type ConnectVendorErrorPayload, } from './token.js';
|
|
1
|
+
export { getToken, getTokenResponse, revokeToken, ConnectError, NoValidTokenError, UserAuthorizationRequiredError, ConnectorInstallationRequiredError, type ConnectErrorOptions, type ConnectOptions, type ConnectTokenParams, type ConnectTokenResponse, type ConnectTokenSubject, type ConnectVendorErrorPayload, } from './token.js';
|
|
2
2
|
export { startAuthorization, type ConnectAuthorizationOptions, type ConnectAuthorizationResponse, } from './authorization.js';
|
|
3
3
|
export type { ConnectAuthorizationDetail } from './authorization-details.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { getToken, getTokenResponse, ConnectError, NoValidTokenError, UserAuthorizationRequiredError, ConnectorInstallationRequiredError, } from './token.js';
|
|
1
|
+
export { getToken, getTokenResponse, revokeToken, ConnectError, NoValidTokenError, UserAuthorizationRequiredError, ConnectorInstallationRequiredError, } from './token.js';
|
|
2
2
|
export { startAuthorization, } from './authorization.js';
|
package/dist/token.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface ConnectTokenParams {
|
|
|
22
22
|
subject: ConnectTokenSubject;
|
|
23
23
|
installationId?: string;
|
|
24
24
|
audience?: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Access scopes to request. Use `['*']` to request the default scopes for
|
|
27
|
+
* the specified subject type.
|
|
28
|
+
*/
|
|
25
29
|
scopes?: string[];
|
|
26
30
|
resources?: string[];
|
|
27
31
|
authorizationDetails?: ConnectAuthorizationDetail[];
|
|
@@ -76,7 +80,23 @@ export declare class ConnectorInstallationRequiredError extends ConnectError {
|
|
|
76
80
|
}
|
|
77
81
|
export interface ConnectOptions {
|
|
78
82
|
vercelToken?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Bypass the in-process token cache and re-fetch from Vercel Connect.
|
|
85
|
+
*
|
|
86
|
+
* The cache normally serves any token that is not within
|
|
87
|
+
* {@link ConnectTokenParams.validityBufferMs} of expiry. That means a
|
|
88
|
+
* grant the user revoked server-side keeps being handed back from the
|
|
89
|
+
* local cache until it expires. Set `forceRefresh` when the caller
|
|
90
|
+
* needs Connect to re-validate the grant on this call — a revoked grant
|
|
91
|
+
* then surfaces as `no_token` / `user_authorization_required` instead of
|
|
92
|
+
* a stale bearer.
|
|
93
|
+
*/
|
|
94
|
+
forceRefresh?: boolean;
|
|
79
95
|
}
|
|
80
96
|
export declare function getToken(connector: string, params: ConnectTokenParams, options?: ConnectOptions): Promise<string>;
|
|
81
97
|
export declare function getTokenResponse(connector: string, params: ConnectTokenParams, options?: ConnectOptions): Promise<ConnectTokenResponse>;
|
|
98
|
+
export declare function revokeToken(connector: string, params: {
|
|
99
|
+
subject: ConnectTokenSubject;
|
|
100
|
+
installationId?: string;
|
|
101
|
+
}, options?: ConnectOptions): Promise<void>;
|
|
82
102
|
export declare function createConnectErrorFromResponse(response: Response, fallbackMessage: string): Promise<ConnectError>;
|
package/dist/token.js
CHANGED
|
@@ -38,15 +38,20 @@ export async function getToken(connector, params, options) {
|
|
|
38
38
|
export async function getTokenResponse(connector, params, options) {
|
|
39
39
|
const bufferMs = params.validityBufferMs ?? DEFAULT_VALIDITY_BUFFER_MS;
|
|
40
40
|
const cacheKey = JSON.stringify({ connector, ...params });
|
|
41
|
-
|
|
42
|
-
if (cached) {
|
|
43
|
-
const now = Date.now();
|
|
44
|
-
if (cached.response.expiresAt - now > bufferMs) {
|
|
45
|
-
cached.lastUsed = now;
|
|
46
|
-
return cached.response;
|
|
47
|
-
}
|
|
41
|
+
if (options?.forceRefresh) {
|
|
48
42
|
cache.delete(cacheKey);
|
|
49
43
|
}
|
|
44
|
+
else {
|
|
45
|
+
const cached = cache.get(cacheKey);
|
|
46
|
+
if (cached) {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (cached.response.expiresAt - now > bufferMs) {
|
|
49
|
+
cached.lastUsed = now;
|
|
50
|
+
return cached.response;
|
|
51
|
+
}
|
|
52
|
+
cache.delete(cacheKey);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
50
55
|
const vercelToken = options?.vercelToken ?? (await getVercelOidcToken());
|
|
51
56
|
const endpoint = `https://api.vercel.com/v1/connect/token/${encodeURIComponent(connector)}`;
|
|
52
57
|
const response = await fetch(endpoint, {
|
|
@@ -68,6 +73,23 @@ export async function getTokenResponse(connector, params, options) {
|
|
|
68
73
|
cache.set(cacheKey, { response: data, lastUsed: Date.now() });
|
|
69
74
|
return data;
|
|
70
75
|
}
|
|
76
|
+
export async function revokeToken(connector, params, options) {
|
|
77
|
+
const vercelToken = options?.vercelToken ?? (await getVercelOidcToken());
|
|
78
|
+
const endpoint = `https://api.vercel.com/v1/connect/connectors/${encodeURIComponent(connector)}/tokens`;
|
|
79
|
+
const response = await fetch(endpoint, {
|
|
80
|
+
method: 'DELETE',
|
|
81
|
+
headers: {
|
|
82
|
+
Accept: 'application/json',
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
Authorization: `Bearer ${vercelToken}`,
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify(params),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw await createConnectErrorFromResponse(response, 'Failed to revoke token');
|
|
90
|
+
}
|
|
91
|
+
cache.clear();
|
|
92
|
+
}
|
|
71
93
|
const DEFAULT_VALIDITY_BUFFER_MS = 30_000;
|
|
72
94
|
const MAX_CACHE_SIZE = 100;
|
|
73
95
|
const cache = new Map();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/connect",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@auth/core": ">=0.37.0",
|
|
46
46
|
"ai": "^6 || ^7",
|
|
47
47
|
"better-auth": ">=1.5.0",
|
|
48
|
-
"eve": ">=0.
|
|
48
|
+
"eve": ">=0.6.0-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"@ai-sdk/mcp": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@auth/core": "0.37.4",
|
|
70
70
|
"ai": "7.0.0-beta.116",
|
|
71
71
|
"better-auth": "1.5.5",
|
|
72
|
-
"eve": "
|
|
72
|
+
"eve": "0.6.0-beta.1",
|
|
73
73
|
"typescript": "^5"
|
|
74
74
|
},
|
|
75
75
|
"private": false,
|