Decoding JWT tokens can be a crucial part of debugging and understanding the authentication and authorization processes in your applications. Whether you’re working on a microservices architecture or a single-page application, being able to quickly inspect JWT tokens can save you a lot of time. In this post, I’ll walk you through how to decode JWT tokens from the command line using tools like base64 and jq.

The Problem

JWT tokens are compact, URL-safe means of representing claims to be transferred between two parties. They are commonly used for authentication and information exchange. However, JWT tokens are often encoded, making them unreadable. Decoding them manually can be cumbersome, especially if you need to do it frequently during development or debugging.

Understanding JWT Structure

Before diving into decoding, let’s briefly understand the structure of a JWT token. A JWT token consists of three parts separated by dots (.):

  1. Header: Metadata about the token type and the signing algorithm.
  2. Payload: Claims (statements about an entity) and additional data.
  3. Signature: Used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed along the way.

Here’s an example of a JWT token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Each part is Base64Url encoded. To decode a JWT, you need to decode each part separately.

Decoding JWT Tokens Using base64

The simplest way to decode a JWT token is by using the base64 command available in most Unix-like systems. Here’s how you can do it:

Step-by-Step Guide

Extract the Header

Use the following command to extract and decode the header:

Extract the Payload

Similarly, extract and decode the payload:

Ignore the Signature

The signature is used for verification and doesn’t need to be decoded manually.

Let’s break down each step with an example.

Example JWT Token

Consider the following JWT token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Extract and Decode the Header

  1. Split the token into its components.
  2. Decode the header using base64.
# Extract the header part
HEADER=$(echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | tr -d '\n')

# Decode the header
echo $HEADER | base64 --decode
Terminal
$ HEADER=$(echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | tr -d '\n') $ echo $HEADER | base64 --decode { "alg": "HS256", "typ": "JWT" }

Extract and Decode the Payload

  1. Extract the payload part.
  2. Decode the payload using base64.
# Extract the payload part
PAYLOAD=$(echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | tr -d '\n')

