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

Examples

Copy-paste starters for every endpoint, in curl, Node, Python, and CI.

Every snippet below honours the JWTSHIELD_URL environment variable. Set it to your account’s API endpoint before running.

Decode a token — /v1/inspect/token

inspect_token.sh
BASE_URL="${JWTSHIELD_URL:-https://api.jwtshield.com}"
TOKEN="$1"
curl --silent --show-error --fail \
--request POST \
--header "Authorization: Bearer $JWTSHIELD_KEY" \
--header "Content-Type: application/json" \
--data "$(printf '{"token": "%s"}' "$TOKEN")" \
"$BASE_URL/v1/inspect/token"
inspect_token.js
const BASE_URL = process.env.JWTSHIELD_URL || "https://api.jwtshield.com";
const KEY = process.env.JWTSHIELD_KEY;

async function inspectToken(token) {
const response = await fetch(`${BASE_URL}/v1/inspect/token`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ token }),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
return await response.json();
}

const body = await inspectToken(process.argv[2]);
console.log(`validated: ${body.validated}`);
console.log(`header:    ${JSON.stringify(body.decoded.header)}`);
console.log(`payload:   ${JSON.stringify(body.decoded.payload)}`);
inspect_token.py
import os, sys, httpx

BASE_URL = os.environ.get("JWTSHIELD_URL", "https://api.jwtshield.com")
KEY = os.environ["JWTSHIELD_KEY"]
response = httpx.post(
  f"{BASE_URL}/v1/inspect/token",
  headers={"Authorization": f"Bearer {KEY}"},
  json={"token": sys.argv[1]},
  timeout=10.0,
)
response.raise_for_status()
body = response.json()
print(f"validated: {body['validated']}")
print(f"header:    {body['decoded']['header']}")
print(f"payload:   {body['decoded']['payload']}")

Validate with an inline policy — /v1/validate/jwt

validate_jwt.sh
BASE_URL="${JWTSHIELD_URL:-https://api.jwtshield.com}"
SECRET="your-256-bit-secret"
ISSUER="https://issuer.example.com"
AUDIENCE="api://backend"
TOKEN="$1"

curl --silent --show-error --fail \
--request POST \
--header "Authorization: Bearer $JWTSHIELD_KEY" \
--header "Content-Type: application/json" \
--data "$(cat <<JSON
{
"token": "${TOKEN}",
"policy": {
  "secret": "${SECRET}",
  "issuer": "${ISSUER}",
  "audiences": ["${AUDIENCE}"],
  "allowed_algs": ["HS256"]
}
}
JSON
)" \
"$BASE_URL/v1/validate/jwt"
validate_jwt.js
const BASE_URL = process.env.JWTSHIELD_URL || "https://api.jwtshield.com";
const KEY = process.env.JWTSHIELD_KEY;

async function validateJwt(token) {
const policy = {
  secret: "your-256-bit-secret",
  issuer: "https://issuer.example.com",
  audiences: ["api://backend"],
  allowed_algs: ["HS256"],
};
const response = await fetch(`${BASE_URL}/v1/validate/jwt`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ token, policy }),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}

const body = await validateJwt(process.argv[2]);
console.log("valid:", body.valid);
for (const finding of body.findings) {
console.log(`  - ${finding.code} (${finding.severity}): ${finding.message}`);
}
validate_jwt.py
import os, sys, httpx

BASE_URL = os.environ.get("JWTSHIELD_URL", "https://api.jwtshield.com")
KEY = os.environ["JWTSHIELD_KEY"]
policy = {
  "secret": "your-256-bit-secret",
  "issuer": "https://issuer.example.com",
  "audiences": ["api://backend"],
  "allowed_algs": ["HS256"],
}
response = httpx.post(
  f"{BASE_URL}/v1/validate/jwt",
  headers={"Authorization": f"Bearer {KEY}"},
  json={"token": sys.argv[1], "policy": policy},
  timeout=10.0,
)
response.raise_for_status()
body = response.json()
print(f"valid:    {body['valid']}")
for f in body["findings"]:
  print(f"  - {f['code']} ({f['severity']}): {f['message']}")
