@heroku/heroku-fetch 0.1.5 → 0.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/README.md +19 -21
- package/dist/browser.d.ts +1 -2
- package/dist/browser.js +1 -2
- package/dist/client/http-error-handler.js +2 -1
- package/dist/client/http-request-hooks.js +2 -25
- package/dist/client/node-environment-defaults.d.ts +10 -2
- package/dist/client/node-environment-defaults.js +13 -11
- package/dist/client/two-factor-authentication-handler.js +2 -1
- package/dist/debug-loggers.d.ts +0 -1
- package/dist/debug-loggers.js +0 -1
- package/dist/host.d.ts +33 -0
- package/dist/host.js +38 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/redact.d.ts +26 -0
- package/dist/redact.js +158 -0
- package/dist/types.d.ts +8 -1
- package/package.json +12 -13
- package/dist/auth/auth.d.ts +0 -66
- package/dist/auth/auth.js +0 -93
- package/dist/cli/cli-login.d.ts +0 -7
- package/dist/cli/cli-login.js +0 -7
- package/dist/cli/login/browser-login.d.ts +0 -8
- package/dist/cli/login/browser-login.js +0 -115
- package/dist/cli/login/index.d.ts +0 -34
- package/dist/cli/login/index.js +0 -193
- package/dist/cli/login/interactive-login.d.ts +0 -8
- package/dist/cli/login/interactive-login.js +0 -59
- package/dist/cli/login/netrc-utils.d.ts +0 -22
- package/dist/cli/login/netrc-utils.js +0 -85
- package/dist/cli/login/oauth.d.ts +0 -20
- package/dist/cli/login/oauth.js +0 -142
- package/dist/cli/login/sso-login.d.ts +0 -8
- package/dist/cli/login/sso-login.js +0 -41
- package/dist/cli/login/types.d.ts +0 -42
- package/dist/cli/login/types.js +0 -11
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Netrc file management utilities
|
|
3
|
-
*/
|
|
4
|
-
import { Netrc } from 'netrc-parser';
|
|
5
|
-
import { debugCliLogin } from '../../debug-loggers.js';
|
|
6
|
-
// Defer netrc instantiation to avoid eager file operations
|
|
7
|
-
let _netrc;
|
|
8
|
-
export function getNetrc() {
|
|
9
|
-
if (!_netrc) {
|
|
10
|
-
_netrc = new Netrc();
|
|
11
|
-
}
|
|
12
|
-
return _netrc;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Save authentication token to netrc file
|
|
16
|
-
*/
|
|
17
|
-
export async function saveToken(entry, apiHost, httpGitHost) {
|
|
18
|
-
debugCliLogin('Saving credentials to netrc for hosts: %s, %s', apiHost, httpGitHost);
|
|
19
|
-
const netrc = getNetrc();
|
|
20
|
-
await netrc.load();
|
|
21
|
-
const hosts = [apiHost, httpGitHost];
|
|
22
|
-
for (const host of hosts) {
|
|
23
|
-
if (!netrc.machines[host])
|
|
24
|
-
netrc.machines[host] = {};
|
|
25
|
-
netrc.machines[host].login = entry.login;
|
|
26
|
-
netrc.machines[host].password = entry.password;
|
|
27
|
-
delete netrc.machines[host].method;
|
|
28
|
-
delete netrc.machines[host].org;
|
|
29
|
-
debugCliLogin('Set credentials for host: %s (login: %s)', host, entry.login);
|
|
30
|
-
}
|
|
31
|
-
if (netrc.machines._tokens) {
|
|
32
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
33
|
-
netrc.machines._tokens.forEach((token) => {
|
|
34
|
-
if (hosts.includes(token.host)) {
|
|
35
|
-
token.internalWhitespace = '\n ';
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
await netrc.save();
|
|
40
|
-
debugCliLogin('Netrc file saved successfully');
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Clear authentication tokens from netrc file
|
|
44
|
-
*/
|
|
45
|
-
export async function clearTokens(apiHost, httpGitHost) {
|
|
46
|
-
debugCliLogin('Clearing credentials from netrc for hosts: %s, %s', apiHost, httpGitHost);
|
|
47
|
-
const netrc = getNetrc();
|
|
48
|
-
await netrc.load();
|
|
49
|
-
delete netrc.machines[apiHost];
|
|
50
|
-
delete netrc.machines[httpGitHost];
|
|
51
|
-
await netrc.save();
|
|
52
|
-
debugCliLogin('Credentials cleared from netrc successfully');
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Get stored token from netrc file
|
|
56
|
-
*/
|
|
57
|
-
export async function getStoredToken(apiHost) {
|
|
58
|
-
debugCliLogin('Reading stored token from netrc for host: %s', apiHost);
|
|
59
|
-
const netrc = getNetrc();
|
|
60
|
-
await netrc.load();
|
|
61
|
-
const entry = netrc.machines[apiHost];
|
|
62
|
-
if (entry?.password) {
|
|
63
|
-
debugCliLogin('Token found in netrc for host: %s', apiHost);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
debugCliLogin('No token found in netrc for host: %s', apiHost);
|
|
67
|
-
}
|
|
68
|
-
return entry?.password;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get stored login from netrc file
|
|
72
|
-
*/
|
|
73
|
-
export async function getStoredLogin(apiHost) {
|
|
74
|
-
debugCliLogin('Reading stored login from netrc for host: %s', apiHost);
|
|
75
|
-
const netrc = getNetrc();
|
|
76
|
-
await netrc.load();
|
|
77
|
-
const entry = netrc.machines[apiHost];
|
|
78
|
-
if (entry?.login) {
|
|
79
|
-
debugCliLogin('Login found in netrc for host: %s (login: %s)', apiHost, entry.login);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
debugCliLogin('No login found in netrc for host: %s', apiHost);
|
|
83
|
-
}
|
|
84
|
-
return entry?.login;
|
|
85
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OAuth token operations
|
|
3
|
-
*/
|
|
4
|
-
import type { HerokuApiClient } from '../../client/index.js';
|
|
5
|
-
import type { NetrcEntry } from './types.js';
|
|
6
|
-
/**
|
|
7
|
-
* Create an OAuth token via the Heroku API
|
|
8
|
-
*/
|
|
9
|
-
export declare function createOAuthToken(apiUrl: string, username: string, password: string, opts?: {
|
|
10
|
-
expiresIn?: number;
|
|
11
|
-
secondFactor?: string;
|
|
12
|
-
}): Promise<NetrcEntry>;
|
|
13
|
-
/**
|
|
14
|
-
* Get the default OAuth token for the current user
|
|
15
|
-
*/
|
|
16
|
-
export declare function getDefaultToken(client: HerokuApiClient): Promise<string | undefined>;
|
|
17
|
-
/**
|
|
18
|
-
* Delete OAuth session and authorizations for the given token
|
|
19
|
-
*/
|
|
20
|
-
export declare function deleteOAuthTokens(client: HerokuApiClient, token: string): Promise<void>;
|
package/dist/cli/login/oauth.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OAuth token operations
|
|
3
|
-
*/
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { debugCliLogin } from '../../debug-loggers.js';
|
|
6
|
-
import { THIRTY_DAYS } from './types.js';
|
|
7
|
-
const hostname = os.hostname();
|
|
8
|
-
/**
|
|
9
|
-
* Create a basic auth header value
|
|
10
|
-
*/
|
|
11
|
-
function basicAuth(username, password) {
|
|
12
|
-
const auth = Buffer.from([username, password].join(':')).toString('base64');
|
|
13
|
-
return `Basic ${auth}`;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Create an OAuth token via the Heroku API
|
|
17
|
-
*/
|
|
18
|
-
export async function createOAuthToken(apiUrl, username, password, opts = {}) {
|
|
19
|
-
debugCliLogin('Creating OAuth authorization for user: %s', username);
|
|
20
|
-
debugCliLogin('Expires in: %d seconds%s', opts.expiresIn || THIRTY_DAYS, opts.secondFactor ? ', with 2FA' : '');
|
|
21
|
-
const requestHeaders = {
|
|
22
|
-
Accept: 'application/vnd.heroku+json; version=3',
|
|
23
|
-
Authorization: basicAuth(username, password),
|
|
24
|
-
'Content-Type': 'application/json',
|
|
25
|
-
};
|
|
26
|
-
if (opts.secondFactor) {
|
|
27
|
-
requestHeaders['Heroku-Two-Factor-Code'] = opts.secondFactor;
|
|
28
|
-
}
|
|
29
|
-
const response = await fetch(`${apiUrl}/oauth/authorizations`, {
|
|
30
|
-
body: JSON.stringify({
|
|
31
|
-
description: `Heroku CLI login from ${hostname}`,
|
|
32
|
-
expires_in: opts.expiresIn || THIRTY_DAYS,
|
|
33
|
-
scope: ['global'],
|
|
34
|
-
}),
|
|
35
|
-
headers: requestHeaders,
|
|
36
|
-
method: 'POST',
|
|
37
|
-
});
|
|
38
|
-
if (!response.ok) {
|
|
39
|
-
const errorBody = await response.json();
|
|
40
|
-
debugCliLogin('OAuth authorization creation failed: %d %s (error id: %s)', response.status, response.statusText, errorBody.id);
|
|
41
|
-
const error = new Error(errorBody.message || response.statusText);
|
|
42
|
-
error.statusCode = response.status;
|
|
43
|
-
error.body = errorBody;
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
const auth = (await response.json());
|
|
47
|
-
debugCliLogin('OAuth authorization created successfully (id: %s)', auth.id);
|
|
48
|
-
return {
|
|
49
|
-
login: auth.user.email,
|
|
50
|
-
password: auth.access_token.token,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Get the default OAuth token for the current user
|
|
55
|
-
*/
|
|
56
|
-
export async function getDefaultToken(client) {
|
|
57
|
-
try {
|
|
58
|
-
debugCliLogin('Fetching default OAuth authorization');
|
|
59
|
-
const response = await client.get('/oauth/authorizations/~');
|
|
60
|
-
const authorization = (await response.json());
|
|
61
|
-
debugCliLogin('Default authorization found: %s', authorization.id);
|
|
62
|
-
return authorization.access_token?.token;
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
if (error instanceof Error) {
|
|
66
|
-
const errBody = error.body;
|
|
67
|
-
if (errBody?.id === 'not_found'
|
|
68
|
-
&& errBody?.resource === 'authorization') {
|
|
69
|
-
debugCliLogin('Default authorization not found');
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (errBody?.id === 'unauthorized') {
|
|
73
|
-
debugCliLogin('Unauthorized to fetch default authorization');
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
debugCliLogin('Error fetching default authorization: %O', error);
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Delete OAuth session and authorizations for the given token
|
|
83
|
-
*/
|
|
84
|
-
export async function deleteOAuthTokens(client, token) {
|
|
85
|
-
debugCliLogin('Deleting OAuth tokens and sessions');
|
|
86
|
-
// Delete the OAuth session (for SSO logins)
|
|
87
|
-
const sessionDeletion = client
|
|
88
|
-
.delete('/oauth/sessions/~')
|
|
89
|
-
.then(() => {
|
|
90
|
-
debugCliLogin('OAuth session deleted successfully');
|
|
91
|
-
})
|
|
92
|
-
.catch(error => {
|
|
93
|
-
if (error instanceof Error) {
|
|
94
|
-
const errBody = error.body;
|
|
95
|
-
if (errBody?.id === 'not_found'
|
|
96
|
-
&& errBody?.resource === 'session') {
|
|
97
|
-
debugCliLogin('OAuth session not found (expected for non-SSO logins)');
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (errBody?.id === 'unauthorized') {
|
|
101
|
-
debugCliLogin('Unauthorized to delete OAuth session');
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
debugCliLogin('Error deleting OAuth session: %O', error);
|
|
106
|
-
throw error;
|
|
107
|
-
});
|
|
108
|
-
// Delete OAuth authorizations matching this token
|
|
109
|
-
const authorizationsDeletion = client
|
|
110
|
-
.get('/oauth/authorizations')
|
|
111
|
-
.then(async (response) => {
|
|
112
|
-
const authorizations = (await response.json());
|
|
113
|
-
debugCliLogin('Found %d OAuth authorizations', authorizations.length);
|
|
114
|
-
// Don't delete the default token
|
|
115
|
-
const defaultToken = await getDefaultToken(client);
|
|
116
|
-
if (defaultToken === token) {
|
|
117
|
-
debugCliLogin('Token matches default authorization, skipping deletion');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const matchingAuths = authorizations.filter(a => a.access_token && a.access_token.token === token);
|
|
121
|
-
debugCliLogin('Found %d authorizations matching token', matchingAuths.length);
|
|
122
|
-
if (matchingAuths.length > 0) {
|
|
123
|
-
return Promise.all(matchingAuths.map(a => {
|
|
124
|
-
debugCliLogin('Deleting OAuth authorization: %s', a.id);
|
|
125
|
-
return client.delete(`/oauth/authorizations/${a.id}`);
|
|
126
|
-
}));
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
.catch(error => {
|
|
130
|
-
if (error instanceof Error) {
|
|
131
|
-
const errBody = error.body;
|
|
132
|
-
if (errBody?.id === 'unauthorized') {
|
|
133
|
-
debugCliLogin('Unauthorized to list/delete OAuth authorizations');
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
debugCliLogin('Error deleting OAuth authorizations: %O', error);
|
|
138
|
-
throw error;
|
|
139
|
-
});
|
|
140
|
-
await Promise.all([sessionDeletion, authorizationsDeletion]);
|
|
141
|
-
debugCliLogin('OAuth token deletion completed');
|
|
142
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSO (Single Sign-On) login flow
|
|
3
|
-
*/
|
|
4
|
-
import { prompt } from '@heroku/heroku-cli-util/hux';
|
|
5
|
-
import { debugCliLogin } from '../../debug-loggers.js';
|
|
6
|
-
import { headers } from './types.js';
|
|
7
|
-
/**
|
|
8
|
-
* Perform SSO login
|
|
9
|
-
*/
|
|
10
|
-
export async function ssoLogin(config) {
|
|
11
|
-
const open = (await import('open')).default;
|
|
12
|
-
let url = process.env.SSO_URL;
|
|
13
|
-
let org = process.env.HEROKU_ORGANIZATION;
|
|
14
|
-
if (!url) {
|
|
15
|
-
const orgName = await prompt('Organization name', {
|
|
16
|
-
default: org,
|
|
17
|
-
type: 'input',
|
|
18
|
-
});
|
|
19
|
-
org = orgName;
|
|
20
|
-
url = `https://sso.heroku.com/saml/${encodeURIComponent(org)}/init?cli=true`;
|
|
21
|
-
}
|
|
22
|
-
debugCliLogin(`opening browser to ${url}`);
|
|
23
|
-
console.error(`Opening browser to:\n${url}\n`);
|
|
24
|
-
console.error('\u001B[90mIf the browser fails to open or you\'re authenticating on a remote\nmachine, please manually open the URL above in your browser.\u001B[0m\n');
|
|
25
|
-
await open(url, { wait: false });
|
|
26
|
-
const password = await prompt('Access token', {
|
|
27
|
-
type: 'password',
|
|
28
|
-
});
|
|
29
|
-
console.error('Validating token...');
|
|
30
|
-
const response = await fetch(`${config.apiUrl}/account`, {
|
|
31
|
-
headers: headers(password),
|
|
32
|
-
});
|
|
33
|
-
if (!response.ok) {
|
|
34
|
-
throw new Error(`Failed to validate token: ${response.statusText}`);
|
|
35
|
-
}
|
|
36
|
-
const account = (await response.json());
|
|
37
|
-
return {
|
|
38
|
-
login: account.email,
|
|
39
|
-
password,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types and interfaces for login functionality
|
|
3
|
-
*/
|
|
4
|
-
export interface NetrcEntry {
|
|
5
|
-
login: string;
|
|
6
|
-
password: string;
|
|
7
|
-
}
|
|
8
|
-
export interface OAuthAuthorization {
|
|
9
|
-
access_token?: {
|
|
10
|
-
token: string;
|
|
11
|
-
};
|
|
12
|
-
id: string;
|
|
13
|
-
user?: {
|
|
14
|
-
email: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export interface Account {
|
|
18
|
-
email: string;
|
|
19
|
-
id: string;
|
|
20
|
-
}
|
|
21
|
-
export interface LoginOptions {
|
|
22
|
-
/** Browser to use for browser-based login */
|
|
23
|
-
browser?: string;
|
|
24
|
-
/** Token expiration time in seconds */
|
|
25
|
-
expiresIn?: number;
|
|
26
|
-
/** Login method to use */
|
|
27
|
-
method?: 'browser' | 'interactive' | 'sso';
|
|
28
|
-
}
|
|
29
|
-
export interface LoginConfig {
|
|
30
|
-
apiHost: string;
|
|
31
|
-
apiUrl: string;
|
|
32
|
-
httpGitHost: string;
|
|
33
|
-
loginHost: string;
|
|
34
|
-
}
|
|
35
|
-
/** Helper to create HTTP headers with bearer token */
|
|
36
|
-
export declare const headers: (token: string) => {
|
|
37
|
-
Accept: string;
|
|
38
|
-
Authorization: string;
|
|
39
|
-
};
|
|
40
|
-
/** Constants */
|
|
41
|
-
export declare const THIRTY_DAYS: number;
|
|
42
|
-
export declare const LOGIN_TIMEOUT: number;
|
package/dist/cli/login/types.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types and interfaces for login functionality
|
|
3
|
-
*/
|
|
4
|
-
/** Helper to create HTTP headers with bearer token */
|
|
5
|
-
export const headers = (token) => ({
|
|
6
|
-
Accept: 'application/vnd.heroku+json; version=3',
|
|
7
|
-
Authorization: `Bearer ${token}`,
|
|
8
|
-
});
|
|
9
|
-
/** Constants */
|
|
10
|
-
export const THIRTY_DAYS = 60 * 60 * 24 * 30;
|
|
11
|
-
export const LOGIN_TIMEOUT = 1000 * 60 * 10; // 10 minutes
|