Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/cli/src/commands/auth/login/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ export default command({
spinner?.start("Waiting for browser authorization...");
if (!spinner) p.log.info("Waiting for browser authorization…");

const result = await login(opts.signal);
const result = await login({
signal: opts.signal,
onAuthUrl: (url) => {
p.log.info(
`If your browser doesn't open automatically, paste this URL:\n${url}`,
);
},
});

config.auth = {
accessToken: result.accessToken,
Expand Down
25 changes: 22 additions & 3 deletions packages/cli/src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,18 @@ export function getWebUrl(): string {
return env.SUPERSET_API_URL.replace("api.superset.sh", "app.superset.sh");
}

export async function login(signal: AbortSignal): Promise<LoginResult> {
export interface LoginOptions {
signal: AbortSignal;
/**
* Called once with the authorization URL just before the browser is
* launched. Useful on headless hosts where `xdg-open` isn't available —
* the caller can surface the URL so the user can paste it into a
* browser on a different machine.
*/
onAuthUrl?: (url: string) => void;
}

export async function login(options: LoginOptions): Promise<LoginResult> {
const apiUrl = env.SUPERSET_API_URL;
const webUrl = getWebUrl();

Expand All @@ -143,13 +154,21 @@ export async function login(signal: AbortSignal): Promise<LoginResult> {
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
authorizeUrl.searchParams.set("state", state);

await openBrowser(authorizeUrl.toString());
const url = authorizeUrl.toString();
try {
options.onAuthUrl?.(url);
} catch (err) {
// Don't leak the bound loopback server if the callback throws.
server.close();
throw err;
}
await openBrowser(url);

const code = await waitForCallback({
server,
port,
expectedState: state,
signal,
signal: options.signal,
timeoutMs: 5 * 60 * 1000,
});

Expand Down