Automate JWT Token Validation in CI/CD Testing Pipelines
Automate JWT token validation in GitHub Actions integration tests. Verify claims, check expiry, and assert token structure without a running auth server.
The Problem
Your integration tests call an auth endpoint, receive a JWT token, and need to verify it contains the right claims (user ID, role, expiry) before calling downstream APIs. Writing JWT parsing code in every test suite is tedious and error-prone.
Why This Matters
Testing JWT claims in CI catches auth bugs before they reach production. A missing <code>role</code> claim or wrong <code>aud</code> audience value causes authorization failures that are hard to debug in staging. Automating this check in your pipeline gives you confidence that every merge passes auth contract tests.
Step-by-Step Instructions
Decode a sample token using the tool below
Paste a JWT from your auth endpoint into the decoder. Inspect the payload structure to identify which claims your tests should assert: sub, role, aud, exp.
Add a JWT claim assertion step using Node.js
In your workflow, use node -e with a one-liner that base64-decodes the payload and checks fields. No JWT library required — just JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).
Assert required claims are present and correct
Check payload.role === 'admin', payload.exp > Date.now()/1000, and any custom claims your API depends on. Fail the job with process.exit(1) if assertions fail.
Use the token for downstream API calls
After validation, pass the token to subsequent steps as a step output. Use it in curl commands or API test frameworks as Authorization: Bearer $TOKEN.
Try It Now — JWT Decoder
Open full page →All processing happens in your browser — no data is sent to any server.
Before & After Example
- name: Get auth token
run: |
TOKEN=$(curl -s -X POST https://auth.example.com/token \
-d '{"client_id":"ci-bot","client_secret":"${{ secrets.CLIENT_SECRET }}"}' \
| jq -r '.access_token')
echo "token=$TOKEN" >> $GITHUB_OUTPUT
# ⚠️ Token stored but claims never verified
# Will downstream tests fail with 403 due to wrong role?
- name: Get and validate auth token
id: auth
run: |
RESPONSE=$(curl -s -X POST https://auth.example.com/token \
-H 'Content-Type: application/json' \
-d '{"client_id":"ci-bot","client_secret":"${{ secrets.CLIENT_SECRET }}"}')
TOKEN=$(echo "$RESPONSE" | jq -r '.access_token')
if [ "$TOKEN" = "null" ] || [ -z "$TOKEN" ]; then
echo "Failed to obtain token"
echo "$RESPONSE" | jq .
exit 1
fi
# Decode and validate JWT claims (no library needed)
node -e "
const token = '$TOKEN';
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString());
console.log('Token claims:', JSON.stringify(payload, null, 2));
// Assert required claims
if (!payload.sub) { console.error('Missing sub claim'); process.exit(1); }
if (payload.exp < Date.now() / 1000) { console.error('Token already expired'); process.exit(1); }
if (!['admin', 'ci-bot'].includes(payload.role)) {
console.error('Wrong role: ' + payload.role); process.exit(1);
}
console.log('✓ JWT claims valid');
"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Call protected API
run: |
curl -s -H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \
https://api.example.com/protected | jq .
Frequently Asked Questions
How do I decode a JWT without installing any npm packages?
JWT payloads are base64url-encoded JSON. In Node.js: JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString()). In Python: import base64, json; json.loads(base64.b64decode(token.split('.')[1] + '==')).
Should I verify the JWT signature in CI?
For testing your own auth service, verifying the signature proves the token was issued by your server. Use jsonwebtoken (Node) or PyJWT (Python) with your public key. For third-party tokens (Auth0, Cognito), use their provided JWKS endpoint.
How do I handle JWT rotation in long CI runs?
Tokens expire. For workflows longer than the token TTL, re-fetch the token before each API call, or use a refresh token flow. Store the expiry from the exp claim and conditionally refresh.
Related Workflows
Want the full JWT Decoder experience?
Open the standalone tool for more space, keyboard shortcuts, and additional features.
Open JWT Decoder →