v0.0.1 · system status
Open APIOpenAPI 3.1Zero token storage

Errors

Every error code, what triggers it, and what to do about it.

JWTShield reports problems through three distinct channels. Read this section before wiring error handling.

How errors are surfaced
  • Token-shape errors return HTTP 400 with body {"error": {"code": "...", "message": "..."}}. The token cannot be parsed at all.
  • Validation errors return HTTP 200 with body {"valid": false, "findings": [...], ...}. The token parsed cleanly but failed one or more checks. Always check valid before trusting the response.
  • Request-shape errors return HTTP 422 with the FastAPI standard validation envelope. Your request body is malformed.

Token-shape errors (HTTP 400)

MALFORMED_TOKEN

Returned by every endpoint that accepts a token when the value cannot be parsed as a three-segment, base64url-encoded JWT containing JSON header and payload.

TriggerExample message
Wrong segment countexpected 3 segments, got 1
Invalid base64urlinvalid base64url segment: Invalid base64-encoded string
Header/payload not JSONheader is not valid JSON: Expecting value
Header/payload not a JSON objectpayload is not a JSON object
Empty token stringtoken is empty

Fix: ensure the token is the value of a JWT, not the entire Authorization: Bearer ... header. Strip any whitespace.

Validation findings (HTTP 200, valid: false)

These appear in the findings[] array of /v1/validate/jwt and any endpoint that performs full validation.

SIGNATURE_INVALID

The signature does not verify against the resolved key. For HS* algorithms the policy secret is wrong; for RS/ES/EdDSA the resolved JWKS key does not match the signature.

ALGORITHM_INVALID

The token’s alg header is not in policy.allowed_algs. JWTShield refuses signature work for any algorithm outside the allowlist; this is the primary defence against alg=none and key-confusion attacks.

ISSUER_MISMATCH

The token’s iss claim does not equal policy.issuer exactly. Comparison is case-sensitive and trailing slashes matter.

AUDIENCE_MISMATCH

The token’s aud claim is not in policy.audiences. Both single-string and array aud claims are supported; at least one entry must match.

TOKEN_EXPIRED

Current time is past exp. Honours policy.clock_skew_seconds.

TOKEN_NOT_YET_VALID

Current time is before nbf. Honours policy.clock_skew_seconds.

REQUIRED_CLAIM_MISSING

A claim listed in policy.required_claims is absent from the payload. The evidence object names which claim.

PROFILE_NOT_FOUND

issuer_profile_id was supplied but no profile with that ID is registered. Check the ISSUER_PROFILES_JSON environment variable on the JWTShield service.

Discovery findings (HTTP 200, in findings[])

Returned by /v1/providers/discover.

DISCOVERY_FAILED

{issuer}/.well-known/openid-configuration did not return a 2xx response, or returned non-JSON. The evidence object includes the HTTP status and body excerpt.

JWKS_UNREACHABLE

The JWKS endpoint advertised in the discovery document timed out or returned a non-2xx response. The discovery jwks_reachability field also reflects this.

INVALID_METADATA

The discovery document is JSON but does not match the OpenID Connect Discovery 1.0 schema (e.g. issuer field missing, jwks_uri not a URL).

Config-lint findings (HTTP 200, in findings[])

Returned by /v1/lint/oidc-config.

WEAK_ALGORITHM

alg_policy.allowed_algs contains an algorithm weaker than RS256/ES256/EdDSA. Refusing weak algorithms reduces the attack surface even when stronger ones are also allowed.

INSECURE_URI

A URI in jwks_uri or redirect_uris uses http:// while https_required is true.

JWKS_URI_MISMATCH

The jwks_uri you sent does not match discovered_jwks_uri from the issuer’s /.well-known/openid-configuration. Either the upstream provider rotated their JWKS endpoint or your config drifted.

CI-token findings (HTTP 200, in findings[])

Returned by /v1/validate/ci-oidc in addition to the standard validation findings above.

GITHUB_REPO_MISMATCH

The token’s repository claim does not match expected_repository. Common cause: a fork or unintended branch triggered the workflow.

GITHUB_REF_MISMATCH

The token’s ref claim does not match expected_ref. Use this to refuse tokens issued by feature branches when production deploys must come from refs/heads/main.

GITLAB_PROJECT_MISMATCH

The token’s project_path claim does not match expected_project_path. Mirrors the GitHub repo check.

GITLAB_REF_PROTECTION_MISMATCH

The token’s ref_protected claim does not match expected_ref_protected. Refuse OIDC-issued tokens from unprotected branches if your deploy gate requires protection.

Rotation findings (HTTP 200, in findings[])

Returned by /v1/validate/jwks-rotation.

NO_KEY_OVERLAP

current_jwks shares no keys (by kid) with previous_jwks. In-flight tokens issued under the previous key set will fail verification immediately. Roll back, or accept brief verification failures during the cutover window.

ROTATION_IN_PROGRESS

Both old and new keys are present in current_jwks. This is the safe overlap state; finish the rotation by removing the old key only after the longest in-flight token has expired.

ROTATION_UNCLEAR

The provided JWKS pair lacks the metadata needed to classify rotation (e.g. keys without kid). Call /v1/providers/discover first or attach kid to your keys.

CI-provider error (HTTP 422)

CI_PROVIDER_UNKNOWN

provider is not one of github_actions or gitlab. The 422 body lists supported providers.

Suspicious patterns (HTTP 200, in suspicious_warnings[])

Returned by /v1/inspect/token. These are heuristic warnings about token structure, not validation failures. They flag dangerous shapes early so they can be refused before signature work runs.

ALG_NONE

header.alg is the literal string "none". Any verifier that honours this header trivially accepts forged tokens. JWTShield’s validate path will reject this regardless of header — this warning surfaces it during decode.

JKU_PRESENT

header.jku is set. Naïve verifiers fetch the URL and use it as the key source, allowing the token issuer to point key resolution at attacker-controlled infrastructure. JWTShield ignores jku during validation; this warning is for clients that may not.

KID_MISSING

No kid in the header. JWKS rotation cannot select between overlapping keys without it. Token issuance is acceptable but operationally fragile.

EXP_MISSING

No exp claim in the payload. Such a token never expires — refresh, revocation, and least-privilege windows all rely on exp being present.

Request validation (HTTP 422)

Standard FastAPI envelope. Most common causes:

{
  "detail": [
    {
      "loc": ["body"],
      "msg": "Exactly one of 'policy' or 'issuer_profile_id' must be provided.",
      "type": "value_error"
    }
  ]
}