Skip to content

Instantly share code, notes, and snippets.

@angelo-v
Last active October 21, 2024 13:19
Show Gist options
  • Save angelo-v/e0208a18d455e2e6ea3c40ad637aac53 to your computer and use it in GitHub Desktop.
Save angelo-v/e0208a18d455e2e6ea3c40ad637aac53 to your computer and use it in GitHub Desktop.
Decode a JWT via command line
# will not work in all cases, see https://gist.github.com/angelo-v/e0208a18d455e2e6ea3c40ad637aac53#gistcomment-3439904
function jwt-decode() {
sed 's/\./\n/g' <<< $(cut -d. -f1,2 <<< $1) | base64 --decode | jq
}
JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
jwt-decode $JWT
@danmactough
Copy link

@seva-ramin's script is fantastic, but just a small bug: tr [+_] [-/] should be tr [-_] [+/]

@seva-ramin
Copy link

@seva-ramin's script is fantastic, but just a small bug: tr [+_] [-/] should be tr [-_] [+/]

Ugh! what a silly mistake! @danmactough, Thank you for catching that. I have updated my post.

@genghisjahn
Copy link

genghisjahn commented May 7, 2021

@seva-ramin for me, on OSX Mojave I get base64: invalid option -- d
Changing to -D works for last two lines:

# assuming we have jq installed
echo $h | base64 -D | jq
echo $p | base64 -D | jq

@seva-ramin
Copy link

seva-ramin commented May 7, 2021

@seva-ramin for me, on OSX Mojave I get base64: invalid option -- d
Changing to -D works for last two lines:

# assuming we have jq installed
echo $h | base64 -D | jq
echo $p | base64 -D | jq

Darn it. They broke base64 in Mojave? I am on Catalina and both options work. Here is the man page for base64 on Catalina.

image

@dbubenheim
Copy link

I additionally had to remove empty parts but then it worked perfectly fine

jq -R 'split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson' <<< $1

@subratamazumder
Copy link

With just jq: jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"

@lukaslihotzki thanks, very useful 👍

@suhlig
Copy link

suhlig commented Jul 5, 2022

With just jq: jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"

Thanks @lukaslihotzki, very useful!

@FlorinTP
Copy link

FlorinTP commented Jul 7, 2022

Or an universal GO approach using RawStdEncoding (with temporary file):

cat << EOFT > ./temp.go &&  go run ./temp.go $JWT |jq  '.|select(.type=="wrapping")' ; rm ./temp.go
package main
import (
        "encoding/base64"
        "strings"
        "fmt"
        "os"
)
var  encoded = os.Args[1]
func main() {
split := strings.Split(encoded, ".")
for i := 0; i < len(split); i++ {
        tokenBytes, err := base64.RawStdEncoding.DecodeString(split[i])
        if err != nil {
          return
        }
        var sToken=string(tokenBytes)
        fmt.Printf("%s",sToken)
    }
}
EOFT

@gustavoromerobenitez
Copy link

With just jq: jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"

Excellent solution, thanks @lukaslihotzki

@indian0ch
Copy link

What about echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" | cut -d '.' -f 2 | base64 -d

@kloverde25
Copy link

what about basenc it's part of coreutils ?

basenc -d --base64url -i <your_file> | jq

@philpennock
Copy link

Sure; coreutils 8.31 and newer, so was not in stable OS releases at the time of the gist. Today, I'd recommend basenc.

@philpennock
Copy link

Oh, beware though that basenc complains about missing = signs, even in --base64url mode, so you'll also need to suppress stderr.

@mvillafuertem
Copy link

What about echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" | cut -d '.' -f 2 | base64 -d

@indian0ch thanks 👍🏻

@jpbochi
Copy link

jpbochi commented Jul 12, 2024

To get around the broken/unreliable @base64d from jq, I got this solution:

jwtd () {
  local input="${1:-}"
  if [ -z "$input" ]; then
    if [ ! -t 0 ]; then
      input=$(cat /dev/stdin)
    else
      echo >&2 '✗ Need an argument or have a piped input!'
      return 1
    fi
  fi
  echo "$input" \
    | jq -Rrce 'split(".")[1] | . + "=" * (. | 4 - length % 4)' \
    | openssl base64 -d -A \
    | jq .
}

It will append the = padding as needed, then pipe into openssl base64 -d -A, which I found to be more reliable and cross-platform than base64. I tested this both on Ubuntu and MacOS.

The bash function accepts either a direct param or piped input (e.g., echo 'base64…==' | jwtd).

@rickgm
Copy link

rickgm commented Oct 2, 2024

@jpbochi Thanks for your script! Why don't you include the tr -- '-_' '+/' step? openssl needs it right? (e.g. openssl/openssl#17559)

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