-
-
Save angelo-v/e0208a18d455e2e6ea3c40ad637aac53 to your computer and use it in GitHub Desktop.
# 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 |
Valid point @philpennock. Do you have a reliable solution?
Just replace the characters? jq -R 'gsub("-";"+") | gsub("_";"/") | split(".") | .[1] | @base64d | fromjson'
JWTs use base64url encoding, which neither
jq
norbase64
(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
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.
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
@seva-ramin's script is fantastic, but just a small bug: tr [+_] [-/]
should be tr [-_] [+/]
@seva-ramin's script is fantastic, but just a small bug:
tr [+_] [-/]
should betr [-_] [+/]
Ugh! what a silly mistake! @danmactough, Thank you for catching that. I have updated my post.
@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 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.
I additionally had to remove empty parts but then it worked perfectly fine
jq -R 'split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson' <<< $1
With just jq:
jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"
@lukaslihotzki thanks, very useful 👍
With just jq:
jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"
Thanks @lukaslihotzki, very useful!
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
With just jq:
jq -R 'split(".") | .[1] | @base64d | fromjson' <<< "$JWT"
Excellent solution, thanks @lukaslihotzki
What about echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" | cut -d '.' -f 2 | base64 -d
what about basenc
it's part of coreutils ?
basenc -d --base64url -i <your_file> | jq
Sure; coreutils 8.31 and newer, so was not in stable OS releases at the time of the gist. Today, I'd recommend basenc.
Oh, beware though that basenc
complains about missing =
signs, even in --base64url
mode, so you'll also need to suppress stderr.
What about echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" | cut -d '.' -f 2 | base64 -d
@indian0ch thanks 👍🏻
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
).
@jpbochi Thanks for your script! Why don't you include the tr -- '-_' '+/'
step? openssl needs it right? (e.g. openssl/openssl#17559)
@rickgm I ended up settling for python3 -m base64 -d
as a replacement for openssl base64 -d -A
. It's more robust, supporting both base64 and base64url modes.
JWTs use base64url encoding, which neither
jq
norbase64
(from GNU coreutils) can handle. So the above examples all work some of the time, not reliably.