A small Bash CLI and function to decode and inspect JSON Web Tokens (JWT) directly in your terminal.
It separates the header and payload, pretty‑prints the JSON, inspects time claims (
iat,nbf,exp), and can verify HS256 signatures using a shared secret. Everything runs locally on your machine - no tokens are sent to external services.
-
Local and offline
- All parsing, decoding, and verification is done locally.
- No network requests; safer than pasting tokens into web-based debuggers.
-
Readable JWT breakdown
-
Decodes Base64Url-encoded header and payload.
-
Pretty‑prints JSON via
jq(or Python'sjson.toolas a fallback). -
Clear sections for:
=== JWT Header ====== JWT Payload ===
-
-
Time claim analysis
-
Extracts and inspects these standard claims (if present):
iat- Issued Atnbf- Not Beforeexp- Expiration Time
-
Prints human‑readable times and simple status information:
EXPIRED/VALIDforexpActive/Not active yetfornbf
-
Works with both GNU
date(Linux) and BSDdate(macOS).
-
-
Signature verification (HS256)
- Supports HS256 (
alg: "HS256") signature verification using OpenSSL. - Compares the token's signature with a locally computed HMAC‑SHA256.
- Shows
Status: VERIFIEDorStatus: INVALIDand prints expected vs actual signature.
- Supports HS256 (
-
Flexible secret input
-
Secret key can be provided in two ways:
-k <secret>CLI optionJWT_SECRETenvironment variable
-
-
Safe CLI function or standalone script
- Can be executed as a standalone script (
./jwt-inspect.sh ...). - Can be sourced into your shell to define the
jwt-inspectfunction without exiting the shell on error.
- Can be executed as a standalone script (
Tip
For sensitive secrets, prefer the JWT_SECRET environment variable over the -k flag,
to avoid leaking the secret into shell history.
- Bash
base64(coreutils)openssl- required for HS256 signature verification
Optional but strongly recommended:
jq- for pretty JSON formatting and colorspython3- used as a fallback JSON formatter (python3 -m json.tool)
The script is otherwise self‑contained and does not require any external JWT libraries.
Download jwt-inspect.sh or clone the repository that contains it.
chmod +x jwt-inspect.shYou can now run it directly:
./jwt-inspect.sh '<your-jwt-here>'To use jwt-inspect as a global command, move or symlink the script into a directory on your PATH:
mv jwt-inspect.sh ~/.local/bin/jwt-inspect
chmod +x ~/.local/bin/jwt-inspectThen you can call:
jwt-inspect '<your-jwt-here>'If you prefer jwt-inspect as a shell function (without starting a new process and without exiting the shell on errors), you can source the script:
source /path/to/jwt-inspect.sh
# now jwt-inspect is available as a function
jwt-inspect '<your-jwt-here>'When sourced, the script sets JWT_SCRIPT_SOURCED=true and jwt-inspect will use return instead of exit on errors.
jwt-inspect 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.invalid-signature'Example output:
=== JWT Header ===
{
"alg": "HS256",
"typ": "JWT"
}
=== JWT Payload ===
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
Issued At (iat):
2018-01-18 02:30:22 (247512306s ago)
Signature Check:
Status: INVALID
Expected: <calculated-signature>
Actual: <signature-from-token>
(Exact timestamps and signatures will differ.)
Provide the secret with -k:
jwt-inspect -k 'a-string-secret-at-least-256-bits-long' '<your-jwt-here>'Or use the JWT_SECRET environment variable:
export JWT_SECRET='a-string-secret-at-least-256-bits-long'
jwt-inspect '<your-jwt-here>'If the algorithm is HS256 and the signature is correct, you will see:
Signature Check:
Status: VERIFIED
The script defines a single user-facing entry point: the jwt-inspect function (also called when the script is executed as jwt-inspect.sh).
Basic syntax:
jwt-inspect [-k <secret>] <token>
echo '<token>' | jwt-inspect [-k <secret>]-
-k <secret>- Shared secret key for HS256 signature verification.- If omitted, the script checks the
JWT_SECRETenvironment variable.
- If omitted, the script checks the
-
-h,--help- Show usage information.
The token can be supplied:
-
As a positional argument:
jwt-inspect '<jwt-token-here>' -
Via stdin (for example, piping from another command or pasting):
echo '<jwt-token-here>' | jwt-inspect
Whitespace characters (spaces, tabs, newlines) are stripped from the token before parsing, so you can safely paste multi-line tokens.
Important
jwt-inspect expects a standard JWT with three dot-separated parts
(header.payload.signature). Tokens with fewer parts are treated as invalid and cause an error.
If the payload contains any of the standard numeric time claims, the script will decode them and print a brief status.
- Prints the UTC timestamp in
YYYY-MM-DD HH:MM:SSformat. - Shows how many seconds ago the token was issued.
-
Prints the UTC timestamp.
-
Compares it with the current time:
Active(green) -nbfis in the past.Not active yet(red) -nbfis in the future.
-
Prints the UTC timestamp.
-
Compares it with the current time:
EXPIRED(red) -expis in the past.VALID(green) - token is still within the validity window.
-
Also prints how many seconds ago it expired, or in how many seconds it will expire.
Note
Time comparisons use the local system clock (date +%s).
Ensure your system time is accurate if you rely on these checks.
The script currently supports HS256 (alg: "HS256") verification using OpenSSL:
- Concatenates
header_raw.payload_rawas the signing input. - Computes
HMAC-SHA256using the provided secret key. - Encodes the result in standard Base64, then converts to Base64Url.
- Compares the Base64Url string with the signature part from the token.
If openssl is missing, signature verification is skipped with a warning.
If the algorithm in the header is not HS256, verification is also skipped with a warning:
Signature Check:
Warning: Algorithm RS256 not supported (only HS256).
Warning
This script does not implement asymmetric algorithms (e.g., RS256) or key management. It is intended as a lightweight helper for HS256 tokens only.
-
Secrets in shell history
-
Passing a secret key via
-kmay cause it to be stored in shell history (~/.bash_history, etc.). -
For sensitive keys, prefer:
export JWT_SECRET='your-secret-here' jwt-inspect '<token>'
-
-
Local verification vs. online tools
jwt-inspectis safer than web-based JWT debuggers because tokens never leave your machine.- However, you are still responsible for keeping your secret keys safe and using the tool in trusted environments.
-
Parsing limitations
- Fallback JSON extraction without
jqis intentionally simple and only reliable for scalar values. For robust output, installingjqis strongly recommended.
- Fallback JSON extraction without
The script uses deterministic exit codes so you can integrate it into other tools and CI pipelines:
0- Success1(JWT_ERR_GENERAL) - General error2(JWT_ERR_USAGE) - Invalid usage or arguments3(JWT_ERR_INVALID_FORMAT) - Not a valid JWT structure4(JWT_ERR_SIG_MISMATCH) - Signature verification failed
When sourced, jwt-inspect returns these codes instead of exiting the shell.
if jwt-inspect "$TOKEN" >/dev/null; then
echo "Token is structurally valid"
else
echo "Token is invalid or expired (see output above)" >&2
fiexport JWT_SECRET='my-secret'
if ! jwt-inspect "$TOKEN" >/dev/null; then
echo "JWT failed verification" >&2
exit 1
fiThis project is licensed under the MIT License.
Important
This tool is intended for debugging and inspection. It does not replace a production-grade JWT validation library or framework. Always validate tokens using well-maintained libraries in your application stack.
Issues and pull requests are welcome.
If you propose new features (e.g., additional algorithms or output formats), please include concrete examples and a brief rationale
so jwt-inspect can remain small, understandable, and focused on its core job 🔏