@vercel/oidc 3.0.5 → 3.2.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/CHANGELOG.md +18 -0
- package/README.md +38 -0
- package/dist/auth-config.d.ts +30 -0
- package/dist/auth-config.js +85 -0
- package/dist/auth-errors.d.ts +16 -0
- package/dist/auth-errors.js +43 -0
- package/dist/get-vercel-oidc-token.d.ts +34 -1
- package/dist/get-vercel-oidc-token.js +10 -6
- package/dist/get-vercel-oidc-token.test.d.ts +1 -0
- package/dist/get-vercel-oidc-token.test.js +363 -0
- package/dist/index-browser.d.ts +2 -0
- package/dist/index-browser.js +12 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -2
- package/dist/oauth.d.ts +17 -0
- package/dist/oauth.js +88 -0
- package/dist/token-io.d.ts +1 -1
- package/dist/token-io.js +1 -1
- package/dist/token-util.d.ts +10 -2
- package/dist/token-util.js +101 -75
- package/dist/token-util.test.d.ts +1 -0
- package/dist/token-util.test.js +232 -0
- package/dist/token.d.ts +19 -1
- package/dist/token.js +21 -14
- package/dist/token.test.js +108 -2
- package/docs/README.md +49 -4
- package/docs/classes/AccessTokenMissingError.md +160 -0
- package/docs/classes/RefreshAccessTokenFailedError.md +166 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @vercel/oidc
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- - Add optional `team` and `project` parameters to `getVercelOidcToken()` to allow explicit control over token refresh behavior instead of always reading from `.vercel/project.json` ([#14864](https://github.com/vercel/vercel/pull/14864))
|
|
8
|
+
- Add `expirationBufferMs` option to both `getVercelOidcToken()` and `getVercelToken()` to proactively refresh tokens before they expire (useful for avoiding auth errors mid-request)
|
|
9
|
+
- Export `getVercelToken()` function with `GetVercelTokenOptions` interface to allow refreshing CLI tokens with configurable expiration buffer
|
|
10
|
+
|
|
11
|
+
## 3.1.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- Allow vercel/oidc to refresh the vercel CLI auth token when running locally ([#14543](https://github.com/vercel/vercel/pull/14543))
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- improve error messages for package consumers ([#14449](https://github.com/vercel/vercel/pull/14449))
|
|
20
|
+
|
|
3
21
|
## 3.0.5
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
1
|
# `@vercel/oidc`
|
|
2
2
|
|
|
3
3
|
Runtime OIDC helper methods intended to be used with your Vercel Functions
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### Getting an OIDC Token
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { getVercelOidcToken } from '@vercel/oidc';
|
|
11
|
+
|
|
12
|
+
// Get token using project.json configuration
|
|
13
|
+
const token = await getVercelOidcToken();
|
|
14
|
+
|
|
15
|
+
// Get token with explicit project and team (supports both IDs and slugs)
|
|
16
|
+
const token = await getVercelOidcToken({
|
|
17
|
+
project: 'my-project', // or 'prj_abc123'
|
|
18
|
+
team: 'my-team', // or 'team_xyz789'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Get token with expiration buffer (refresh if expires within 5 minutes)
|
|
22
|
+
const token = await getVercelOidcToken({
|
|
23
|
+
expirationBufferMs: 5 * 60 * 1000,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
|
|
29
|
+
### `getVercelOidcToken(options?)`
|
|
30
|
+
|
|
31
|
+
Gets the current OIDC token from the request context or environment variable. Will refresh the token if expired in development.
|
|
32
|
+
|
|
33
|
+
**Options:**
|
|
34
|
+
|
|
35
|
+
- `project?: string` - Project ID (prj\_\*) or slug
|
|
36
|
+
- `team?: string` - Team ID (team\_\*) or slug
|
|
37
|
+
- `expirationBufferMs?: number` - Buffer time in ms before expiry to trigger refresh (default: 0)
|
|
38
|
+
|
|
39
|
+
### `getVercelOidcTokenSync()`
|
|
40
|
+
|
|
41
|
+
Synchronously gets the current OIDC token without refreshing. Use `getVercelOidcToken()` if you need automatic refresh in development.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth configuration stored in ~/.../com.vercel.cli/auth.json
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthConfig {
|
|
5
|
+
/** An `access_token` obtained using the OAuth Device Authorization flow. */
|
|
6
|
+
token?: string;
|
|
7
|
+
/** A `refresh_token` obtained using the OAuth Device Authorization flow. */
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The absolute time (seconds) when the token expires.
|
|
11
|
+
* Used to optimistically check if the token is still valid.
|
|
12
|
+
*/
|
|
13
|
+
expiresAt?: number;
|
|
14
|
+
/** Whether to skip writing this config to disk. */
|
|
15
|
+
skipWrite?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read the auth config from disk
|
|
19
|
+
* Returns null if the file doesn't exist or cannot be read
|
|
20
|
+
*/
|
|
21
|
+
export declare function readAuthConfig(): AuthConfig | null;
|
|
22
|
+
/**
|
|
23
|
+
* Write the auth config to disk with proper permissions
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeAuthConfig(config: AuthConfig): void;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an access token is valid (not expired)
|
|
28
|
+
* Copied from packages/cli/src/util/client.ts:72-81
|
|
29
|
+
*/
|
|
30
|
+
export declare function isValidAccessToken(authConfig: AuthConfig, expirationBufferMs?: number): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var auth_config_exports = {};
|
|
30
|
+
__export(auth_config_exports, {
|
|
31
|
+
isValidAccessToken: () => isValidAccessToken,
|
|
32
|
+
readAuthConfig: () => readAuthConfig,
|
|
33
|
+
writeAuthConfig: () => writeAuthConfig
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(auth_config_exports);
|
|
36
|
+
var fs = __toESM(require("fs"));
|
|
37
|
+
var path = __toESM(require("path"));
|
|
38
|
+
var import_token_util = require("./token-util");
|
|
39
|
+
function getAuthConfigPath() {
|
|
40
|
+
const dataDir = (0, import_token_util.getVercelDataDir)();
|
|
41
|
+
if (!dataDir) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return path.join(dataDir, "auth.json");
|
|
47
|
+
}
|
|
48
|
+
function readAuthConfig() {
|
|
49
|
+
try {
|
|
50
|
+
const authPath = getAuthConfigPath();
|
|
51
|
+
if (!fs.existsSync(authPath)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const content = fs.readFileSync(authPath, "utf8");
|
|
55
|
+
if (!content) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return JSON.parse(content);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function writeAuthConfig(config) {
|
|
64
|
+
const authPath = getAuthConfigPath();
|
|
65
|
+
const authDir = path.dirname(authPath);
|
|
66
|
+
if (!fs.existsSync(authDir)) {
|
|
67
|
+
fs.mkdirSync(authDir, { mode: 504, recursive: true });
|
|
68
|
+
}
|
|
69
|
+
fs.writeFileSync(authPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
70
|
+
}
|
|
71
|
+
function isValidAccessToken(authConfig, expirationBufferMs = 0) {
|
|
72
|
+
if (!authConfig.token)
|
|
73
|
+
return false;
|
|
74
|
+
if (typeof authConfig.expiresAt !== "number")
|
|
75
|
+
return true;
|
|
76
|
+
const nowInSeconds = Math.floor(Date.now() / 1e3);
|
|
77
|
+
const bufferInSeconds = expirationBufferMs / 1e3;
|
|
78
|
+
return authConfig.expiresAt >= nowInSeconds + bufferInSeconds;
|
|
79
|
+
}
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
isValidAccessToken,
|
|
83
|
+
readAuthConfig,
|
|
84
|
+
writeAuthConfig
|
|
85
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when no authentication configuration is found.
|
|
3
|
+
* This typically means the user needs to log in.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AccessTokenMissingError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when attempting to refresh the authentication token fails.
|
|
11
|
+
* This includes cases where no refresh token is available.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RefreshAccessTokenFailedError extends Error {
|
|
14
|
+
name: string;
|
|
15
|
+
constructor(cause?: unknown);
|
|
16
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var auth_errors_exports = {};
|
|
20
|
+
__export(auth_errors_exports, {
|
|
21
|
+
AccessTokenMissingError: () => AccessTokenMissingError,
|
|
22
|
+
RefreshAccessTokenFailedError: () => RefreshAccessTokenFailedError
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(auth_errors_exports);
|
|
25
|
+
class AccessTokenMissingError extends Error {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(
|
|
28
|
+
"No authentication found. Please log in with the Vercel CLI (vercel login)."
|
|
29
|
+
);
|
|
30
|
+
this.name = "AccessTokenMissingError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
class RefreshAccessTokenFailedError extends Error {
|
|
34
|
+
constructor(cause) {
|
|
35
|
+
super("Failed to refresh authentication token.", { cause });
|
|
36
|
+
this.name = "RefreshAccessTokenFailedError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
40
|
+
0 && (module.exports = {
|
|
41
|
+
AccessTokenMissingError,
|
|
42
|
+
RefreshAccessTokenFailedError
|
|
43
|
+
});
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for getting the Vercel OIDC token.
|
|
3
|
+
*/
|
|
4
|
+
export interface GetVercelOidcTokenOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Optional team ID (team_*) or slug to use for token refresh.
|
|
7
|
+
* When provided, this team will be used instead of reading from `.vercel/project.json`.
|
|
8
|
+
*/
|
|
9
|
+
team?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional project ID (prj_*) or slug to use for token refresh.
|
|
12
|
+
* When provided, this project will be used instead of reading from `.vercel/project.json`.
|
|
13
|
+
*/
|
|
14
|
+
project?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional time buffer in milliseconds before token expiry to consider it expired.
|
|
17
|
+
* When provided, the token will be refreshed if it expires within this buffer time.
|
|
18
|
+
* @default 0
|
|
19
|
+
*/
|
|
20
|
+
expirationBufferMs?: number;
|
|
21
|
+
}
|
|
1
22
|
/**
|
|
2
23
|
* Gets the current OIDC token from the request context or the environment variable.
|
|
3
24
|
*
|
|
@@ -8,6 +29,7 @@
|
|
|
8
29
|
*
|
|
9
30
|
* Unlike the `getVercelOidcTokenSync` function, this function will refresh the token if it is expired in a development environment.
|
|
10
31
|
*
|
|
32
|
+
* @param {GetVercelOidcTokenOptions} [options] - Optional configuration for token retrieval.
|
|
11
33
|
* @returns {Promise<string>} A promise that resolves to the OIDC token.
|
|
12
34
|
* @throws {Error} If the `x-vercel-oidc-token` header is missing from the request context and the environment variable `VERCEL_OIDC_TOKEN` is not set. If the token
|
|
13
35
|
* is expired in a development environment, will also throw an error if the token cannot be refreshed: no CLI credentials are available, CLI credentials are expired, no project configuration is available
|
|
@@ -23,8 +45,19 @@
|
|
|
23
45
|
* console.error('Error:', error.message);
|
|
24
46
|
* });
|
|
25
47
|
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
*
|
|
51
|
+
* ```js
|
|
52
|
+
* // Using the OIDC token with explicit team and project (supports IDs and slugs)
|
|
53
|
+
* getVercelOidcToken({ team: 'my-team', project: 'my-project' }).then((token) => {
|
|
54
|
+
* console.log('OIDC Token:', token);
|
|
55
|
+
* }).catch((error) => {
|
|
56
|
+
* console.error('Error:', error.message);
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
26
59
|
*/
|
|
27
|
-
export declare function getVercelOidcToken(): Promise<string>;
|
|
60
|
+
export declare function getVercelOidcToken(options?: GetVercelOidcTokenOptions): Promise<string>;
|
|
28
61
|
/**
|
|
29
62
|
* Gets the current OIDC token from the request context or the environment variable.
|
|
30
63
|
*
|
|
@@ -34,7 +34,7 @@ __export(get_vercel_oidc_token_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(get_vercel_oidc_token_exports);
|
|
35
35
|
var import_get_context = require("./get-context");
|
|
36
36
|
var import_token_error = require("./token-error");
|
|
37
|
-
async function getVercelOidcToken() {
|
|
37
|
+
async function getVercelOidcToken(options) {
|
|
38
38
|
let token = "";
|
|
39
39
|
let err;
|
|
40
40
|
try {
|
|
@@ -47,16 +47,20 @@ async function getVercelOidcToken() {
|
|
|
47
47
|
await import("./token-util.js"),
|
|
48
48
|
await import("./token.js")
|
|
49
49
|
]);
|
|
50
|
-
if (!token || isExpired(getTokenPayload(token))) {
|
|
51
|
-
await refreshToken();
|
|
50
|
+
if (!token || isExpired(getTokenPayload(token), options?.expirationBufferMs)) {
|
|
51
|
+
await refreshToken(options);
|
|
52
52
|
token = getVercelOidcTokenSync();
|
|
53
53
|
}
|
|
54
54
|
} catch (error) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
let message = err instanceof Error ? err.message : "";
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
message = `${message}
|
|
57
58
|
${error.message}`;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
if (message) {
|
|
61
|
+
throw new import_token_error.VercelOidcTokenError(message);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
60
64
|
}
|
|
61
65
|
return token;
|
|
62
66
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|