@vercel/connect 2.3.3 → 2.3.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/dist/authjs/connect-provider.js +5 -5
- package/dist/betterauth/connect-provider.js +5 -5
- package/dist/internal/authorization-context.d.ts +9 -0
- package/dist/internal/authorization-context.js +57 -0
- package/package.json +1 -1
- package/dist/internal/team-id.d.ts +0 -3
- package/dist/internal/team-id.js +0 -47
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { customFetch } from '@auth/core';
|
|
27
27
|
import { getVercelOidcToken } from '@vercel/oidc';
|
|
28
|
-
import {
|
|
28
|
+
import { tryGetVercelAuthorizationContext, withAuthorizationContext, } from '../internal/authorization-context.js';
|
|
29
29
|
const CONNECT_AUTHORIZATION_URL = 'https://connect.vercel.com/oauth/authorize';
|
|
30
30
|
const CONNECT_TOKEN_URL = 'https://connect.vercel.com/oauth/token';
|
|
31
31
|
const CONNECT_USERINFO_URL = 'https://connect.vercel.com/oauth/userinfo';
|
|
@@ -49,10 +49,10 @@ export function connect(options) {
|
|
|
49
49
|
client: { token_endpoint_auth_method: 'none' },
|
|
50
50
|
authorization: {
|
|
51
51
|
// Best-effort: scope the Vercel Connect consent UI to the
|
|
52
|
-
// deployment's
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
url:
|
|
52
|
+
// deployment's team, project, and environment from the OIDC token.
|
|
53
|
+
// Falls back to the unqualified URL when the token isn't present
|
|
54
|
+
// (e.g. running outside Vercel).
|
|
55
|
+
url: withAuthorizationContext(CONNECT_AUTHORIZATION_URL, tryGetVercelAuthorizationContext()),
|
|
56
56
|
params: { scope },
|
|
57
57
|
},
|
|
58
58
|
token: CONNECT_TOKEN_URL,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { getVercelOidcToken } from '@vercel/oidc';
|
|
31
31
|
import { getOAuth2Tokens } from 'better-auth/oauth2';
|
|
32
|
-
import {
|
|
32
|
+
import { tryGetVercelAuthorizationContext, withAuthorizationContext, } from '../internal/authorization-context.js';
|
|
33
33
|
import { ConnectError, createConnectErrorFromResponse } from '../token.js';
|
|
34
34
|
const CONNECT_AUTHORIZATION_URL = 'https://connect.vercel.com/oauth/authorize';
|
|
35
35
|
const CONNECT_TOKEN_URL = 'https://connect.vercel.com/oauth/token';
|
|
@@ -46,10 +46,10 @@ export function connect(options) {
|
|
|
46
46
|
providerId: options.providerId,
|
|
47
47
|
clientId: options.connector,
|
|
48
48
|
// Best-effort: scope the Vercel Connect consent UI to the
|
|
49
|
-
// deployment's
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
authorizationUrl:
|
|
49
|
+
// deployment's team, project, and environment from the OIDC token.
|
|
50
|
+
// Falls back to the unqualified URL when the token isn't present
|
|
51
|
+
// (e.g. running outside Vercel).
|
|
52
|
+
authorizationUrl: withAuthorizationContext(CONNECT_AUTHORIZATION_URL, tryGetVercelAuthorizationContext()),
|
|
53
53
|
scopes,
|
|
54
54
|
pkce: true,
|
|
55
55
|
tokenUrl: CONNECT_TOKEN_URL,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface AuthorizationContext {
|
|
2
|
+
teamId?: string;
|
|
3
|
+
projectId?: string;
|
|
4
|
+
environment?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function tryGetVercelAuthorizationContext(): AuthorizationContext | undefined;
|
|
7
|
+
export declare function extractAuthorizationContextFromOidcToken(token: string): AuthorizationContext | undefined;
|
|
8
|
+
export declare function withAuthorizationContext(baseUrl: string, context: AuthorizationContext | undefined): string;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort Vercel authorization context extraction for the OAuth adapters.
|
|
3
|
+
*
|
|
4
|
+
* Vercel Connect's `/oauth/authorize` endpoint accepts an
|
|
5
|
+
* optional team id, project id, and environment to scope the consent UI.
|
|
6
|
+
* These values come from the Vercel OIDC token's `owner_id`, `project_id`,
|
|
7
|
+
* and `custom_environment_id` or `environment` claims. We decode the JWT
|
|
8
|
+
* payload without signature verification — we're reading our own token.
|
|
9
|
+
*
|
|
10
|
+
* Failure is silent: the helper returns `undefined` if the token
|
|
11
|
+
* isn't available (e.g. running outside Vercel) or the payload can't
|
|
12
|
+
* be decoded. Adapters then fall back to an unqualified
|
|
13
|
+
* authorization URL.
|
|
14
|
+
*/
|
|
15
|
+
import { getVercelOidcTokenSync } from '@vercel/oidc';
|
|
16
|
+
export function tryGetVercelAuthorizationContext() {
|
|
17
|
+
let token;
|
|
18
|
+
try {
|
|
19
|
+
token = getVercelOidcTokenSync();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return extractAuthorizationContextFromOidcToken(token);
|
|
25
|
+
}
|
|
26
|
+
export function extractAuthorizationContextFromOidcToken(token) {
|
|
27
|
+
try {
|
|
28
|
+
const parts = token.split('.');
|
|
29
|
+
if (parts.length < 2)
|
|
30
|
+
return undefined;
|
|
31
|
+
const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
32
|
+
const padLen = (4 - (padded.length % 4)) % 4;
|
|
33
|
+
const claims = JSON.parse(atob(padded + '='.repeat(padLen)));
|
|
34
|
+
const environment = claims.custom_environment_id || claims.environment;
|
|
35
|
+
return {
|
|
36
|
+
teamId: typeof claims.owner_id === 'string' ? claims.owner_id : undefined,
|
|
37
|
+
projectId: typeof claims.project_id === 'string' ? claims.project_id : undefined,
|
|
38
|
+
environment: typeof environment === 'string' ? environment : undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function withAuthorizationContext(baseUrl, context) {
|
|
46
|
+
if (!context)
|
|
47
|
+
return baseUrl;
|
|
48
|
+
const url = new URL(baseUrl);
|
|
49
|
+
if (context.teamId)
|
|
50
|
+
url.searchParams.set('teamId', context.teamId);
|
|
51
|
+
if (context.projectId)
|
|
52
|
+
url.searchParams.set('projectId', context.projectId);
|
|
53
|
+
if (context.environment) {
|
|
54
|
+
url.searchParams.set('environment', context.environment);
|
|
55
|
+
}
|
|
56
|
+
return url.toString();
|
|
57
|
+
}
|
package/package.json
CHANGED
package/dist/internal/team-id.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Best-effort Vercel team-id extraction for the OAuth adapters.
|
|
3
|
-
*
|
|
4
|
-
* Vercel Connect's `/oauth/authorize` endpoint accepts an
|
|
5
|
-
* optional `?teamId=...` query parameter that scopes the consent UI
|
|
6
|
-
* to a specific team. The team id lives inside the Vercel OIDC
|
|
7
|
-
* token's `owner_id` claim, so we decode the JWT payload (no
|
|
8
|
-
* signature verification — we're reading our own token) and pull it
|
|
9
|
-
* out.
|
|
10
|
-
*
|
|
11
|
-
* Failure is silent: the helper returns `undefined` if the token
|
|
12
|
-
* isn't available (e.g. running outside Vercel) or the payload can't
|
|
13
|
-
* be decoded. Adapters then fall back to an unqualified
|
|
14
|
-
* authorization URL.
|
|
15
|
-
*/
|
|
16
|
-
import { getVercelOidcTokenSync } from '@vercel/oidc';
|
|
17
|
-
export function tryGetVercelTeamId() {
|
|
18
|
-
let token;
|
|
19
|
-
try {
|
|
20
|
-
token = getVercelOidcTokenSync();
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
return extractTeamIdFromOidcToken(token);
|
|
26
|
-
}
|
|
27
|
-
export function extractTeamIdFromOidcToken(token) {
|
|
28
|
-
try {
|
|
29
|
-
const parts = token.split('.');
|
|
30
|
-
if (parts.length < 2)
|
|
31
|
-
return undefined;
|
|
32
|
-
const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
33
|
-
const padLen = (4 - (padded.length % 4)) % 4;
|
|
34
|
-
const claims = JSON.parse(atob(padded + '='.repeat(padLen)));
|
|
35
|
-
return typeof claims.owner_id === 'string' ? claims.owner_id : undefined;
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export function withTeamId(baseUrl, teamId) {
|
|
42
|
-
if (!teamId)
|
|
43
|
-
return baseUrl;
|
|
44
|
-
const url = new URL(baseUrl);
|
|
45
|
-
url.searchParams.set('teamId', teamId);
|
|
46
|
-
return url.toString();
|
|
47
|
-
}
|