@vercel/oidc 3.5.0 → 3.6.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 +13 -0
- package/dist/auth-errors.js +4 -1
- package/dist/get-vercel-oidc-token-with-refresh.test.js +17 -9
- package/dist/index-browser.test.js +5 -0
- package/dist/token-util.d.ts +1 -1
- package/dist/token-util.js +63 -29
- package/dist/token-util.test.js +63 -40
- package/dist/token.js +10 -4
- package/dist/token.test.js +91 -1
- package/docs/functions/getVercelToken.md +1 -1
- package/package.json +5 -1
- package/dist/auth-config.d.ts +0 -30
- package/dist/auth-config.js +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @vercel/oidc
|
|
2
2
|
|
|
3
|
+
## 3.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fddeb55: Add configurable credentials storage handling across the CLI auth stack. Storage of credentials can be configured by the new `credStorage` key in global `config.json` or the new `VERCEL_TOKEN_STORAGE` environment variable. The environment variable takes precedence over the configuration key. Accepted values are `file` (store credentials in `auth.json`), `keyring` (store credentials in system keyring, e.g macOS Keychain or Secrets Service on Linux), and `auto` (try storing in keyring if available, fall back to `file` if keyring is not available).
|
|
8
|
+
|
|
9
|
+
`@vercel/oidc` supports keyring-stored authentication credentials by delegating the OIDC minting to the CLI executable via `@vercel/cli-exec`.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [fddeb55]
|
|
14
|
+
- @vercel/cli-config@0.2.0
|
|
15
|
+
|
|
3
16
|
## 3.5.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/dist/auth-errors.js
CHANGED
|
@@ -32,8 +32,11 @@ class AccessTokenMissingError extends Error {
|
|
|
32
32
|
}
|
|
33
33
|
class RefreshAccessTokenFailedError extends Error {
|
|
34
34
|
constructor(cause) {
|
|
35
|
-
super("Failed to refresh authentication token."
|
|
35
|
+
super("Failed to refresh authentication token.");
|
|
36
36
|
this.name = "RefreshAccessTokenFailedError";
|
|
37
|
+
if (cause !== void 0) {
|
|
38
|
+
this.cause = cause;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -27,8 +27,19 @@ var os = __toESM(require("os"));
|
|
|
27
27
|
var path = __toESM(require("path"));
|
|
28
28
|
var fs = __toESM(require("fs"));
|
|
29
29
|
var import_token_io = require("./token-io");
|
|
30
|
+
var import_cli_config = require("@vercel/cli-config");
|
|
30
31
|
var import_get_vercel_oidc_token_with_refresh = require("./get-vercel-oidc-token-with-refresh");
|
|
31
32
|
var tokenUtil = __toESM(require("./token-util"));
|
|
33
|
+
import_vitest.vi.mock("@vercel/cli-config", async (importOriginal) => ({
|
|
34
|
+
...await importOriginal(),
|
|
35
|
+
getGlobalPathConfig: import_vitest.vi.fn(),
|
|
36
|
+
getLikelyEffectiveCredStorage: import_vitest.vi.fn(() => "file")
|
|
37
|
+
}));
|
|
38
|
+
import_vitest.vi.mock("@vercel/cli-exec", () => ({
|
|
39
|
+
execVercelCli: import_vitest.vi.fn(),
|
|
40
|
+
VercelCliError: class VercelCliError extends Error {
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
32
43
|
import_vitest.vi.mock("./token-io");
|
|
33
44
|
import_vitest.vi.mock("./get-context", () => ({
|
|
34
45
|
getContext: () => ({ headers: {} })
|
|
@@ -57,6 +68,7 @@ import_vitest.vi.mock("./get-context", () => ({
|
|
|
57
68
|
import_vitest.vi.spyOn(process, "cwd").mockReturnValue(rootDir);
|
|
58
69
|
import_vitest.vi.mocked(import_token_io.findRootDir).mockReturnValue(rootDir);
|
|
59
70
|
import_vitest.vi.mocked(import_token_io.getUserDataDir).mockReturnValue(userDataDir);
|
|
71
|
+
import_vitest.vi.mocked(import_cli_config.getGlobalPathConfig).mockReturnValue(cliDataDir);
|
|
60
72
|
});
|
|
61
73
|
(0, import_vitest.afterEach)(() => {
|
|
62
74
|
if (fs.existsSync(rootDir)) {
|
|
@@ -129,28 +141,24 @@ import_vitest.vi.mock("./get-context", () => ({
|
|
|
129
141
|
);
|
|
130
142
|
import_vitest.vi.spyOn(tokenUtil, "getVercelOidcToken").mockRejectedValue(
|
|
131
143
|
new TypeError(
|
|
132
|
-
"Vercel OIDC token is malformed. Expected a string-valued token property.
|
|
144
|
+
"Vercel OIDC token is malformed. Expected a string-valued token property."
|
|
133
145
|
)
|
|
134
146
|
);
|
|
135
147
|
process.env.VERCEL_OIDC_TOKEN = createExpiredToken();
|
|
136
148
|
await (0, import_vitest.expect)((0, import_get_vercel_oidc_token_with_refresh.getVercelOidcToken)()).rejects.toThrow(
|
|
137
|
-
/Vercel OIDC token is malformed\. Expected a string-valued token property
|
|
149
|
+
/Vercel OIDC token is malformed\. Expected a string-valued token property\./
|
|
138
150
|
);
|
|
139
151
|
});
|
|
140
152
|
(0, import_vitest.test)("should throw helpful error when token has invalid format", async () => {
|
|
141
153
|
process.env.VERCEL_OIDC_TOKEN = "not-a-valid-jwt-token";
|
|
142
154
|
import_vitest.vi.spyOn(tokenUtil, "getTokenPayload").mockImplementation(() => {
|
|
143
|
-
throw new Error("Invalid token.
|
|
155
|
+
throw new Error("Invalid token.");
|
|
144
156
|
});
|
|
145
|
-
await (0, import_vitest.expect)((0, import_get_vercel_oidc_token_with_refresh.getVercelOidcToken)()).rejects.toThrow(
|
|
146
|
-
/Invalid token\. Please run `vc env pull` and try again/
|
|
147
|
-
);
|
|
157
|
+
await (0, import_vitest.expect)((0, import_get_vercel_oidc_token_with_refresh.getVercelOidcToken)()).rejects.toThrow(/Invalid token\./);
|
|
148
158
|
});
|
|
149
159
|
(0, import_vitest.test)("should throw error when token expiry check fails", async () => {
|
|
150
160
|
process.env.VERCEL_OIDC_TOKEN = "not-a-jwt-token";
|
|
151
|
-
await (0, import_vitest.expect)((0, import_get_vercel_oidc_token_with_refresh.getVercelOidcToken)()).rejects.toThrow(
|
|
152
|
-
/Invalid token\. Please run `vc env pull` and try again/
|
|
153
|
-
);
|
|
161
|
+
await (0, import_vitest.expect)((0, import_get_vercel_oidc_token_with_refresh.getVercelOidcToken)()).rejects.toThrow(/Invalid token\./);
|
|
154
162
|
});
|
|
155
163
|
(0, import_vitest.test)("should fail when no token exists and no CLI credentials available", async () => {
|
|
156
164
|
process.env.VERCEL_OIDC_TOKEN = void 0;
|
|
@@ -24,6 +24,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
var import_vitest = require("vitest");
|
|
25
25
|
var DefaultImports = __toESM(require("./index"));
|
|
26
26
|
var BrowserImports = __toESM(require("./index-browser"));
|
|
27
|
+
import_vitest.vi.mock("@vercel/cli-exec", () => ({
|
|
28
|
+
execVercelCli: import_vitest.vi.fn(),
|
|
29
|
+
VercelCliError: class VercelCliError extends Error {
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
27
32
|
(0, import_vitest.describe)("browser export", () => {
|
|
28
33
|
(0, import_vitest.test)("should match the default export", async () => {
|
|
29
34
|
(0, import_vitest.expect)(Object.keys(BrowserImports).sort()).toStrictEqual(
|
package/dist/token-util.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function getVercelDataDir(): string | null;
|
|
2
1
|
export interface GetVercelTokenOptions {
|
|
3
2
|
/**
|
|
4
3
|
* Optional time buffer in milliseconds before token expiry to consider it expired.
|
|
@@ -11,6 +10,7 @@ export declare function getVercelToken(options?: GetVercelTokenOptions): Promise
|
|
|
11
10
|
interface VercelTokenResponse {
|
|
12
11
|
token: string;
|
|
13
12
|
}
|
|
13
|
+
export declare function getVercelOidcTokenFromCli(projectId: string, teamId?: string): Promise<VercelTokenResponse>;
|
|
14
14
|
export declare function getVercelOidcToken(authToken: string, projectId: string, teamId?: string): Promise<VercelTokenResponse | null>;
|
|
15
15
|
export declare function assertVercelOidcTokenResponse(res: unknown): asserts res is VercelTokenResponse;
|
|
16
16
|
export declare function findProjectInfo(): {
|
package/dist/token-util.js
CHANGED
|
@@ -31,8 +31,8 @@ __export(token_util_exports, {
|
|
|
31
31
|
assertVercelOidcTokenResponse: () => assertVercelOidcTokenResponse,
|
|
32
32
|
findProjectInfo: () => findProjectInfo,
|
|
33
33
|
getTokenPayload: () => getTokenPayload,
|
|
34
|
-
getVercelDataDir: () => getVercelDataDir,
|
|
35
34
|
getVercelOidcToken: () => getVercelOidcToken,
|
|
35
|
+
getVercelOidcTokenFromCli: () => getVercelOidcTokenFromCli,
|
|
36
36
|
getVercelToken: () => getVercelToken,
|
|
37
37
|
isExpired: () => isExpired,
|
|
38
38
|
loadToken: () => loadToken,
|
|
@@ -41,29 +41,23 @@ __export(token_util_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(token_util_exports);
|
|
42
42
|
var path = __toESM(require("path"));
|
|
43
43
|
var fs = __toESM(require("fs"));
|
|
44
|
+
var import_cli_exec = require("@vercel/cli-exec");
|
|
45
|
+
var import_cli_config = require("@vercel/cli-config");
|
|
44
46
|
var import_token_error = require("./token-error");
|
|
45
47
|
var import_token_io = require("./token-io");
|
|
46
|
-
var import_auth_config = require("./auth-config");
|
|
47
48
|
var import_oauth = require("./oauth");
|
|
48
49
|
var import_auth_errors = require("./auth-errors");
|
|
49
|
-
function getVercelDataDir() {
|
|
50
|
-
const vercelFolder = "com.vercel.cli";
|
|
51
|
-
const dataDir = (0, import_token_io.getUserDataDir)();
|
|
52
|
-
if (!dataDir) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
return path.join(dataDir, vercelFolder);
|
|
56
|
-
}
|
|
57
50
|
async function getVercelToken(options) {
|
|
58
|
-
const
|
|
59
|
-
|
|
51
|
+
const configDir = (0, import_cli_config.getGlobalPathConfig)();
|
|
52
|
+
const authConfig = (0, import_cli_config.tryReadAuthConfig)(configDir);
|
|
53
|
+
if (!authConfig || !authConfig.token && !authConfig.refreshToken) {
|
|
60
54
|
throw new import_auth_errors.AccessTokenMissingError();
|
|
61
55
|
}
|
|
62
|
-
if (
|
|
56
|
+
if (isValidAccessToken(authConfig, options?.expirationBufferMs)) {
|
|
63
57
|
return authConfig.token;
|
|
64
58
|
}
|
|
65
59
|
if (!authConfig.refreshToken) {
|
|
66
|
-
(0,
|
|
60
|
+
(0, import_cli_config.writeAuthConfig)(configDir, {});
|
|
67
61
|
throw new import_auth_errors.RefreshAccessTokenFailedError("No refresh token available");
|
|
68
62
|
}
|
|
69
63
|
try {
|
|
@@ -72,26 +66,70 @@ async function getVercelToken(options) {
|
|
|
72
66
|
});
|
|
73
67
|
const [tokensError, tokens] = await (0, import_oauth.processTokenResponse)(tokenResponse);
|
|
74
68
|
if (tokensError || !tokens) {
|
|
75
|
-
(0,
|
|
69
|
+
(0, import_cli_config.writeAuthConfig)(configDir, {});
|
|
76
70
|
throw new import_auth_errors.RefreshAccessTokenFailedError(tokensError);
|
|
77
71
|
}
|
|
78
72
|
const updatedConfig = {
|
|
79
73
|
token: tokens.access_token,
|
|
80
|
-
expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
|
|
74
|
+
expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in,
|
|
75
|
+
refreshToken: tokens.refresh_token
|
|
81
76
|
};
|
|
82
|
-
|
|
83
|
-
updatedConfig.refreshToken = tokens.refresh_token;
|
|
84
|
-
}
|
|
85
|
-
(0, import_auth_config.writeAuthConfig)(updatedConfig);
|
|
77
|
+
(0, import_cli_config.writeAuthConfig)(configDir, updatedConfig);
|
|
86
78
|
return updatedConfig.token;
|
|
87
79
|
} catch (error) {
|
|
88
|
-
(0,
|
|
80
|
+
(0, import_cli_config.writeAuthConfig)(configDir, {});
|
|
89
81
|
if (error instanceof import_auth_errors.AccessTokenMissingError || error instanceof import_auth_errors.RefreshAccessTokenFailedError) {
|
|
90
82
|
throw error;
|
|
91
83
|
}
|
|
92
84
|
throw new import_auth_errors.RefreshAccessTokenFailedError(error);
|
|
93
85
|
}
|
|
94
86
|
}
|
|
87
|
+
function isValidAccessToken(authConfig, expirationBufferMs = 0) {
|
|
88
|
+
if (!authConfig.token)
|
|
89
|
+
return false;
|
|
90
|
+
if (typeof authConfig.expiresAt !== "number")
|
|
91
|
+
return true;
|
|
92
|
+
const nowInSeconds = Math.floor(Date.now() / 1e3);
|
|
93
|
+
const bufferInSeconds = expirationBufferMs / 1e3;
|
|
94
|
+
return authConfig.expiresAt >= nowInSeconds + bufferInSeconds;
|
|
95
|
+
}
|
|
96
|
+
async function getVercelOidcTokenFromCli(projectId, teamId) {
|
|
97
|
+
const args = ["project", "token", projectId, "--format=json"];
|
|
98
|
+
if (teamId) {
|
|
99
|
+
args.push("--scope", teamId);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await (0, import_cli_exec.execVercelCli)(args);
|
|
103
|
+
let parsedOutput;
|
|
104
|
+
if (typeof stdout !== "string") {
|
|
105
|
+
throw new import_token_error.VercelOidcTokenError(
|
|
106
|
+
"Failed to refresh OIDC token: `vercel project token` did not return stdout"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
parsedOutput = JSON.parse(stdout);
|
|
111
|
+
} catch {
|
|
112
|
+
throw new import_token_error.VercelOidcTokenError(
|
|
113
|
+
"Failed to refresh OIDC token: `vercel project token` returned invalid JSON: " + stdout
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
assertVercelOidcTokenResponse(parsedOutput);
|
|
117
|
+
return parsedOutput;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof import_token_error.VercelOidcTokenError) {
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
let message = error instanceof Error ? error.message : "";
|
|
123
|
+
const stderr = error instanceof import_cli_exec.VercelCliError ? error.stderr?.trim() : void 0;
|
|
124
|
+
if (stderr && !message.includes(stderr)) {
|
|
125
|
+
message = `${message}
|
|
126
|
+
${stderr}`.trim();
|
|
127
|
+
}
|
|
128
|
+
throw new import_token_error.VercelOidcTokenError(
|
|
129
|
+
message ? `Failed to refresh OIDC token with the Vercel CLI: ${message}` : "Failed to refresh OIDC token with the Vercel CLI"
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
95
133
|
async function getVercelOidcToken(authToken, projectId, teamId) {
|
|
96
134
|
const url = `https://api.vercel.com/v1/projects/${projectId}/token?source=vercel-oidc-refresh${teamId ? `&teamId=${teamId}` : ""}`;
|
|
97
135
|
const res = await fetch(url, {
|
|
@@ -111,13 +149,11 @@ async function getVercelOidcToken(authToken, projectId, teamId) {
|
|
|
111
149
|
}
|
|
112
150
|
function assertVercelOidcTokenResponse(res) {
|
|
113
151
|
if (!res || typeof res !== "object") {
|
|
114
|
-
throw new TypeError(
|
|
115
|
-
"Vercel OIDC token is malformed. Expected an object. Please run `vc env pull` and try again"
|
|
116
|
-
);
|
|
152
|
+
throw new TypeError("Vercel OIDC token is malformed. Expected an object.");
|
|
117
153
|
}
|
|
118
154
|
if (!("token" in res) || typeof res.token !== "string") {
|
|
119
155
|
throw new TypeError(
|
|
120
|
-
"Vercel OIDC token is malformed. Expected a string-valued token property.
|
|
156
|
+
"Vercel OIDC token is malformed. Expected a string-valued token property."
|
|
121
157
|
);
|
|
122
158
|
}
|
|
123
159
|
}
|
|
@@ -174,9 +210,7 @@ function loadToken(projectId) {
|
|
|
174
210
|
function getTokenPayload(token) {
|
|
175
211
|
const tokenParts = token.split(".");
|
|
176
212
|
if (tokenParts.length !== 3) {
|
|
177
|
-
throw new import_token_error.VercelOidcTokenError(
|
|
178
|
-
"Invalid token. Please run `vc env pull` and try again"
|
|
179
|
-
);
|
|
213
|
+
throw new import_token_error.VercelOidcTokenError("Invalid token.");
|
|
180
214
|
}
|
|
181
215
|
const base64 = tokenParts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
182
216
|
const padded = base64.padEnd(
|
|
@@ -193,8 +227,8 @@ function isExpired(token, bufferMs = 0) {
|
|
|
193
227
|
assertVercelOidcTokenResponse,
|
|
194
228
|
findProjectInfo,
|
|
195
229
|
getTokenPayload,
|
|
196
|
-
getVercelDataDir,
|
|
197
230
|
getVercelOidcToken,
|
|
231
|
+
getVercelOidcTokenFromCli,
|
|
198
232
|
getVercelToken,
|
|
199
233
|
isExpired,
|
|
200
234
|
loadToken,
|
package/dist/token-util.test.js
CHANGED
|
@@ -23,9 +23,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
var import_vitest = require("vitest");
|
|
25
25
|
var import_token_util = require("./token-util");
|
|
26
|
-
var
|
|
26
|
+
var cliConfig = __toESM(require("@vercel/cli-config"));
|
|
27
27
|
var oauth = __toESM(require("./oauth"));
|
|
28
28
|
var import_auth_errors = require("./auth-errors");
|
|
29
|
+
import_vitest.vi.mock("@vercel/cli-exec", () => ({
|
|
30
|
+
execVercelCli: import_vitest.vi.fn(),
|
|
31
|
+
VercelCliError: class VercelCliError extends Error {
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
import_vitest.vi.mock("@vercel/cli-config", () => ({
|
|
35
|
+
getGlobalPathConfig: import_vitest.vi.fn(() => "/mock/user/data/com.vercel.cli"),
|
|
36
|
+
tryReadAuthConfig: import_vitest.vi.fn(),
|
|
37
|
+
writeAuthConfig: import_vitest.vi.fn()
|
|
38
|
+
}));
|
|
29
39
|
import_vitest.vi.mock("fs");
|
|
30
40
|
import_vitest.vi.mock("./token-io", () => ({
|
|
31
41
|
getUserDataDir: import_vitest.vi.fn(() => "/mock/user/data"),
|
|
@@ -45,15 +55,13 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
45
55
|
expiresAt: Math.floor(Date.now() / 1e3) + 3600
|
|
46
56
|
// expires in 1 hour
|
|
47
57
|
};
|
|
48
|
-
import_vitest.vi.
|
|
49
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
50
|
-
});
|
|
58
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(validToken);
|
|
51
59
|
const token = await (0, import_token_util.getVercelToken)();
|
|
52
60
|
(0, import_vitest.expect)(token).toBe("valid-access-token");
|
|
53
|
-
(0, import_vitest.expect)(
|
|
61
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).not.toHaveBeenCalled();
|
|
54
62
|
});
|
|
55
63
|
(0, import_vitest.it)("should throw AccessTokenMissingError if auth config does not exist", async () => {
|
|
56
|
-
import_vitest.vi.
|
|
64
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(null);
|
|
57
65
|
await (0, import_vitest.expect)((0, import_token_util.getVercelToken)()).rejects.toThrow(import_auth_errors.AccessTokenMissingError);
|
|
58
66
|
});
|
|
59
67
|
(0, import_vitest.it)("should refresh token if expired and refresh token exists", async () => {
|
|
@@ -72,16 +80,15 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
72
80
|
refresh_token: "new-refresh-token"
|
|
73
81
|
})
|
|
74
82
|
};
|
|
75
|
-
import_vitest.vi.
|
|
76
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
77
|
-
});
|
|
83
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
78
84
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockResponse);
|
|
79
85
|
const token = await (0, import_token_util.getVercelToken)();
|
|
80
86
|
(0, import_vitest.expect)(token).toBe("new-access-token");
|
|
81
87
|
(0, import_vitest.expect)(oauth.refreshTokenRequest).toHaveBeenCalledWith({
|
|
82
88
|
refresh_token: "valid-refresh-token"
|
|
83
89
|
});
|
|
84
|
-
(0, import_vitest.expect)(
|
|
90
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).toHaveBeenCalledWith(
|
|
91
|
+
import_vitest.expect.any(String),
|
|
85
92
|
import_vitest.expect.objectContaining({
|
|
86
93
|
token: "new-access-token",
|
|
87
94
|
refreshToken: "new-refresh-token",
|
|
@@ -89,20 +96,41 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
89
96
|
})
|
|
90
97
|
);
|
|
91
98
|
});
|
|
99
|
+
(0, import_vitest.it)("should refresh token when only refreshToken is present", async () => {
|
|
100
|
+
const refreshOnlyToken = {
|
|
101
|
+
refreshToken: "valid-refresh-token"
|
|
102
|
+
};
|
|
103
|
+
const mockResponse = {
|
|
104
|
+
ok: true,
|
|
105
|
+
json: async () => ({
|
|
106
|
+
access_token: "new-access-token",
|
|
107
|
+
token_type: "Bearer",
|
|
108
|
+
expires_in: 3600
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(refreshOnlyToken);
|
|
112
|
+
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockResponse);
|
|
113
|
+
const token = await (0, import_token_util.getVercelToken)();
|
|
114
|
+
(0, import_vitest.expect)(token).toBe("new-access-token");
|
|
115
|
+
(0, import_vitest.expect)(oauth.refreshTokenRequest).toHaveBeenCalledWith({
|
|
116
|
+
refresh_token: "valid-refresh-token"
|
|
117
|
+
});
|
|
118
|
+
});
|
|
92
119
|
(0, import_vitest.it)("should clear auth and throw RefreshAccessTokenFailedError if token expired and no refresh token", async () => {
|
|
93
120
|
const expiredTokenNoRefresh = {
|
|
94
121
|
token: "expired-access-token",
|
|
95
122
|
expiresAt: Math.floor(Date.now() / 1e3) - 3600
|
|
96
123
|
};
|
|
97
|
-
import_vitest.vi.
|
|
124
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(
|
|
98
125
|
expiredTokenNoRefresh
|
|
99
126
|
);
|
|
100
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
101
|
-
});
|
|
102
127
|
await (0, import_vitest.expect)((0, import_token_util.getVercelToken)()).rejects.toThrow(
|
|
103
128
|
import_auth_errors.RefreshAccessTokenFailedError
|
|
104
129
|
);
|
|
105
|
-
(0, import_vitest.expect)(
|
|
130
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).toHaveBeenCalledWith(
|
|
131
|
+
import_vitest.expect.any(String),
|
|
132
|
+
{}
|
|
133
|
+
);
|
|
106
134
|
});
|
|
107
135
|
(0, import_vitest.it)("should clear auth and throw RefreshAccessTokenFailedError if refresh fails with OAuth error", async () => {
|
|
108
136
|
const expiredToken = {
|
|
@@ -117,14 +145,15 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
117
145
|
error_description: "Refresh token expired"
|
|
118
146
|
})
|
|
119
147
|
};
|
|
120
|
-
import_vitest.vi.
|
|
121
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
122
|
-
});
|
|
148
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
123
149
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockErrorResponse);
|
|
124
150
|
await (0, import_vitest.expect)((0, import_token_util.getVercelToken)()).rejects.toThrow(
|
|
125
151
|
import_auth_errors.RefreshAccessTokenFailedError
|
|
126
152
|
);
|
|
127
|
-
(0, import_vitest.expect)(
|
|
153
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).toHaveBeenCalledWith(
|
|
154
|
+
import_vitest.expect.any(String),
|
|
155
|
+
{}
|
|
156
|
+
);
|
|
128
157
|
});
|
|
129
158
|
(0, import_vitest.it)("should clear auth and throw RefreshAccessTokenFailedError if refresh fails with network error", async () => {
|
|
130
159
|
const expiredToken = {
|
|
@@ -132,27 +161,26 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
132
161
|
refreshToken: "valid-refresh-token",
|
|
133
162
|
expiresAt: Math.floor(Date.now() / 1e3) - 3600
|
|
134
163
|
};
|
|
135
|
-
import_vitest.vi.
|
|
136
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
137
|
-
});
|
|
164
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
138
165
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockRejectedValue(
|
|
139
166
|
new Error("Network error")
|
|
140
167
|
);
|
|
141
168
|
await (0, import_vitest.expect)((0, import_token_util.getVercelToken)()).rejects.toThrow(
|
|
142
169
|
import_auth_errors.RefreshAccessTokenFailedError
|
|
143
170
|
);
|
|
144
|
-
(0, import_vitest.expect)(
|
|
171
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).toHaveBeenCalledWith(
|
|
172
|
+
import_vitest.expect.any(String),
|
|
173
|
+
{}
|
|
174
|
+
);
|
|
145
175
|
});
|
|
146
176
|
(0, import_vitest.it)("should treat token as valid if expiresAt is missing (--token case)", async () => {
|
|
147
177
|
const tokenWithoutExpiry = {
|
|
148
178
|
token: "cli-provided-token"
|
|
149
179
|
};
|
|
150
|
-
import_vitest.vi.
|
|
151
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
152
|
-
});
|
|
180
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(tokenWithoutExpiry);
|
|
153
181
|
const token = await (0, import_token_util.getVercelToken)();
|
|
154
182
|
(0, import_vitest.expect)(token).toBe("cli-provided-token");
|
|
155
|
-
(0, import_vitest.expect)(
|
|
183
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).not.toHaveBeenCalled();
|
|
156
184
|
});
|
|
157
185
|
(0, import_vitest.it)("should preserve new refresh token if provided in response", async () => {
|
|
158
186
|
const expiredToken = {
|
|
@@ -169,18 +197,17 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
169
197
|
refresh_token: "new-refresh-token"
|
|
170
198
|
})
|
|
171
199
|
};
|
|
172
|
-
import_vitest.vi.
|
|
173
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
174
|
-
});
|
|
200
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
175
201
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockResponse);
|
|
176
202
|
await (0, import_token_util.getVercelToken)();
|
|
177
|
-
(0, import_vitest.expect)(
|
|
203
|
+
(0, import_vitest.expect)(cliConfig.writeAuthConfig).toHaveBeenCalledWith(
|
|
204
|
+
import_vitest.expect.any(String),
|
|
178
205
|
import_vitest.expect.objectContaining({
|
|
179
206
|
refreshToken: "new-refresh-token"
|
|
180
207
|
})
|
|
181
208
|
);
|
|
182
209
|
});
|
|
183
|
-
(0, import_vitest.it)("should
|
|
210
|
+
(0, import_vitest.it)("should preserve refresh token if not provided in response", async () => {
|
|
184
211
|
const expiredToken = {
|
|
185
212
|
token: "expired-access-token",
|
|
186
213
|
refreshToken: "existing-refresh-token",
|
|
@@ -195,13 +222,11 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
195
222
|
// No refresh_token in response
|
|
196
223
|
})
|
|
197
224
|
};
|
|
198
|
-
import_vitest.vi.
|
|
199
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
200
|
-
});
|
|
225
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
201
226
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockResponse);
|
|
202
227
|
await (0, import_token_util.getVercelToken)();
|
|
203
|
-
const writeCall = import_vitest.vi.mocked(
|
|
204
|
-
(0, import_vitest.expect)(writeCall).
|
|
228
|
+
const writeCall = import_vitest.vi.mocked(cliConfig.writeAuthConfig).mock.calls[0][1];
|
|
229
|
+
(0, import_vitest.expect)(writeCall).toHaveProperty("refreshToken", "existing-refresh-token");
|
|
205
230
|
});
|
|
206
231
|
(0, import_vitest.it)("should calculate expiresAt correctly from expires_in", async () => {
|
|
207
232
|
const expiredToken = {
|
|
@@ -218,14 +243,12 @@ import_vitest.vi.mock("./token-io", () => ({
|
|
|
218
243
|
// 2 hours
|
|
219
244
|
})
|
|
220
245
|
};
|
|
221
|
-
import_vitest.vi.
|
|
222
|
-
import_vitest.vi.spyOn(authConfig, "writeAuthConfig").mockImplementation(() => {
|
|
223
|
-
});
|
|
246
|
+
import_vitest.vi.mocked(cliConfig.tryReadAuthConfig).mockReturnValue(expiredToken);
|
|
224
247
|
import_vitest.vi.spyOn(oauth, "refreshTokenRequest").mockResolvedValue(mockResponse);
|
|
225
248
|
const beforeCall = Math.floor(Date.now() / 1e3);
|
|
226
249
|
await (0, import_token_util.getVercelToken)();
|
|
227
250
|
const afterCall = Math.floor(Date.now() / 1e3);
|
|
228
|
-
const writeCall = import_vitest.vi.mocked(
|
|
251
|
+
const writeCall = import_vitest.vi.mocked(cliConfig.writeAuthConfig).mock.calls[0][1];
|
|
229
252
|
(0, import_vitest.expect)(writeCall.expiresAt).toBeGreaterThanOrEqual(beforeCall + 7200);
|
|
230
253
|
(0, import_vitest.expect)(writeCall.expiresAt).toBeLessThanOrEqual(afterCall + 7200);
|
|
231
254
|
});
|
package/dist/token.js
CHANGED
|
@@ -21,6 +21,7 @@ __export(token_exports, {
|
|
|
21
21
|
refreshToken: () => refreshToken
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(token_exports);
|
|
24
|
+
var import_cli_config = require("@vercel/cli-config");
|
|
24
25
|
var import_token_error = require("./token-error");
|
|
25
26
|
var import_token_util = require("./token-util");
|
|
26
27
|
async function refreshToken(options) {
|
|
@@ -42,10 +43,15 @@ async function refreshToken(options) {
|
|
|
42
43
|
}
|
|
43
44
|
let maybeToken = (0, import_token_util.loadToken)(projectId);
|
|
44
45
|
if (!maybeToken || (0, import_token_util.isExpired)((0, import_token_util.getTokenPayload)(maybeToken.token), options?.expirationBufferMs)) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const configDir = (0, import_cli_config.getGlobalPathConfig)();
|
|
47
|
+
if ((0, import_cli_config.getLikelyEffectiveCredStorage)(configDir) === "keyring") {
|
|
48
|
+
maybeToken = await (0, import_token_util.getVercelOidcTokenFromCli)(projectId, teamId);
|
|
49
|
+
} else {
|
|
50
|
+
const authToken = await (0, import_token_util.getVercelToken)({
|
|
51
|
+
expirationBufferMs: options?.expirationBufferMs
|
|
52
|
+
});
|
|
53
|
+
maybeToken = await (0, import_token_util.getVercelOidcToken)(authToken, projectId, teamId);
|
|
54
|
+
}
|
|
49
55
|
if (!maybeToken) {
|
|
50
56
|
throw new import_token_error.VercelOidcTokenError("Failed to refresh OIDC token");
|
|
51
57
|
}
|
package/dist/token.test.js
CHANGED
|
@@ -26,9 +26,25 @@ var import_vitest = require("vitest");
|
|
|
26
26
|
var os = __toESM(require("os"));
|
|
27
27
|
var path = __toESM(require("path"));
|
|
28
28
|
var fs = __toESM(require("fs"));
|
|
29
|
+
var import_cli_config = require("@vercel/cli-config");
|
|
30
|
+
var import_cli_exec = require("@vercel/cli-exec");
|
|
29
31
|
var import_token_io = require("./token-io");
|
|
30
32
|
var tokenUtil = __toESM(require("./token-util"));
|
|
31
33
|
var import_token = require("./token");
|
|
34
|
+
import_vitest.vi.mock("@vercel/cli-config", () => ({
|
|
35
|
+
getGlobalPathConfig: import_vitest.vi.fn(),
|
|
36
|
+
getLikelyEffectiveCredStorage: import_vitest.vi.fn(() => "file")
|
|
37
|
+
}));
|
|
38
|
+
import_vitest.vi.mock("@vercel/cli-exec", () => ({
|
|
39
|
+
execVercelCli: import_vitest.vi.fn(),
|
|
40
|
+
VercelCliError: class VercelCliError extends Error {
|
|
41
|
+
constructor(options) {
|
|
42
|
+
super(options.message);
|
|
43
|
+
this.name = "VercelCliError";
|
|
44
|
+
this.stderr = options.stderr;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
32
48
|
import_vitest.vi.mock("./token-io");
|
|
33
49
|
(0, import_vitest.describe)("refreshToken", () => {
|
|
34
50
|
let rootDir;
|
|
@@ -38,6 +54,7 @@ import_vitest.vi.mock("./token-io");
|
|
|
38
54
|
const projectId = "prj_test123";
|
|
39
55
|
(0, import_vitest.beforeEach)(() => {
|
|
40
56
|
import_vitest.vi.clearAllMocks();
|
|
57
|
+
import_vitest.vi.mocked(import_cli_config.getLikelyEffectiveCredStorage).mockReturnValue("file");
|
|
41
58
|
process.env.VERCEL_OIDC_TOKEN = void 0;
|
|
42
59
|
const random = `test-${(0, import_crypto.randomUUID)()}`;
|
|
43
60
|
rootDir = path.join(os.tmpdir(), random);
|
|
@@ -49,7 +66,10 @@ import_vitest.vi.mock("./token-io");
|
|
|
49
66
|
fs.mkdirSync(path.join(rootDir, ".vercel"), {
|
|
50
67
|
recursive: true
|
|
51
68
|
});
|
|
52
|
-
fs.writeFileSync(
|
|
69
|
+
fs.writeFileSync(
|
|
70
|
+
path.join(cliDataDir, "auth.json"),
|
|
71
|
+
JSON.stringify({ token: "test" })
|
|
72
|
+
);
|
|
53
73
|
fs.writeFileSync(
|
|
54
74
|
path.join(rootDir, ".vercel", "project.json"),
|
|
55
75
|
JSON.stringify({ projectId })
|
|
@@ -57,6 +77,7 @@ import_vitest.vi.mock("./token-io");
|
|
|
57
77
|
import_vitest.vi.spyOn(process, "cwd").mockReturnValue(rootDir);
|
|
58
78
|
import_vitest.vi.mocked(import_token_io.findRootDir).mockReturnValue(rootDir);
|
|
59
79
|
import_vitest.vi.mocked(import_token_io.getUserDataDir).mockReturnValue(userDataDir);
|
|
80
|
+
import_vitest.vi.mocked(import_cli_config.getGlobalPathConfig).mockReturnValue(cliDataDir);
|
|
60
81
|
import_vitest.vi.spyOn(tokenUtil, "getVercelToken").mockResolvedValue("test");
|
|
61
82
|
import_vitest.vi.spyOn(tokenUtil, "getVercelOidcToken").mockResolvedValue({
|
|
62
83
|
token: "test-token"
|
|
@@ -66,6 +87,15 @@ import_vitest.vi.mock("./token-io");
|
|
|
66
87
|
name: "test-name",
|
|
67
88
|
exp: Date.now() + 1e5
|
|
68
89
|
});
|
|
90
|
+
import_vitest.vi.mocked(import_cli_exec.execVercelCli).mockResolvedValue({
|
|
91
|
+
stdout: JSON.stringify({ token: "cli-token" }),
|
|
92
|
+
stderr: "",
|
|
93
|
+
invocation: {
|
|
94
|
+
command: "vercel",
|
|
95
|
+
commandArgs: [],
|
|
96
|
+
source: "path"
|
|
97
|
+
}
|
|
98
|
+
});
|
|
69
99
|
});
|
|
70
100
|
(0, import_vitest.test)("should correctly load saved token from file", async () => {
|
|
71
101
|
const token = { token: "test-saved" };
|
|
@@ -81,6 +111,66 @@ import_vitest.vi.mock("./token-io");
|
|
|
81
111
|
const token = JSON.parse(fs.readFileSync(tokenPath, "utf8"));
|
|
82
112
|
(0, import_vitest.expect)(token).toEqual({ token: "test-token" });
|
|
83
113
|
});
|
|
114
|
+
(0, import_vitest.test)("should use cli-exec when keyring-backed credentials are likely", async () => {
|
|
115
|
+
import_vitest.vi.mocked(import_cli_config.getLikelyEffectiveCredStorage).mockReturnValue("keyring");
|
|
116
|
+
await (0, import_token.refreshToken)();
|
|
117
|
+
(0, import_vitest.expect)(import_cli_exec.execVercelCli).toHaveBeenCalledWith([
|
|
118
|
+
"project",
|
|
119
|
+
"token",
|
|
120
|
+
projectId,
|
|
121
|
+
"--format=json"
|
|
122
|
+
]);
|
|
123
|
+
(0, import_vitest.expect)(tokenUtil.getVercelToken).not.toHaveBeenCalled();
|
|
124
|
+
(0, import_vitest.expect)(tokenUtil.getVercelOidcToken).not.toHaveBeenCalled();
|
|
125
|
+
(0, import_vitest.expect)(process.env.VERCEL_OIDC_TOKEN).toBe("cli-token");
|
|
126
|
+
});
|
|
127
|
+
(0, import_vitest.test)("should pass scope to cli-exec when team is known", async () => {
|
|
128
|
+
const customTeamId = "team_customscope123";
|
|
129
|
+
import_vitest.vi.mocked(import_cli_config.getLikelyEffectiveCredStorage).mockReturnValue("keyring");
|
|
130
|
+
await (0, import_token.refreshToken)({ team: customTeamId, project: projectId });
|
|
131
|
+
(0, import_vitest.expect)(import_cli_exec.execVercelCli).toHaveBeenCalledWith([
|
|
132
|
+
"project",
|
|
133
|
+
"token",
|
|
134
|
+
projectId,
|
|
135
|
+
"--format=json",
|
|
136
|
+
"--scope",
|
|
137
|
+
customTeamId
|
|
138
|
+
]);
|
|
139
|
+
});
|
|
140
|
+
(0, import_vitest.test)("should fail with a cli-specific error for malformed cli json output", async () => {
|
|
141
|
+
import_vitest.vi.mocked(import_cli_config.getLikelyEffectiveCredStorage).mockReturnValue("keyring");
|
|
142
|
+
import_vitest.vi.mocked(import_cli_exec.execVercelCli).mockResolvedValue({
|
|
143
|
+
stdout: JSON.stringify({ nope: "missing-token" }),
|
|
144
|
+
stderr: "",
|
|
145
|
+
invocation: {
|
|
146
|
+
command: "vercel",
|
|
147
|
+
commandArgs: [],
|
|
148
|
+
source: "path"
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
await (0, import_vitest.expect)((0, import_token.refreshToken)()).rejects.toThrow(
|
|
152
|
+
"Failed to refresh OIDC token with the Vercel CLI: Vercel OIDC token is malformed. Expected a string-valued token property."
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
(0, import_vitest.test)("should include cli stderr when cli-exec fails", async () => {
|
|
156
|
+
import_vitest.vi.mocked(import_cli_config.getLikelyEffectiveCredStorage).mockReturnValue("keyring");
|
|
157
|
+
import_vitest.vi.mocked(import_cli_exec.execVercelCli).mockRejectedValue(
|
|
158
|
+
new import_cli_exec.VercelCliError({
|
|
159
|
+
code: "VERCEL_CLI_ERRORED",
|
|
160
|
+
message: "Command failed with exit code 1",
|
|
161
|
+
stderr: "Project not found for this scope",
|
|
162
|
+
exitCode: 1,
|
|
163
|
+
invocation: {
|
|
164
|
+
command: "vercel",
|
|
165
|
+
commandArgs: [],
|
|
166
|
+
source: "path"
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
await (0, import_vitest.expect)((0, import_token.refreshToken)()).rejects.toThrow(
|
|
171
|
+
"Failed to refresh OIDC token with the Vercel CLI: Command failed with exit code 1\nProject not found for this scope"
|
|
172
|
+
);
|
|
173
|
+
});
|
|
84
174
|
(0, import_vitest.test)("should use provided team and project instead of reading project.json", async () => {
|
|
85
175
|
const customProjectId = "prj_custom123";
|
|
86
176
|
const customTeamId = "team_custom456";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
> **getVercelToken**(`options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\>
|
|
8
8
|
|
|
9
|
-
Defined in: [packages/oidc/src/token-util.ts:
|
|
9
|
+
Defined in: [packages/oidc/src/token-util.ts:27](https://github.com/vercel/vercel/blob/main/packages/oidc/src/token-util.ts#L27)
|
|
10
10
|
|
|
11
11
|
## Parameters
|
|
12
12
|
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"require": "./dist/index.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
|
-
"version": "3.
|
|
21
|
+
"version": "3.6.0",
|
|
22
22
|
"repository": {
|
|
23
23
|
"directory": "packages/oidc",
|
|
24
24
|
"type": "git",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"bugs": {
|
|
28
28
|
"url": "https://github.com/vercel/vercel/issues"
|
|
29
29
|
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@vercel/cli-config": "0.2.0",
|
|
32
|
+
"@vercel/cli-exec": "0.1.1"
|
|
33
|
+
},
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"prettier": "3.8.3",
|
|
32
36
|
"tinyspawn": "1.3.1",
|
package/dist/auth-config.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
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;
|
package/dist/auth-config.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
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
|
-
});
|