Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active October 30, 2024 03:31
Show Gist options
  • Save thomasdarimont/46358bc8167fce059d83a1ebdb92b0e7 to your computer and use it in GitHub Desktop.
Save thomasdarimont/46358bc8167fce059d83a1ebdb92b0e7 to your computer and use it in GitHub Desktop.
Example for decoding a JWT Payload with your Shell (bash, zsh...)

Setup

Add this to your .profile, .bashrc, .zshrc...

decode_base64_url() {
  local len=$((${#1} % 4))
  local result="$1"
  if [ $len -eq 2 ]; then result="$1"'=='
  elif [ $len -eq 3 ]; then result="$1"'=' 
  fi
  echo "$result" | tr '_-' '/+' | openssl enc -d -base64
}

decode_jwt(){
   decode_base64_url $(echo -n $2 | cut -d "." -f $1) | jq .
}

# Decode JWT header
alias jwth="decode_jwt 1"

# Decode JWT Payload
alias jwtp="decode_jwt 2"

Usage

jwtp eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

Output

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
@a-magdy
Copy link

a-magdy commented Jul 5, 2020

Thanks for the helpful gist
I've adjusted it a bit

  • used base64 instead of openssl (so I don't have to install it on an alpine docker image)
  • changed the order of arguments in the decode_jwt method, expecting the jwt first, and defaulting the second argument to 2 (as we usually need to decode the body)
_decode_base64_url() {
  local len=$((${#1} % 4))
  local result="$1"
  if [ $len -eq 2 ]; then result="$1"'=='
  elif [ $len -eq 3 ]; then result="$1"'=' 
  fi
  echo "$result" | tr '_-' '/+' | base64 -d
}

# $1 => JWT to decode
# $2 => either 1 for header or 2 for body (default is 2)
decode_jwt() { _decode_base64_url $(echo -n $1 | cut -d "." -f ${2:-2}) | jq .; }

# decodes body (default behaviour)
decode_jwt $MY_JWT
# decodes header
decode_jwt $MY_JWT 1 

Link to the fork: https://gist.github.com/a-magdy/a771f1426043ab4b66b19cc9a652908b

@vithalreddy
Copy link

Using Jq

function jwt_decode(){
    jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$1"
}

Usage:

╰─ jwt_decode eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

@frittenlab
Copy link

@vithalreddy Perfect cheers :)

@christopheblin
Copy link

@vithalreddy beautiful, thanks !

@sumitjainn
Copy link

Solution from @vithalreddy works perfectly across platforms, thanks!

@tanduong
Copy link

Perfect. Thanks @vithalreddy.

@jrichardsz
Copy link

Awesome when people share their knowledge for free. Thanks men!

I will add your script to my jarvis assistant

https://github.com/jrichardsz/linux-commandline-assistant/tree/master/commands

@mortya
Copy link

mortya commented Jul 31, 2024

Thanks, @vithalreddy and @chriswhite199 ! Your answers are better than what I came up with on my own. Combining your two answers:

function jwt_decode(){
    jq -R 'split(".") | .[1] | @base64d | fromjson |
      # you can replace the "localtime" with "gmttime" if that makes sense
      if .exp then (.expStr = (.exp | localtime | strftime("%Y-%m-%dT%H:%M:%S %Z"))) else . end |
      if .iat then (.iatStr = (.iat | localtime | strftime("%Y-%m-%dT%H:%M:%S %Z"))) else . end |
    .' <<< "$1"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment