@vercel/cli-auth 0.0.1

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/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@vercel/cli-auth",
3
+ "description": "Used by Vercel's CLIs to handle authentication",
4
+ "version": "0.0.1",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/vercel/vercel.git",
11
+ "directory": "packages/cli-auth"
12
+ },
13
+ "private": false,
14
+ "files": [
15
+ "*.js",
16
+ "*.js.map",
17
+ "*.ts",
18
+ "!vitest.config.mts",
19
+ "!**/*.test.ts"
20
+ ],
21
+ "dependencies": {
22
+ "async-listen": "3.0.0",
23
+ "open": "8.4.0",
24
+ "xdg-app-paths": "5",
25
+ "zod": "4.1.11"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "22",
29
+ "memfs": "4.14.0",
30
+ "typescript": "5.9.2",
31
+ "vite": "7.1.7",
32
+ "vitest": "2.1.4"
33
+ },
34
+ "ci-cd": {
35
+ "//": "https://www.notion.so/1d6e06b059c48017a7afe7e8698de336",
36
+ "test": {
37
+ "requiredDependencies": [
38
+ "@api/tests"
39
+ ]
40
+ }
41
+ },
42
+ "scripts": {
43
+ "build": "pnpm clear && tsc -b",
44
+ "clear": "rm -rf *.js *.d.ts *.js.map tsconfig.tsbuildinfo",
45
+ "test": "vitest",
46
+ "typecheck": "tsc --noEmit"
47
+ }
48
+ }
package/sso.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import * as z from 'zod/mini';
2
+ import type { OAuth } from './oauth';
3
+ import type { Credentials } from './credentials-store';
4
+ /**
5
+ * Re-authorize a team's SSO session by opening the browser and waiting for the user to complete
6
+ * the SSO flow. Use when the user's SSO session has expired or missing.
7
+ *
8
+ */
9
+ export declare function reauthorizeTeam(params: {
10
+ /** The team's slug that requires re-authorization */
11
+ team: string;
12
+ /** The current access token that needs to be re-authorized */
13
+ token: Credentials['token'];
14
+ oauth: OAuth;
15
+ }): Promise<void>;
16
+ declare const SSOError: z.ZodMiniObject<{
17
+ code: z.ZodMiniString<string>;
18
+ message: z.ZodMiniString<string>;
19
+ scope: z.ZodMiniOptional<z.ZodMiniString<string>>;
20
+ enforced: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
21
+ }, z.core.$strip>;
22
+ /** Parse an error-like object to determine if SSO re-authorization is required. */
23
+ export declare function parseSSOError(error: unknown): z.infer<typeof SSOError> | undefined;
24
+ export {};
package/sso.js ADDED
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.reauthorizeTeam = reauthorizeTeam;
40
+ exports.parseSSOError = parseSSOError;
41
+ const node_http_1 = __importDefault(require("node:http"));
42
+ const async_listen_1 = require("async-listen");
43
+ const open_1 = __importDefault(require("open"));
44
+ const z = __importStar(require("zod/mini"));
45
+ /**
46
+ * Re-authorize a team's SSO session by opening the browser and waiting for the user to complete
47
+ * the SSO flow. Use when the user's SSO session has expired or missing.
48
+ *
49
+ */
50
+ async function reauthorizeTeam(params) {
51
+ const { team, token, oauth } = params;
52
+ if (!token) {
53
+ throw new TypeError('Token is missing, cannot initiate SSO re-authorization. Please log in again.');
54
+ }
55
+ const introspection = await oauth.introspectToken(token);
56
+ const { active, session_id, client_id } = introspection;
57
+ if (!active || !session_id || !client_id) {
58
+ throw new Error('Introspection failed. Token is either expired or otherwise invalid. Please log in again.');
59
+ }
60
+ const verificationToken = await waitForVerification({
61
+ team,
62
+ session_id,
63
+ client_id,
64
+ });
65
+ await fetch(`https://api.vercel.com/registration/verify?${new URLSearchParams({ token: verificationToken })}`);
66
+ }
67
+ /**
68
+ * Get a verification token by spawning a localhost HTTP server
69
+ * that gets redirected to as the OAuth callback URL.
70
+ *
71
+ * The token returned can be sent back to the Vercel API to complete
72
+ * the SSO re-authorization.
73
+ */
74
+ async function waitForVerification(params) {
75
+ const { team, session_id, client_id } = params;
76
+ const server = node_http_1.default.createServer();
77
+ const { port } = await (0, async_listen_1.listen)(server, 0, '127.0.0.1');
78
+ const initSSOUrl = new URL(`/sso/${team}?${new URLSearchParams({
79
+ session_id,
80
+ client_id,
81
+ next: `http://localhost:${port}`,
82
+ })}`, `https://vercel.com`);
83
+ try {
84
+ const [query] = await Promise.all([
85
+ new Promise((resolve, reject) => {
86
+ server.once('request', (req, res) => {
87
+ // Close the HTTP connection to prevent `server.close()` from hanging
88
+ res.setHeader('connection', 'close');
89
+ const { searchParams } = new URL(req.url || '/', 'http://localhost');
90
+ resolve(searchParams);
91
+ // Redirect the user back to the Vercel login notification page
92
+ res.statusCode = 302;
93
+ res.setHeader('location', getNotificationUrl(searchParams)).end();
94
+ });
95
+ server.once('error', reject);
96
+ }),
97
+ (0, open_1.default)(initSSOUrl.href),
98
+ ]);
99
+ const loginError = query.get('loginError');
100
+ if (loginError) {
101
+ const error = new Error('Login failed.');
102
+ error.cause = JSON.parse(loginError);
103
+ throw error;
104
+ }
105
+ const token = query.get('token');
106
+ if (!token) {
107
+ throw new TypeError('Could not get verification token. Please contact support.');
108
+ }
109
+ return token;
110
+ }
111
+ finally {
112
+ server.close();
113
+ }
114
+ }
115
+ function getNotificationUrl(params) {
116
+ const loginState = params.has('loginError')
117
+ ? 'failed'
118
+ : params.has('ssoEmail')
119
+ ? 'incomplete'
120
+ : 'success';
121
+ return `https://vercel.com/notifications/cli-login-${loginState}?${params}`;
122
+ }
123
+ const SSOError = z.object({
124
+ code: z.string(),
125
+ message: z.string(),
126
+ scope: z.optional(z.string()),
127
+ enforced: z.optional(z.boolean()),
128
+ });
129
+ /** Parse an error-like object to determine if SSO re-authorization is required. */
130
+ function parseSSOError(error) {
131
+ const { data } = SSOError.safeParse(error);
132
+ if (!data?.enforced)
133
+ return;
134
+ return data;
135
+ }
136
+ //# sourceMappingURL=sso.js.map
package/sso.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["sso.ts"],"names":[],"mappings":"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"}
package/sso.ts ADDED
@@ -0,0 +1,135 @@
1
+ import http from 'node:http';
2
+ import { listen } from 'async-listen';
3
+ import open from 'open';
4
+ import * as z from 'zod/mini';
5
+ import type { OAuth } from './oauth';
6
+ import type { Credentials } from './credentials-store';
7
+
8
+ /**
9
+ * Re-authorize a team's SSO session by opening the browser and waiting for the user to complete
10
+ * the SSO flow. Use when the user's SSO session has expired or missing.
11
+ *
12
+ */
13
+ export async function reauthorizeTeam(params: {
14
+ /** The team's slug that requires re-authorization */
15
+ team: string;
16
+ /** The current access token that needs to be re-authorized */
17
+ token: Credentials['token'];
18
+ oauth: OAuth;
19
+ }): Promise<void> {
20
+ const { team, token, oauth } = params;
21
+
22
+ if (!token) {
23
+ throw new TypeError(
24
+ 'Token is missing, cannot initiate SSO re-authorization. Please log in again.'
25
+ );
26
+ }
27
+
28
+ const introspection = await oauth.introspectToken(token);
29
+ const { active, session_id, client_id } = introspection;
30
+
31
+ if (!active || !session_id || !client_id) {
32
+ throw new Error(
33
+ 'Introspection failed. Token is either expired or otherwise invalid. Please log in again.'
34
+ );
35
+ }
36
+
37
+ const verificationToken = await waitForVerification({
38
+ team,
39
+ session_id,
40
+ client_id,
41
+ });
42
+
43
+ await fetch(
44
+ `https://api.vercel.com/registration/verify?${new URLSearchParams({ token: verificationToken })}`
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Get a verification token by spawning a localhost HTTP server
50
+ * that gets redirected to as the OAuth callback URL.
51
+ *
52
+ * The token returned can be sent back to the Vercel API to complete
53
+ * the SSO re-authorization.
54
+ */
55
+ async function waitForVerification(params: {
56
+ team: string;
57
+ session_id: string;
58
+ client_id: string;
59
+ }): Promise<string> {
60
+ const { team, session_id, client_id } = params;
61
+ const server = http.createServer();
62
+ const { port } = await listen(server, 0, '127.0.0.1');
63
+ const initSSOUrl = new URL(
64
+ `/sso/${team}?${new URLSearchParams({
65
+ session_id,
66
+ client_id,
67
+ next: `http://localhost:${port}`,
68
+ })}`,
69
+ `https://vercel.com`
70
+ );
71
+
72
+ try {
73
+ const [query] = await Promise.all([
74
+ new Promise<URLSearchParams>((resolve, reject) => {
75
+ server.once('request', (req, res) => {
76
+ // Close the HTTP connection to prevent `server.close()` from hanging
77
+ res.setHeader('connection', 'close');
78
+
79
+ const { searchParams } = new URL(req.url || '/', 'http://localhost');
80
+ resolve(searchParams);
81
+
82
+ // Redirect the user back to the Vercel login notification page
83
+ res.statusCode = 302;
84
+ res.setHeader('location', getNotificationUrl(searchParams)).end();
85
+ });
86
+ server.once('error', reject);
87
+ }),
88
+ open(initSSOUrl.href),
89
+ ]);
90
+
91
+ const loginError = query.get('loginError');
92
+ if (loginError) {
93
+ const error = new Error('Login failed.');
94
+ error.cause = JSON.parse(loginError);
95
+ throw error;
96
+ }
97
+
98
+ const token = query.get('token');
99
+ if (!token) {
100
+ throw new TypeError(
101
+ 'Could not get verification token. Please contact support.'
102
+ );
103
+ }
104
+
105
+ return token;
106
+ } finally {
107
+ server.close();
108
+ }
109
+ }
110
+
111
+ function getNotificationUrl(params: URLSearchParams): string {
112
+ const loginState = params.has('loginError')
113
+ ? 'failed'
114
+ : params.has('ssoEmail')
115
+ ? 'incomplete'
116
+ : 'success';
117
+
118
+ return `https://vercel.com/notifications/cli-login-${loginState}?${params}`;
119
+ }
120
+
121
+ const SSOError = z.object({
122
+ code: z.string(),
123
+ message: z.string(),
124
+ scope: z.optional(z.string()),
125
+ enforced: z.optional(z.boolean()),
126
+ });
127
+
128
+ /** Parse an error-like object to determine if SSO re-authorization is required. */
129
+ export function parseSSOError(
130
+ error: unknown
131
+ ): z.infer<typeof SSOError> | undefined {
132
+ const { data } = SSOError.safeParse(error);
133
+ if (!data?.enforced) return;
134
+ return data;
135
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Get the user agent string for the current environment.
3
+ *
4
+ * @exapmle
5
+ * ```
6
+ * getUserAgent({ name: 'my-cli', version: '1.0.0' });
7
+ * // 'hostname @ my-cli 1.0.0 node-v18.16.0 linux (x64)'
8
+ * ```
9
+ */
10
+ export declare function getUserAgent(pkg: {
11
+ name: string;
12
+ version: string;
13
+ }): string;
package/user-agent.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getUserAgent = getUserAgent;
7
+ const node_os_1 = __importDefault(require("node:os"));
8
+ /**
9
+ * Get the user agent string for the current environment.
10
+ *
11
+ * @exapmle
12
+ * ```
13
+ * getUserAgent({ name: 'my-cli', version: '1.0.0' });
14
+ * // 'hostname @ my-cli 1.0.0 node-v18.16.0 linux (x64)'
15
+ * ```
16
+ */
17
+ function getUserAgent(pkg) {
18
+ return `${node_os_1.default.hostname()} @ ${pkg.name} ${pkg.version} node-${process.version} ${node_os_1.default.platform()} (${node_os_1.default.arch()})`;
19
+ }
20
+ //# sourceMappingURL=user-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-agent.js","sourceRoot":"","sources":["user-agent.ts"],"names":[],"mappings":";;;;;AAWA,oCAIC;AAfD,sDAAyB;AAEzB;;;;;;;;GAQG;AACH,SAAgB,YAAY,CAAC,GAAsC;IACjE,OAAO,GAAG,iBAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,SAClD,OAAO,CAAC,OACV,IAAI,iBAAE,CAAC,QAAQ,EAAE,KAAK,iBAAE,CAAC,IAAI,EAAE,GAAG,CAAC;AACrC,CAAC"}
package/user-agent.ts ADDED
@@ -0,0 +1,16 @@
1
+ import os from 'node:os';
2
+
3
+ /**
4
+ * Get the user agent string for the current environment.
5
+ *
6
+ * @exapmle
7
+ * ```
8
+ * getUserAgent({ name: 'my-cli', version: '1.0.0' });
9
+ * // 'hostname @ my-cli 1.0.0 node-v18.16.0 linux (x64)'
10
+ * ```
11
+ */
12
+ export function getUserAgent(pkg: { name: string; version: string }): string {
13
+ return `${os.hostname()} @ ${pkg.name} ${pkg.version} node-${
14
+ process.version
15
+ } ${os.platform()} (${os.arch()})`;
16
+ }