-
-
Save stokito/f2d7ea0b300f14638a9063559384ec89 to your computer and use it in GitHub Desktop.
A shell (ash, dash, Bash) script to decode JWT token. Version ported to OpenWrt here https://gist.github.com/stokito/43afca84fc34d1d362bf210cd941a366
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Decode a JWT from stdin and verify it's signature with the JWT issuer public key | |
# Only RS256 keys are supported for signature check | |
# | |
# Put OAuth server public key in PEM format to /var/cache/oauth/$JWT_KID.key.pub.pem | |
# You must create the folder first | |
# $ sudo mkdir -p /var/cache/oauth/ | |
# To converted key from JWK to PEM use https://8gwifi.org/jwkconvertfunctions.jsp or https://keytool.online/ | |
# NOTE: For Google you can get the keys in PEM format via https://www.googleapis.com/oauth2/v1/certs | |
# Decode the keys with decodeURIComponent() | |
# TODO fetch public key automatically in https://jwt.io/ manner: | |
# get "kid" field from JWT header | |
# get "iss" field from JWT header which is the token issuer url e.g. https://accounts.google.com | |
# add /.well-known/openid-configuration and fetch OIDC discovery e.g. wget https://accounts.google.com/.well-known/openid-configuration | |
# from the OIDC discovery JSON take jwks_uri e.g. https://www.googleapis.com/oauth2/v3/certs | |
# in the JWKS find the public key (JWK) which signed the JWT | |
# convert the JWK to PEM format to make openssl happy | |
# store the fetched pub key into /var/cache/ and next time check it there first to avoid calls to jwks_uri | |
# HOW TO USE: | |
# $ chmod +x jwt-decode.sh | |
# Parse file: | |
# $ cat id_token.txt | ./jwt-decode.sh | |
# if signature check failed then error code will be non-zero | |
if [ -z $(command -v jq) ]; then | |
echo "This script will NOT work on your machine." | |
echo "Please install jq first: https://stedolan.github.io/jq/download/" | |
exit 1 | |
fi | |
base64_padding() | |
{ | |
local len=$(( ${#1} % 4 )) | |
local padded_b64='' | |
if [ ${len} = 2 ]; then | |
padded_b64="${1}==" | |
elif [ ${len} = 3 ]; then | |
padded_b64="${1}=" | |
else | |
padded_b64="${1}" | |
fi | |
echo -n "$padded_b64" | |
} | |
base64url_to_b64() | |
{ | |
base64_padding "${1}" | tr -- '-_' '+/' | |
} | |
# read the JWT from stdin and split by comma into three variables | |
IFS='.' read -r JWT_HEADER_B64URL JWT_PAYLOAD_B64URL JWT_SIGNATURE_B64URL | |
JWT_HEADER_B64=$(base64url_to_b64 ${JWT_HEADER_B64URL}) | |
JWT_PAYLOAD_B64=$(base64url_to_b64 ${JWT_PAYLOAD_B64URL}) | |
JWT_SIGNATURE_B64=$(base64url_to_b64 ${JWT_SIGNATURE_B64URL}) | |
JWT_HEADER=$(echo "${JWT_HEADER_B64}" | base64 -d) | |
JWT_PAYLOAD=$(echo "${JWT_PAYLOAD_B64}" | base64 -d) | |
echo "JWT Header:" | |
echo "${JWT_HEADER}" | jq | |
echo "JWT Payload:" | |
echo "${JWT_PAYLOAD}" | jq | |
echo "JWT Signature (Base 64 padded):" | |
echo "${JWT_SIGNATURE_B64}" | |
JWT_ALG=$(echo "$JWT_HEADER" | jq -r .alg) | |
JWT_KID=$(echo "$JWT_HEADER" | jq -r .kid) | |
JWT_TYP=$(echo "$JWT_HEADER" | jq -r .typ) | |
JWT_ISS=$(echo "$JWT_PAYLOAD" | jq -r .iss) | |
JWT_SUB=$(echo "$JWT_PAYLOAD" | jq -r .sub) | |
JWT_EMAIL=$(echo "$JWT_PAYLOAD" | jq -r .email) | |
JWT_IAT=$(echo "$JWT_PAYLOAD" | jq -r .iat) | |
echo "alg: $JWT_ALG kid: $JWT_KID" | |
echo "sub: $JWT_SUB email: $JWT_EMAIL iat: $JWT_IAT" | |
# verify signature | |
if [ ${JWT_ALG} = "RS256" ]; then | |
PUB_KEY_FILE="/var/tmp/oauth/$JWT_KID.key.pub.pem" | |
if [ ! -f $PUB_KEY_FILE ]; then | |
>&2 echo "No pub key $JWT_KID" | |
if [ $JWT_ISS = "https://accounts.google.com" ]; then | |
mkdir -p /var/tmp/oauth/ | |
# use old jwks_url which return certs in PEM format | |
OAUTH_CERTS_URL="https://www.googleapis.com/oauth2/v1/certs" | |
echo "Fetch it from $OAUTH_CERTS_URL" | |
wget $OAUTH_CERTS_URL -q -O /tmp/jwks.json | |
CERT_FILE="/tmp/$JWT_KID.crt" | |
jq -r ".$JWT_KID" /tmp/jwks.json > "$CERT_FILE" | |
rm /tmp/jwks.json | |
openssl x509 -pubkey -in "$CERT_FILE" -noout > "$PUB_KEY_FILE" | |
rm "$CERT_FILE" | |
else | |
exit 1 | |
fi | |
fi | |
SIG_FILE="/tmp/$JWT_SUB-$JWT_IAT.sig.dat" | |
echo -n "$JWT_SIGNATURE_B64" | base64 -d > ${SIG_FILE} | |
JWT_BODY=$(echo -n "$JWT_HEADER_B64URL.$JWT_PAYLOAD_B64URL") | |
echo -n "$JWT_BODY" | openssl dgst -sha256 -verify "${PUB_KEY_FILE}" -signature ${SIG_FILE} | |
JWT_SIG_VERIFIED=$? | |
rm ${SIG_FILE} | |
if [ ${JWT_SIG_VERIFIED} -ne 0 ]; then | |
>&2 echo "Bad Signature" | |
exit ${JWT_SIG_VERIFIED} | |
exit; | |
fi | |
else | |
>&2 echo "Error 3: Unsupported signature algorithm $JWT_ALG" | |
exit 3 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also related and may be useful:
https://willhaley.com/blog/generate-jwt-with-bash/
https://auth0.com/docs/tokens/guides/validate-jwts
https://www.fastly.com/build-on-fastly/solution-pattern/jwt
https://blog.liplex.de/generate-a-new-jwt-public-and-private-key/
https://stackoverflow.com/questions/40595895/how-can-i-generate-the-private-and-public-certificates-for-jwt-with-rs256-algori
https://superuser.com/questions/1419155/generating-jwt-rs256-signature-with-openssl?rq=1
https://stackoverflow.com/questions/55656854/how-to-transform-a-jwk-to-public-key-over-openssl
https://stackoverflow.com/questions/41264157/how-to-convert-a-public-key-from-a-jwk-into-pem-for-openssl
https://habr.com/ru/company/ispsystem/blog/354420/
https://github.com/emcrisostomo/jwt-cli
https://willhaley.com/blog/generate-jwt-with-bash/
https://www.jvt.me/posts/2019/06/13/pretty-printing-jwt-openssl/
Here is an example with verifying a signature
https://gist.github.com/rolandyoung/176dd310a6948e094be6
An example of base64 URL encode/decode
https://github.com/Moodstocks/moodstocks-api-clients/blob/master/bash/base64url.sh
OAuth playground
https://developers.google.com/oauthplayground/