# Decode the payload
echo $PAYLOAD | base64 --decode
Terminal
$ PAYLOAD=$(echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | tr -d '\n') $ echo $PAYLOAD | base64 --decode { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

Handling Base64Url Encoding

JWT tokens use Base64Url encoding, which is similar to Base64 but uses - and _ instead of + and /, and does not include padding =. To handle this, you can replace - and _ with + and / respectively, and add padding if necessary.

# Function to decode Base64Url
decode_base64url() {
  local encoded="$1"
  # Replace URL-safe characters
  encoded="${encoded//_/+}"
  encoded="${encoded//-/$__}"
  # Add padding
  while (( ${#encoded} % 4 )); do
    encoded+="="
  done
  echo "$encoded" | base64 --decode
}

# Decode the header using the function
decode_base64url "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
Terminal
$ decode_base64url "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" { "alg": "HS256", "typ": "JWT" }

Decoding the Entire Token

You can create a script to decode the entire JWT token automatically.

#!/bin/bash

# Function to decode Base64Url
decode_base64url() {
  local encoded="$1"
  # Replace URL-safe characters
  encoded="${encoded//_/+}"
  encoded="${encoded//-/$__}"
  # Add padding
  while (( ${#encoded} % 4 )); do
    encoded+="="
  done
  echo "$encoded" | base64 --decode
}

# Check if a token is provided
if [ -z "$1" ]; then
  echo "Usage: $0 <jwt_token>"
  exit 1
fi

# Split the token into parts
IFS='.' read -r -a parts <<< "$1"

# Decode each part
for i in "${!parts[@]}"; do
  case $i in
    0) echo "Header: $(decode_base64url "${parts[$i]}")" ;;
    1) echo "Payload: $(decode_base64url "${parts[$i]}")" ;;
    2) echo "Signature: ${parts[$i]}" ;;
  esac
done
💜 Pro Tip: Save this script as `decode_jwt.sh` and make it executable with `chmod +x decode_jwt.sh`. You can then run it by passing a JWT token as an argument.

🎯 Key Takeaways

  • JWT tokens consist of three parts: Header, Payload, and Signature.
  • Use `base64` to decode the Header and Payload.
  • Handle Base64Url encoding by replacing `-` and `_` with `+` and `/`, and adding padding.

Decoding JWT Tokens Using jq

While base64 is sufficient for basic decoding, jq is a powerful tool for parsing JSON data. It can make the output more readable and easier to work with.

Installing jq

If you don’t have jq installed, you can install it using your package manager.

# On Ubuntu/Debian
sudo apt-get install jq

# On macOS
brew install jq

Decoding with jq

You can combine base64 and jq to decode and pretty-print the JWT token.

# Decode the header and pretty-print
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 --decode | jq .

# Decode the payload and pretty-print
echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | base64 --decode | jq .
Terminal
$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 --decode | jq . { "alg": "HS256", "typ": "JWT" } $ echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | base64 --decode | jq . { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

Automating with a Script

You can create a script to decode and pretty-print the entire JWT token using jq.

#!/bin/bash

# Function to decode Base64Url
decode_base64url() {
  local encoded="$1"
  # Replace URL-safe characters
  encoded="${encoded//_/+}"
  encoded="${encoded//-/$__}"
  # Add padding
  while (( ${#encoded} % 4 )); do
    encoded+="="
  done
  echo "$encoded" | base64 --decode
}

# Check if a token is provided
if [ -z "$1" ]; then
  echo "Usage: $0 <jwt_token>"
  exit 1
fi

# Split the token into parts
IFS='.' read -r -a parts <<< "$1"

# Decode each part and pretty-print with jq
for i in "${!parts[@]}"; do
  case $i in
    0) echo "Header: $(decode_base64url "${parts[$i]}" | jq .)" ;;
    1) echo "Payload: $(decode_base64url "${parts[$i]}" | jq .)" ;;
    2) echo "Signature: ${parts[$i]}" ;;
  esac
done
💜 Pro Tip: Save this script as `decode_jwt_jq.sh` and make it executable with `chmod +x decode_jwt_jq.sh`. You can then run it by passing a JWT token as an argument.

🎯 Key Takeaways

  • `jq` makes JSON output more readable and easier to work with.
  • Combine `base64` and `jq` for a powerful decoding solution.
  • Create scripts for automation and convenience.

Decoding JWT Tokens Without Installing Tools

Sometimes, you might not have the luxury of installing additional tools like jq. In such cases, you can use pure Bash or other built-in tools.

Using Pure Bash

Bash has limited capabilities for JSON parsing, but you can still decode and display the token parts.

#!/bin/bash

# Function to decode Base64Url
decode_base64url() {
  local encoded="$1"
  # Replace URL-safe characters
  encoded="${encoded//_/+}"
  encoded="${encoded//-/$__}"
  # Add padding
  while (( ${#encoded} % 4 )); do
    encoded+="="
  done
  echo "$encoded" | base64 --decode
}

# Check if a token is provided
if [ -z "$1" ]; then
  echo "Usage: $0 <jwt_token>"
  exit 1
fi

# Split the token into parts
IFS='.' read -r -a parts <<< "$1"

# Decode each part
for i in "${!parts[@]}"; do
  case $i in
    0) echo "Header: $(decode_base64url "${parts[$i]}")" ;;
    1) echo "Payload: $(decode_base64url "${parts[$i]}")" ;;
    2) echo "Signature: ${parts[$i]}" ;;
  esac
done
💜 Pro Tip: This script only decodes the token and doesn’t pretty-print the JSON. For readability, consider using `jq` if possible.

Using python

If you have Python installed, you can use it to decode JWT tokens.

#!/bin/bash

# Check if a token is provided
if [ -z "$1" ]; then
  echo "Usage: $0 <jwt_token>"
  exit 1
fi

# Decode the token using Python
python -c "import sys, base64, json; token = sys.argv[1].split('.'); print('Header:', json.dumps(json.loads(base64.b64decode(token[0] + '==' * (-len(token[0]) % 4))), indent=2)); print('Payload:', json.dumps(json.loads(base64.b64decode(token[1] + '==' * (-len(token[1]) % 4))), indent=2))" "$1"
💜 Pro Tip: This script uses Python to decode and pretty-print the JWT token. Ensure Python is installed on your system.

🎯 Key Takeaways

  • Use pure Bash for environments where no additional tools are available.
  • Leverage Python for decoding if it’s already installed.
  • Choose the method that best fits your environment.

Security Considerations

Decoding JWT tokens is generally safe, but there are some security considerations to keep in mind.

Don’t Modify JWT Tokens

JWT tokens are signed to ensure their integrity. Modifying the token will invalidate the signature, causing the token to be rejected by the server.

🚨 Security Alert: Never modify JWT tokens manually. This can lead to authentication failures or security vulnerabilities.

Keep Tokens Secure

JWT tokens should be treated as sensitive data. Avoid logging or storing them unnecessarily. Use secure storage solutions and follow best practices for handling sensitive information.

⚠️ Warning: Protect JWT tokens from unauthorized access. Use HTTPS and secure storage mechanisms.

Validate Tokens on the Server

Always validate JWT tokens on the server side. Relying solely on client-side validation can be insecure.

💡 Key Point: Server-side validation ensures that tokens are valid and haven’t been tampered with.

🎯 Key Takeaways

  • Never modify JWT tokens manually.
  • Keep JWT tokens secure and avoid unnecessary logging.
  • Validate tokens on the server side for security.

Comparison of Decoding Methods

Here’s a comparison of different methods for decoding JWT tokens:

MethodProsConsUse When
Pure BashNo additional dependenciesLimited JSON parsingMinimal environments
`base64` + `jq`Readable output, powerful JSON parsingRequires `jq` installationDevelopment and debugging
PythonFlexible, powerfulRequires Python installationPython is available

📋 Quick Reference

  • base64 --decode - Decode Base64 encoded strings.
  • jq - Parse and pretty-print JSON data.
  • python - Use Python for flexible decoding and parsing.

Conclusion

Decoding JWT tokens from the command line is a valuable skill for IAM engineers and developers. By using tools like base64 and jq, you can quickly inspect and understand the contents of JWT tokens. Remember to handle tokens securely and validate them on the server side. With these techniques, you can save time and improve your debugging process.

That’s it. Simple, secure, works. Happy coding!