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 (.):
- Header: Metadata about the token type and the signing algorithm.
- Payload: Claims (statements about an entity) and additional data.
- 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
- Split the token into its components.
- Decode the header using
base64.
# Extract the header part
HEADER=$(echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | tr -d '\n')
# Decode the header
echo $HEADER | base64 --decode
Extract and Decode the Payload
- Extract the payload part.
- Decode the payload using
base64.
# Extract the payload part
PAYLOAD=$(echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | tr -d '\n')
# Decode the payload
echo $PAYLOAD | base64 --decode
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"
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
🎯 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 .
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
🎯 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
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"
🎯 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.
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.
Validate Tokens on the Server
Always validate JWT tokens on the server side. Relying solely on client-side validation can be insecure.
🎯 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:
| Method | Pros | Cons | Use When |
|---|---|---|---|
| Pure Bash | No additional dependencies | Limited JSON parsing | Minimal environments |
| `base64` + `jq` | Readable output, powerful JSON parsing | Requires `jq` installation | Development and debugging |
| Python | Flexible, powerful | Requires Python installation | Python 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!