Inspect token
Decode a JWT structure without verifying its signature or claims.
Decode the header, payload, and signature segment of a JWT and report any suspicious structural patterns. Decoding does not verify trust — see /v1/validate/jwt for that.
The response always carries validated: false and a warning string to that effect. Use this endpoint for fixtures, logs, debugging, and pre-validation pattern detection. Never use the decoded payload as an authorization input.
Request
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | yes | The JWT to decode. Three base64url-encoded segments separated by dots. The leading Bearer prefix is not stripped server-side; trim it client-side. Minimum length 1. |
Response — 200
| Field | Type | Required | Description |
|---|---|---|---|
| decoded.header | object | yes | The decoded JWT header as a JSON object. |
| decoded.payload | object | yes | The decoded JWT payload as a JSON object. |
| decoded.signature_segment | string | yes | The raw signature segment (base64url, undecoded). Echoed for debugging only — verification belongs in /v1/validate/jwt. |
| validated | boolean | yes | Always false. This endpoint does not validate. |
| warning | string | yes | Human-readable disclaimer that decoded ≠ validated. Echoed unchanged on every response. |
| suspicious_warnings | string[] | yes | Codes detected by structural inspection. See Suspicious patterns below. |
{
"decoded": {
"header": {
"alg": "HS256",
"typ": "JWT",
"kid": "k1"
},
"payload": {
"sub": "usr_1",
"exp": 9999999999
},
"signature_segment": "KJ2lp..."
},
"validated": false,
"warning": "Token was decoded only. Decoded does not mean validated: signature, issuer, audience, and claim policy were not checked.",
"suspicious_warnings": []
} Suspicious patterns
suspicious_warnings returns codes for any of the following structural risks. Each one is documented in Errors with detection rules and remediation guidance.
| Code | Trigger |
|---|---|
| ALG_NONE | header.alg == "none" (exact, case-sensitive). |
| JKU_PRESENT | jku field present in header. Indicates a token whose key resolution would fetch from an arbitrary URL on naïve verifiers. |
| KID_MISSING | No kid in header. Operationally fragile during JWKS rotation. |
| EXP_MISSING | No exp in payload. Token has no expiry. |
Detection is key-presence based, not truthiness. A token whose exp claim is 0 is expired, not missing exp; it surfaces as TOKEN_EXPIRED from /v1/validate/jwt, not as a suspicious warning here.
Errors
| Status | Code | Cause |
|---|---|---|
| 400 | MALFORMED_TOKEN | Token is not a parseable JWT (wrong segment count, invalid base64url, non-JSON header/payload, header/payload not a JSON object). |
| 422 | — | Request body missing the token field, or token is empty. |
// 400
{
"error": {
"code": "MALFORMED_TOKEN",
"message": "expected 3 segments, got 1"
}
}
Examples
curl -sSf -X POST "$JWTSHIELD_URL/v1/inspect/token" \
-H "Authorization: Bearer $JWTSHIELD_KEY" \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfMSJ9.x"}'const KEY = process.env.JWTSHIELD_KEY;
const r = await fetch(`${BASE_URL}/v1/inspect/token`, {
method: "POST",
headers: {
"Authorization": `Bearer ${KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ token }),
});
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const body = await r.json();
console.log(body.decoded.header, body.suspicious_warnings);KEY = os.environ["JWTSHIELD_KEY"]
r = httpx.post(
f"{BASE_URL}/v1/inspect/token",
headers={"Authorization": f"Bearer {KEY}"},
json={"token": token},
timeout=10.0,
)
r.raise_for_status()
body = r.json()
print(body["decoded"]["header"], body["suspicious_warnings"])