I was exploring the possibility of getting the auth token in bash using certificate authentication. Here are two examples how to obtain access token, one for Microsoft Graph and the other one for Google APIs.
Both scripts are similar:
- create jwt payload/claims
- sign header +payload with private key of the certificate uploaded to Microsoft/Google
- post data to token endpoint
- acquire access_token
A bit of explanation
-
base64 -w 0 | tr '/+' '_-' | tr -d '='
- this is simple base64url encodingbase64 -w 0
means that data won't be broken in multiple lines,tr '/+' '_-'
is used to replace/
with_
and+
with-
, and finallytr -d '='
is used to remove=
which is standard base64 padding -
openssl dgst -sha256 -sign $key_file
is signing input with certificate key stored in the$key_file
-
date +%s
is current timestamp, and$(($ts + 600))
adds 10 minutes of token validity
Microsoft uses certificate fingerprint (thumbprint) in jwt header and it is a binary value encoded as base64url.
-
openssl x509 -fingerprint -noout -in $cert_file
will produce something likeSHA1 Fingerprint=AA:BB:CC:DD:EE...
after whichcut -d '=' -f 2
is used to get everything right of the=
sign (the fingerprint), which is then converted into binary usingxxd -r -p
and finally converted into base64url
It also uses "a Guid" for unique identifier of a jwt where cat /proc/sys/kernel/random/uuid
is used to create it.
In the end, jq
is used to extract auth_token
into the file or to display the error message in case authentication was not successful.
It is probably easier just to use vendor provided tools or libraries, but sometimes having a simple shell script is just good enough.
Microsoft Graph
#!/bin/bash
set -e
client_id='<replace_with_client_id>'
tenant_id='<replace_with_tenant_id>'
cert_file='my.crt' #certificate (used for fingerprint)
key_file='my.key' #certificate private key (for signing)
cert_hash=$(openssl x509 -fingerprint -noout -in $cert_file | cut -d '=' -f 2 | xxd -r -p | base64 -w 0| tr '/+' '_-' | tr -d '=')
jwt_header="{\"alg\":\"RS256\",\"typ\":\"JWT\",\"x5t\":\"$cert_hash\"}"
ts=$(date +%s)
part_aud="\"aud\":\"https://login.microsoftonline.com/$tenant_id/oauth2/v2.0/token\""
part_nbf="\"nbf\":$ts"
part_exp="\"exp\":$(($ts + 600))"
part_jti="\"jti\":\"$(cat /proc/sys/kernel/random/uuid)\""
part_iss="\"iss\":\"$client_id\""
part_sub="\"sub\":\"$client_id\""
jwt_payload="{$part_aud,$part_exp,$part_iss,$part_jti,$part_nbf,$part_sub}"
token_data="$(echo -n $jwt_header | base64 -w 0 | tr '/+' '_-' | tr -d '=').$(echo -n $jwt_payload | base64 -w 0 | tr '/+' '_-' | tr -d '=')"
signature=$(echo -n $token_data | openssl dgst -sha256 -sign $key_file | base64 -w 0 | tr '/+' '_-' | tr -d '=')
assertion="$token_data.$signature"
resp=$(curl -Ssl -X POST "https://login.microsoftonline.com/$tenant_id/oauth2/v2.0/token" \
--data-urlencode "client_id=$client_id" \
--data-urlencode 'scope=https://graph.microsoft.com/.default' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode "client_assertion=$assertion" \
--data-urlencode 'grant_type=client_credentials' \
)
if echo $resp | grep -q 'access_token'; then
echo $resp | jq -j '.access_token' >access_token
else
echo $resp | jq -r '.error + " error:\n" + .error_description'
exit 1
fi
Source:
Google APIs
#!/bin/bash
set -e
client_email='client@your-project-name.iam.gserviceaccount.com'
subject_email='subject@example.com' #user that will be impersonated
scopes='https://www.googleapis.com/auth/<scope1> https://www.googleapis.com/auth/<scope2>'
key_file='my.key' #certificate private key (for signing)
jwt_header="{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
ts=$(date +%s)
part_iss="\"iss\":\"$client_email\""
part_sub="\"sub\":\"$subject_email\""
part_scope="\"scope\":\"$scopes\""
part_aud="\"aud\":\"https://oauth2.googleapis.com/token\""
part_exp="\"exp\":\"$(($ts + 600))\""
part_iat="\"iat\":\"$ts\""
jwt_payload="{$part_iss,$part_sub,$part_scope,$part_aud,$part_exp,$part_iat}"
token_data="$(echo -n $jwt_header | base64 -w 0 | tr '/+' '_-' | tr -d '=').$(echo -n $jwt_payload | base64 -w 0 | tr '/+' '_-' | tr -d '=')"
signature=$(echo -n $token_data | openssl dgst -sha256 -sign $key_file | base64 -w 0 | tr '/+' '_-' | tr -d '=')
assertion="$token_data.$signature"
resp=$(curl -Ssl -X POST 'https://oauth2.googleapis.com/token' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
--data-urlencode "assertion=$assertion" \
)
if echo $resp | grep -q 'access_token'; then
echo $resp | jq -j '.access_token' >access_token
else
echo $resp | jq -r '.error + " error:\n" + .error_description'
exit 1
fi
Source:
https://developers.google.com/identity/protocols/oauth2/service-account#httprest
Top comments (0)