For four years I thought kubectl required gcloud auth login. But I also needed ADC for scripts and apps, so every time my tokens expired I’d authenticate twice - once for the CLI, then again with gcloud auth application-default login. Two browser windows, two OAuth flows, same Google account.

Yes, gcloud auth login --update-adc exists, but it doesn’t let you specify scopes. I needed spreadsheets access for some scripts.

Turns out you can make kubectl use ADC directly. One auth with custom scopes, everything works.

The problem Link to heading

By default, GKE’s auth plugin uses gcloud CLI credentials:

# This is what kubeconfig normally uses
gke-gcloud-auth-plugin

But I wanted everything to use ADC so I only need to run gcloud auth application-default login once.

The fix Link to heading

The gke-gcloud-auth-plugin has a hidden flag:

gke-gcloud-auth-plugin --use_application_default_credentials

To make kubectl use this, update your kubeconfig. Find the exec sections and add the flag:

users:
- name: gke_project_zone_cluster
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: gke-gcloud-auth-plugin
      args:
        - --use_application_default_credentials  # Add this
      provideClusterInfo: true

Or patch it with sed:

sed -i '' 's/args: null/args:\n      - --use_application_default_credentials/g' ~/.kube/config

Now kubectl authenticates via ADC instead of gcloud CLI credentials.

A helper function Link to heading

I wrapped this into a glogin shell function with a few useful flags:

# Usage: glogin [-f] [-q] [-c] [-s]
glogin() {
  local force=false
  local quiet=false
  local cli=false
  local scopes=false

  while getopts ":fqcs" opt; do
    case ${opt} in
      f) force=true ;;
      q) quiet=true ;;
      c) cli=true ;;
      s) scopes=true ;;
    esac
  done

  # Show current scopes
  if [[ $scopes == true ]]; then
    local token=$(gcloud auth application-default print-access-token 2>/dev/null)
    if [[ -n "$token" ]]; then
      curl -s "https://oauth2.googleapis.com/tokeninfo?access_token=$token" | jq -r '.scope // "No scopes found"'
    else
      echo "No valid ADC token"
    fi
    return $?
  fi

  # CLI auth (for gcloud commands)
  if [[ $cli == true ]]; then
    gcloud auth login
    return $?
  fi

  # ADC auth - check if needed
  local adc_file="$HOME/.config/gcloud/application_default_credentials.json"
  local needs_login=false

  if [[ $force == true ]]; then
    needs_login=true
  elif [[ ! -f "$adc_file" ]]; then
    needs_login=true
  elif ! gcloud auth application-default print-access-token &>/dev/null; then
    needs_login=true
  fi

  if [[ $needs_login == true ]]; then
    [[ $quiet == false ]] && echo "Logging into Google Cloud..."
    gcloud auth application-default login \
      --scopes=openid,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/spreadsheets \
      ${quiet:+--quiet}
  else
    [[ $quiet == false ]] && echo "GCP ADC is valid."
  fi
}

Usage:

glogin      # Check ADC, login if needed
glogin -f   # Force re-auth
glogin -q   # Quiet mode (for scripts/startup)
glogin -c   # CLI auth (for gcloud commands)
glogin -s   # Show current scopes

The function validates the token before prompting - it only re-auths when the refresh token is actually invalid. I also include the spreadsheets scope since I occasionally need that for scripts.

Auto-check on shell startup Link to heading

Add this to your .profile or .zshrc to automatically check and refresh ADC in the background:

# Skip in non-interactive environments (e.g. Claude Code)
if [[ -z "$CLAUDECODE" ]]; then
  (glogin -q &)
fi

This runs the check in a subshell so it doesn’t block your terminal. If ADC is expired, the browser will open for auth.

One browser auth, and both kubectl and my apps work.

Gotcha: gcloud CLI still needs separate auth Link to heading

The gcloud CLI itself (gcloud compute, gcloud container, etc.) stubbornly refuses to use ADC. If you need those commands, you’ll still need gcloud auth login. But honestly, I rarely use the gcloud CLI directly - kubectl and Terraform cover most of my needs.

Further reading Link to heading