print(f"summary:  {body['summary']}")

Validate against a registered issuer profile

When operators register profiles via the ISSUER_PROFILES_JSON environment variable, clients can reference them by ID instead of inlining the policy.

validate_with_profile.sh
curl -sSf -X POST "$JWTSHIELD_URL/v1/validate/jwt" \
-H "Authorization: Bearer $JWTSHIELD_KEY" \
-H "Content-Type: application/json" \
-d '{"token":"<jwt>","issuer_profile_id":"acme-auth0"}'
validate_with_profile.py
KEY = os.environ["JWTSHIELD_KEY"]
httpx.post(
  f"{BASE_URL}/v1/validate/jwt",
  headers={"Authorization": f"Bearer {KEY}"},
  json={
      "token": token,
      "issuer_profile_id": "acme-auth0",
  },
  timeout=10.0,
).raise_for_status()

Run a regression suite — /v1/test/auth-regression

auth_regression.sh
curl -sSf -X POST "$JWTSHIELD_URL/v1/test/auth-regression" \
-H "Authorization: Bearer $JWTSHIELD_KEY" \
-H "Content-Type: application/json" \
-d '{
  "checks": [
    {
      "token": "<good-token>",
      "issuer_profile_id": "acme-auth0"
    },
    {
      "token": "<expired-token>",
      "issuer_profile_id": "acme-auth0",
      "expected_failure_codes": ["TOKEN_EXPIRED"]
    }
  ],
  "fail_on_severity": "error"
}'

Validate a CI OIDC token — /v1/validate/ci-oidc

GitHub Actions:

# .github/workflows/jwtshield-validate.yml
permissions:
  contents: read
  id-token: write

jobs:
  validate-oidc:
    runs-on: ubuntu-latest
    steps:
      - name: Retrieve runner OIDC token
        id: token
        run: |
          JWT=$(curl -sSf \
            -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://jwtshield" \
            | python3 -c 'import json,sys; print(json.load(sys.stdin)["value"])')
          echo "::add-mask::$JWT"
          echo "jwt=$JWT" >> "$GITHUB_OUTPUT"
      - name: Validate via JWTShield
        run: |
          curl -sSf -H "Authorization: Bearer ${{ secrets.JWTSHIELD_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{
              "token": "${{ steps.token.outputs.jwt }}",
              "provider": "github_actions",
              "expected_repository": "${{ github.repository }}",
              "expected_ref": "${{ github.ref }}"
            }' \
            "$JWTSHIELD_URL/v1/validate/ci-oidc"

GitLab CI:

# .gitlab-ci.yml
validate-oidc:
  stage: validate
  image: python:3.12-slim
  id_tokens:
    JWTSHIELD_OIDC_TOKEN:
      aud: "api://jwtshield"
  script:
    - |
      curl -sSf -H "Authorization: Bearer $JWTSHIELD_KEY" \
        -H "Content-Type: application/json" \
        -d "{\"token\":\"$JWTSHIELD_OIDC_TOKEN\",\"provider\":\"gitlab\",\"expected_project_path\":\"$CI_PROJECT_PATH\"}" \
        "$JWTSHIELD_URL/v1/validate/ci-oidc"

See CI · OIDC guide for the full workflow with failure handling.

Lint an OIDC config — /v1/lint/oidc-config

curl -sSf -X POST "$JWTSHIELD_URL/v1/lint/oidc-config" \
  -H "Authorization: Bearer $JWTSHIELD_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "https://acme.auth0.com/",
    "client_id": "abc123",
    "audiences": ["api://acme-backend"],
    "jwks_uri": "https://acme.auth0.com/.well-known/jwks.json",
    "redirect_uris": ["https://app.acme.com/cb"],
    "alg_policy": { "allowed_algs": ["RS256"] },
    "https_required": true
  }'