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.