Skip to content

Instantly share code, notes, and snippets.

@angelo-v
Last active February 13, 2024 20:49
Show Gist options
  • Star 65 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • 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
@alexgottscha
Copy link

why not just cut -d"." -f1,2 <<< $1 | sed 's/\./\n/g' | base64 --decode | jq?

@lukaslihotzki
Copy link

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

@micklove
Copy link

micklove commented Mar 3, 2020

@lukaslihotzki, nice work :)

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

@philpennock
Copy link

JWTs use base64url encoding, which neither jq nor base64 (from GNU coreutils) can handle. So the above examples all work some of the time, not reliably.

@angelo-v
Copy link
Author

angelo-v commented Sep 2, 2020

Valid point @philpennock. Do you have a reliable solution?

@lukaslihotzki
Copy link

Just replace the characters? jq -R 'gsub("-";"+") | gsub("_";"/") | split(".") | .[1] | @base64d | fromjson'

@seva-ramin
Copy link

seva-ramin commented Sep 26, 2020

JWTs use base64url encoding, which neither jq nor base64 (from GNU coreutils) can handle. So the above examples all work some of the time, not reliably.

@philpennock is correct. Here is my solution in a shell script:

#!/bin/bash

# pad base64URL encoded to base64
paddit() {
  input=$1
  l=`echo -n $input | wc -c`
  while [ `expr $l % 4` -ne 0 ]
  do
    input="${input}="
    l=`echo -n $input | wc -c`
  done
  echo $input
}

# read and split the token and do some base64URL translation
read jwt
read h p s <<< $(echo $jwt | tr [-_] [+/] | sed 's/\./ /g')

h=`paddit $h`
p=`paddit $p`

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

@philpennock
Copy link

Oh, I never spoke up, sorry: I use shell around Perl because Perl had the easiest access to base64url when I last went looking, but I was impressed by the solution from @lukaslihotzki using gsub inside jq as being the solution with the fewest additional dependencies.

@eum602
Copy link

eum602 commented Oct 13, 2020

I was also trying to decode a JWT token. I could decode a JWT with:

for line in `echo $JWT | tr "." "\n"`; do echo $line | base64 --decode | jq  && echo;done

@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.

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