It's one of those where you think it's either malice or lack of any practical testing by the development team.
Let's assume you have WSL/WSL2 installed and it's an Ubuntu (I'm saying this only because I didn't test with other ones but the behavior theoretically should be the same) distro. You've recently installed gcloud SDK and are ready to go. So you start with some init:
$ gcloud init
Welcome! This command will take you through the configuration of gcloud.
Your current configuration has been set to: [default]
You can skip diagnostics next time by using the following flag:
gcloud init --skip-diagnostics
Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.
Reachability Check passed.
Network diagnostic passed (1/1 checks passed).
You must log in to continue. Would you like to log in (Y/n)? y
But then you quickly end up with the following error message:
You are authorizing gcloud CLI without access to a web browser. Please run the following command on a machine with a web browser and copy its output back here. Make sure the installed gcloud version is 372.0.0 or newer.
gcloud auth login --remote-bootstrap="https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=REDACTED.apps.googleusercontent.com&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fsqlservice.login+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=REDACTED&access_type=offline&code_challenge=REDACTED&code_challenge_method=S256&token_usage=remote"
A quick Google search shows that a similar issue was happening with GitHub CLI utility. The solution is the following:
export BROWSER=wslview
sudo ln -s $(which wslview) /usr/local/bin/xdg-open
Without going into too much details, it relies on setting a special
And it doesn't work.
It looks like gcloud does some other magic to find out if the browser is present. We'll have to dig a little bit deeper to know what exactly. By inspecting
$ grep -R "without access to a web browser" .
grep: ./lib/googlecloudsdk/core/credentials/__pycache__/flow.cpython-39.pyc: binary file matches
Hmm, a binary file match. Most likely the original code doesn't have this exact string on the same line, however, the precompiled Python version does since the string is present there already concatenated. Anyway, we know that we need to look for a file called flow.py in directory
In that file we quickly find a class called
...
class NoBrowserFlow(InstalledAppFlow):
"""Flow to authorize gcloud on a machine without access to web browsers.
Out-of-band flow (OobFlow) is deprecated. This flow together with the helper
flow NoBrowserHelperFlow is the replacement. gcloud in
environments without access to browsers (i.e. access via ssh) can use this
flow to authorize gcloud. This flow will print authorization parameters
which will be taken by the helper flow to build the final authorization
request. The helper flow (run by a gcloud instance
with access to browsers) will launch the browser and ask for user's
authorization. After the authorization, the helper flow will print the
authorization response to pass back to this flow to continue the process
(exchanging for the refresh/access tokens).
"""
...
Let's see where it's used:
$ grep -R NoBrowserFlow .
grep: ./lib/googlecloudsdk/api_lib/auth/__pycache__/util.cpython-39.pyc: binary file matches
./lib/googlecloudsdk/api_lib/auth/util.py:class NoBrowserFlowRunner(FlowRunner):
./lib/googlecloudsdk/api_lib/auth/util.py: """A flow runner to run NoBrowserFlow."""
./lib/googlecloudsdk/api_lib/auth/util.py: return c_flow.NoBrowserFlow.from_client_config(
./lib/googlecloudsdk/api_lib/auth/util.py: return c_flow.NoBrowserFlow.from_client_config(
./lib/googlecloudsdk/api_lib/auth/util.py: user_creds = NoBrowserFlowRunner(scopes, client_config).Run()
./lib/googlecloudsdk/api_lib/auth/util.py: user_creds = NoBrowserFlowRunner(scopes, client_config).Run()
OK, we have a
python -m webbrowser -t "https://www.python.org"
However, before that they're also checking if the currently running platform is Linux-based and if at least one of these environment variables are set:
_DISPLAY_VARIABLES = ['DISPLAY', 'WAYLAND_DISPLAY', 'MIR_SOCKET']
As you probably have already guessed, none of them are set in WSL by default because there's no window server running by default either. There is a thing called WSLg with support for this scenario but it's not installed by default. This results in a silly case where you have all the bits ready and waiting to be executed but the logic of the code itself isn't sophisticated enough to detect environments such as WSL.
The actual workaround is very simple. You need to short-circuit the environment variable check with:
export DISPLAY=foo
After this gcloud will happily report that the OAuth link has been opened in my browser:
$ gcloud init
Welcome! This command will take you through the configuration of gcloud.
Your current configuration has been set to: [default]
You can skip diagnostics next time by using the following flag:
gcloud init --skip-diagnostics
Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.
Reachability Check passed.
Network diagnostic passed (1/1 checks passed).
You must log in to continue. Would you like to log in (Y/n)? y
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=REDACTED.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8085%2F&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fsqlservice.login+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=REDACTED&access_type=offline&code_challenge=REDACTED&code_challenge_method=S256
You should be good to go after that. I probably need to file a bug report about this...
Update (2022-08-10): The bug report already exists and even contains the same workaround as described in this post :-)