sitevision-cli 1.0.0-beta.11 → 1.0.0-beta.12
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.
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { TextInput } from './TextInput.js';
|
|
5
5
|
import { writeDevProperties } from '../utils/project-detection.js';
|
|
6
6
|
import { setDeployPassword, deleteDeployPassword, setOAuth2ClientSecret, } from '../utils/keychain.js';
|
|
7
|
-
import { DEFAULT_REDIRECT_PORT } from '../utils/oauth2-auth.js';
|
|
7
|
+
import { DEFAULT_REDIRECT_PORT, discoverOAuth2Config, } from '../utils/oauth2-auth.js';
|
|
8
8
|
const LABELS = {
|
|
9
9
|
domain: 'Domain',
|
|
10
10
|
siteName: 'Site Name',
|
|
@@ -50,8 +50,39 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
|
|
|
50
50
|
scopes: initialProperties?.oauth2?.scopes?.join(' ') ?? '',
|
|
51
51
|
clientSecret: '',
|
|
52
52
|
}));
|
|
53
|
+
const [discoveryNote, setDiscoveryNote] = useState('');
|
|
53
54
|
const method = properties.authMethod ?? 'basic';
|
|
54
55
|
const isOAuth = method === 'oauth2';
|
|
56
|
+
// When OAuth2 is chosen, auto-fill the endpoints from the site's OpenID
|
|
57
|
+
// configuration (unauthenticated) so the user doesn't type them in.
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (method !== 'oauth2' || !properties.domain)
|
|
60
|
+
return;
|
|
61
|
+
if (oauth.authorizationEndpoint && oauth.tokenEndpoint)
|
|
62
|
+
return;
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
setDiscoveryNote('Looking up OAuth2 endpoints…');
|
|
65
|
+
void discoverOAuth2Config(properties.domain, properties.useHTTPForDevDeploy).then(discovered => {
|
|
66
|
+
if (cancelled)
|
|
67
|
+
return;
|
|
68
|
+
if (discovered) {
|
|
69
|
+
setOauth(previous => ({
|
|
70
|
+
...previous,
|
|
71
|
+
authorizationEndpoint: previous.authorizationEndpoint || discovered.authorizationEndpoint,
|
|
72
|
+
tokenEndpoint: previous.tokenEndpoint || discovered.tokenEndpoint,
|
|
73
|
+
scopes: previous.scopes || (discovered.scopesSupported?.join(' ') ?? ''),
|
|
74
|
+
}));
|
|
75
|
+
setDiscoveryNote('Endpoints auto-filled from the site OpenID config.');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
setDiscoveryNote('Could not auto-discover endpoints — enter them below.');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return () => {
|
|
82
|
+
cancelled = true;
|
|
83
|
+
};
|
|
84
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
85
|
+
}, [method, properties.domain]);
|
|
55
86
|
const methodSteps = method === 'oauth2'
|
|
56
87
|
? [
|
|
57
88
|
'oauthClientId',
|
|
@@ -177,7 +208,7 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
|
|
|
177
208
|
return null;
|
|
178
209
|
}
|
|
179
210
|
};
|
|
180
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Setup Development Properties" }), _jsxs(Text, { children: [' ', "Step ", stepIndex + 1, " of ", steps.length, ":", ' ', currentStep ? LABELS[currentStep] : ''] })] }), isOAuth && (
|
|
211
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Setup Development Properties" }), _jsxs(Text, { children: [' ', "Step ", stepIndex + 1, " of ", steps.length, ":", ' ', currentStep ? LABELS[currentStep] : ''] })] }), isOAuth && (_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Whitelist this redirect URI on the OAuth2 client: http://127.0.0.1:", redirectPort, "/callback"] }), discoveryNote && _jsx(Text, { color: "yellow", children: discoveryNote })] })), _jsx(Box, { marginBottom: 1, children: steps.map((s, i) => (_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: i <= stepIndex ? 'green' : 'gray', children: i < stepIndex ? '✓' : i === stepIndex ? '●' : '○' }) }, s))) }), _jsx(Box, { borderStyle: "single", borderColor: "gray", padding: 1, children: renderInput() })] }));
|
|
181
212
|
}
|
|
182
213
|
function MethodSelect({ defaultValue, onSubmit, }) {
|
|
183
214
|
const [index, setIndex] = useState(() => Math.max(0, AUTH_METHODS.findIndex(m => m.value === defaultValue)));
|
|
@@ -6,6 +6,18 @@ export declare function createPkcePair(): {
|
|
|
6
6
|
verifier: string;
|
|
7
7
|
challenge: string;
|
|
8
8
|
};
|
|
9
|
+
export interface DiscoveredOAuth2 {
|
|
10
|
+
authorizationEndpoint: string;
|
|
11
|
+
tokenEndpoint: string;
|
|
12
|
+
scopesSupported?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fetch the site's OpenID configuration (unauthenticated) to auto-fill the
|
|
16
|
+
* authorization/token endpoints. Returns null if it isn't published (provider
|
|
17
|
+
* not enabled) or the response isn't a valid config, so callers fall back to
|
|
18
|
+
* manual entry.
|
|
19
|
+
*/
|
|
20
|
+
export declare function discoverOAuth2Config(domain: string, useHTTP?: boolean): Promise<DiscoveredOAuth2 | null>;
|
|
9
21
|
export declare function openBrowser(url: string): void;
|
|
10
22
|
/**
|
|
11
23
|
* Start an interactive OAuth2 login. Returns the authorize URL to open and a
|
|
@@ -44,6 +44,37 @@ async function postToken(config, params, secret) {
|
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
/** OpenID configuration path (published at the issuer root once the provider is saved). */
|
|
48
|
+
const DISCOVERY_PATH = '/.well-known/openid-configuration';
|
|
49
|
+
/**
|
|
50
|
+
* Fetch the site's OpenID configuration (unauthenticated) to auto-fill the
|
|
51
|
+
* authorization/token endpoints. Returns null if it isn't published (provider
|
|
52
|
+
* not enabled) or the response isn't a valid config, so callers fall back to
|
|
53
|
+
* manual entry.
|
|
54
|
+
*/
|
|
55
|
+
export async function discoverOAuth2Config(domain, useHTTP = false) {
|
|
56
|
+
if (!domain)
|
|
57
|
+
return null;
|
|
58
|
+
const protocol = useHTTP ? 'http' : 'https';
|
|
59
|
+
try {
|
|
60
|
+
const response = await makeRequest(`${protocol}://${domain}${DISCOVERY_PATH}`, { method: 'GET' });
|
|
61
|
+
if (response.statusCode !== 200)
|
|
62
|
+
return null;
|
|
63
|
+
const doc = JSON.parse(response.body.toString());
|
|
64
|
+
if (!doc.authorization_endpoint || !doc.token_endpoint)
|
|
65
|
+
return null;
|
|
66
|
+
return {
|
|
67
|
+
authorizationEndpoint: doc.authorization_endpoint,
|
|
68
|
+
tokenEndpoint: doc.token_endpoint,
|
|
69
|
+
scopesSupported: Array.isArray(doc.scopes_supported)
|
|
70
|
+
? doc.scopes_supported
|
|
71
|
+
: undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
47
78
|
export function openBrowser(url) {
|
|
48
79
|
const isWin = process.platform === 'win32';
|
|
49
80
|
const cmd = process.platform === 'darwin' ? 'open' : isWin ? 'cmd' : 'xdg-open';
|