Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-15 08:35:32

0001 #!/usr/bin/env bash
0002 # get_panda_token.sh
0003 # Replicates panda-client's device authorization flow to get an id_token.
0004 # Usage: source get_panda_token.sh
0005 
0006 PANDA_SERVER=${PANDA_SERVER:-"https://pandaserver.cern.ch:25443"}
0007 VO=${VO:-"atlas"}
0008 TOKEN_FILE=${TOKEN_FILE:-"${HOME}/.panda_id_token"}
0009 
0010 echo "==> Fetching auth config from ${PANDA_SERVER}/auth/${VO}_auth_config.json"
0011 # -k disables TLS certificate verification; acceptable for a developer helper script
0012 AUTH_CONFIG=$(curl -sk "${PANDA_SERVER}/auth/${VO}_auth_config.json")
0013 if [ -z "$AUTH_CONFIG" ]; then
0014     echo "ERROR: Failed to fetch auth config"
0015     return 1 2>/dev/null || exit 1
0016 fi
0017 
0018 # Parse the fields from the auth config
0019 read -r CLIENT_ID CLIENT_SECRET AUDIENCE OIDC_CONFIG_URL < <(
0020     python3 -c "
0021 import sys, json
0022 d = json.load(sys.stdin)
0023 print(d['client_id'], d.get('client_secret') or '', d['audience'], d['oidc_config_url'])
0024 " <<< "$AUTH_CONFIG")
0025 
0026 echo "==> client_id: $CLIENT_ID"
0027 echo "==> audience:  $AUDIENCE"
0028 
0029 # Fetch OIDC endpoints
0030 OIDC_CONFIG=$(curl -sk "$OIDC_CONFIG_URL")
0031 read -r DEVICE_ENDPOINT TOKEN_ENDPOINT < <(
0032     python3 -c "
0033 import sys, json
0034 d = json.load(sys.stdin)
0035 print(d['device_authorization_endpoint'], d['token_endpoint'])
0036 " <<< "$OIDC_CONFIG")
0037 
0038 echo "==> Requesting device code..."
0039 DEVICE_RESPONSE=$(curl -sk -X POST "$DEVICE_ENDPOINT" \
0040     -H "Content-Type: application/x-www-form-urlencoded" \
0041     -d "client_id=${CLIENT_ID}&scope=openid profile email offline_access&audience=${AUDIENCE}")
0042 
0043 # Parse device response
0044 # device_code: code to include in token request
0045 # verification_uri_complete: URL to open in browser for user authentication
0046 # expires_in: time in seconds before device code expires
0047 # Interval: polling interval in seconds
0048 read -r DEVICE_CODE VERIFICATION_URI EXPIRES_IN INTERVAL < <(
0049     python3 -c "
0050 import sys, json
0051 d = json.load(sys.stdin)
0052 print(d['device_code'], d['verification_uri_complete'], d['expires_in'], d.get('interval', 5))
0053 " <<< "$DEVICE_RESPONSE")
0054 
0055 if ! [[ "$EXPIRES_IN" =~ ^[0-9]+$ ]] || ! [[ "$INTERVAL" =~ ^[0-9]+$ ]]; then
0056     echo "ERROR: Invalid expires_in or interval in device response"
0057     return 1 2>/dev/null || exit 1
0058 fi
0059 
0060 echo ""
0061 echo "==> Please open the following URL in your browser and sign in:"
0062 echo ""
0063 echo "    $VERIFICATION_URI"
0064 echo ""
0065 read -rp "Press Enter once you have signed in..."
0066 
0067 # Poll for token
0068 echo "==> Polling for token..."
0069 ELAPSED=0
0070 while [ "$ELAPSED" -lt "$EXPIRES_IN" ]; do
0071     # client_secret is empty for public clients; included here for confidential client support
0072     TOKEN_RESPONSE=$(curl -sk -X POST "$TOKEN_ENDPOINT" \
0073         -H "Content-Type: application/x-www-form-urlencoded" \
0074         -d "client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=${DEVICE_CODE}")
0075 
0076     ERROR=$(echo "$TOKEN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error',''))" 2>/dev/null)
0077 
0078     if [ "$ERROR" == "authorization_pending" ]; then
0079         # RFC 8628 requires waiting at least interval seconds between polls; +1 adds a small buffer
0080         sleep $((INTERVAL + 1))
0081         ELAPSED=$((ELAPSED + INTERVAL + 1))
0082         continue
0083     elif [ -z "$ERROR" ]; then
0084         read -r ID_TOKEN TOKEN_EXPIRES_IN < <(
0085             python3 -c "
0086 import sys, json
0087 d = json.load(sys.stdin)
0088 print(d['id_token'], d.get('expires_in', ''))
0089 " <<< "$TOKEN_RESPONSE")
0090         echo "$TOKEN_RESPONSE" > "$TOKEN_FILE"
0091         EXPIRY_STR=$(date -d "+${TOKEN_EXPIRES_IN} seconds" 2>/dev/null || date -v "+${TOKEN_EXPIRES_IN}S" 2>/dev/null || echo "unknown")
0092         echo ""
0093         echo "==> Token saved to $TOKEN_FILE"
0094         echo "==> Token expires at: $EXPIRY_STR"
0095         break
0096     else
0097         echo "ERROR: $TOKEN_RESPONSE"
0098         return 1 2>/dev/null || exit 1
0099     fi
0100 done
0101 
0102 if [ -z "$ID_TOKEN" ]; then
0103     echo "ERROR: Timed out waiting for authentication"
0104     return 1 2>/dev/null || exit 1
0105 fi
0106 
0107 echo "==> Token generated successfully!"
0108 
0109 # Executed as source get_panda_token.sh: export token to current shell
0110 if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
0111     # id_token is exported as ACCESS_TOKEN to match the name panda-client tools expect
0112     export ACCESS_TOKEN=$ID_TOKEN
0113     # TOKEN_FILE exported so callers can extract other fields (e.g. refresh_token) without hardcoding the path
0114     export TOKEN_FILE
0115     echo "==> ACCESS_TOKEN exported to current shell (full response in \$TOKEN_FILE: $TOKEN_FILE)"
0116 # Executed as ./get_panda_token.sh: print instructions to export token manually since we can't modify the parent shell's environment
0117 else
0118     echo ""
0119     echo "Run the following to set your token:"
0120     echo ""
0121     echo "  export ACCESS_TOKEN=$ID_TOKEN"
0122     echo "  export TOKEN_FILE=$TOKEN_FILE"
0123 fi