Skip to content
23 changes: 12 additions & 11 deletions apps/cli/commands/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AUTH_EVENTS } from '@studio/common/lib/cli-events';
import { getAuthenticationUrl } from '@studio/common/lib/oauth';
import { readAuthToken, updateSharedConfig } from '@studio/common/lib/shared-config';
import { AuthCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions';
import { __, sprintf } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { getUserInfo } from 'cli/lib/api';
import { openBrowser } from 'cli/lib/browser';
import { emitCliEvent } from 'cli/lib/daemon-client';
Expand All @@ -30,26 +30,27 @@ export async function runCommand(): Promise< void > {
return;
}

logger.reportStart( LoggerAction.LOGIN, __( 'Opening browser for authentication…' ) );

const appLocale = await getAppLocale();
const authUrl = getAuthenticationUrl( appLocale, CLI_REDIRECT_URI );

logger.reportStart( LoggerAction.LOGIN, __( 'Opening browser for authentication…' ) );
try {
await openBrowser( authUrl );
logger.reportSuccess( __( 'Browser opened successfully' ) );
} catch ( error ) {
// If the browser fails to open, allow users to manually open the URL
const loggerError = new LoggerError(
sprintf( __( 'Failed to open browser. Please open the URL manually: %s' ), authUrl ),
error
logger.reportWarning(
__( "Couldn't open a browser automatically. Use the URL below instead." ) +
( error instanceof Error ? `: ${ error.message }` : '' )
);
logger.reportError( loggerError );
}

console.log(
__( 'Please complete authentication in your browser and paste the generated token here.' )
);
// Always surface the URL explicitly so users on remote/headless machines
// have a usable link to open on another device.
console.log( '' );
console.log( __( 'To authenticate, open this URL in a browser on any device:' ) );
console.log( authUrl );
console.log( '' );
console.log( __( 'After approving access, copy the generated token and paste it here.' ) );
console.log( '' );

let accessToken: Awaited< ReturnType< typeof input > >;
Expand Down
11 changes: 11 additions & 0 deletions apps/cli/commands/auth/tests/login.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@ describe( 'Auth Login Command', () => {
expect( mockReportError ).toHaveBeenCalledWith( expect.any( LoggerError ) );
} );

it( 'should print the URL even when the browser opens', async () => {
const logSpy = vi.spyOn( console, 'log' ).mockImplementation( () => undefined );

await runCommand();

expect( openBrowser ).toHaveBeenCalledWith( mockAuthUrl );
expect( logSpy ).toHaveBeenCalledWith( mockAuthUrl );

logSpy.mockRestore();
} );

it( 'should use provided locale', async () => {
vi.mocked( getAppLocale ).mockResolvedValue( 'fr' );

Expand Down