Skip to main content
Version 0.1Draft

Token debugger

Overview

A LuxID web tool for decoding ID Tokens and Access Tokens, validating their signature against the LuxID JWKS, and surfacing expiry, audience, issuer, acr, and amr values.

When integrating with LuxID, you will frequently need to inspect tokens to understand what claims are present, whether the signature is valid, and whether the token is still within its validity window. The LuxID Token Debugger is a dedicated web tool for this purpose.

The Token Debugger is available at: https://tools.luxid.lu/token-debugger

It is intended for use with UAT tokens and development tokens only. See the Security warning section before pasting any token.


What you can do with the token debugger

Paste a token or full token response

The tool accepts:

  • A raw ID Token (a JWT string beginning with eyJ...).
  • A raw Access Token (also a JWT in the LuxID implementation).
  • A full token response JSON - paste the entire body returned by the token endpoint, and the tool will extract and analyse both the id_token and access_token fields automatically.

Decode header and payload

The tool decodes and formats both the JWT header and payload sections as pretty-printed JSON:

Header example:

{
"alg": "RS256",
"typ": "JWT",
"kid": "luxid-signing-key-2024-01"
}

Payload example:

{
"iss": "https://login.luxid.lu",
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"aud": "your-client-id",
"exp": 1748000000,
"iat": 1747996400,
"auth_time": 1747996395,
"nonce": "abc123",
"acr": "urn:luxid:acr:level:substantial",
"amr": ["otp"],
"email": "testuser@example.com",
"email_verified": true,
"given_name": "Jean",
"family_name": "Dupont"
}

Validate the signature

The tool fetches the current JWKS from the LuxID JWKS endpoint for the relevant environment (production or UAT, detected from the iss claim) and verifies the token signature against the matching key by kid.

The result is displayed as:

  • Signature valid - the token was issued and signed by LuxID.
  • Signature invalid - the token has been tampered with, was signed by a different key, or is malformed.
  • Key not found - the kid in the token header does not match any key currently in the JWKS (this can happen if the token was issued before a key rotation and the old key has since been retired).

Expiry and timing checks

The tool displays the following in colour-coded form:

FieldGreenRed
exp (expiry)Token is still validToken has expired
iat (issued at)Within expected windowIssued in the future (clock skew issue)
nbf (not before, if present)Current time is past nbfToken not yet valid
auth_timePresent and within session windowMissing or unreasonably old

Audience and issuer checks

  • iss: compared against the expected LuxID issuer for the detected environment. A mismatch is flagged in red.
  • aud: displayed and highlighted. You must manually verify this matches your Client ID - the tool cannot know your application's registered Client ID.

If the aud contains an unexpected value (for example, aud is an array containing a third-party client ID you do not recognise), this should be treated as a serious security concern - see Protect your Application.

acr and amr human descriptions

LuxID tokens carry machine-readable values for the Authentication Context Class Reference (acr) and Authentication Methods References (amr). The tool maps these to human-readable descriptions:

acr valueDescription
urn:luxid:acr:level:lowLow assurance - password only (auth_level 2)
urn:luxid:acr:level:substantialSubstantial assurance - password + second factor or passkey (auth_level 3, 4, or 8)
urn:luxid:acr:level:highHigh assurance - LuxTrust (auth_level 9)
amr valueDescription
pwdPassword authentication
otpOne-Time Code via SMS or voice call
totpAuthenticator app (TOTP)
fidoPasskey (WebAuthn/FIDO2)
luxtrustLuxTrust mobile app or smartcard

For a full description of assurance levels, see Multi-factor authentication.


Screenshot

The Token Debugger showing a decoded ID Token with signature validation, expiry status, and acr/amr descriptions.


Security warning

danger

Never paste production tokens from real users into any online tool.

Production tokens contain personal data (email, given_name, family_name, sub, and potentially phone_number or other verified attributes). Pasting them into a web-based tool - including the LuxID Token Debugger - exposes that data to the tool's infrastructure, which may conflict with your data processing obligations under GDPR.

Follow this rule:

  • Use the LuxID Token Debugger only with UAT tokens generated from test user accounts.
  • For any analysis of production tokens, use offline tools running entirely within your own infrastructure (see Offline alternatives below).
  • If you need to share a token for debugging with the LuxID support team, use a UAT token or redact the payload before sharing.

Offline alternatives

If you prefer to work offline, or you need to inspect production tokens without sending them to an external service, the following approaches are recommended.

Using jwt.io (UAT and development tokens only)

jwt.io (opens in a new tab) is a widely used JWT inspector. Paste a token to decode the header and payload. To verify the signature:

  1. Open the LuxID JWKS endpoint for UAT:
https://login-uat.luxid.lu/mga/sps/oauth/oauth20/jwks/OIDC-LUXID
  1. Find the key whose kid matches the kid in your token header.
  2. Copy the x5c or n/e values and paste them as the public key in jwt.io.

The signature verification will confirm whether the token is authentic.

caution

Same rule applies: only use jwt.io with UAT or development tokens, not production tokens from real users.

Inspecting a token programmatically

Validate the signature, issuer, audience and expiry in one call, entirely within your own infrastructure. Only run this against UAT or development tokens, not production tokens from real users.

import jwt
import requests

def get_jwks(env='production'):
base = 'https://login.luxid.lu' if env == 'production' else 'https://login-uat.luxid.lu'
resp = requests.get(f"{base}/mga/sps/oauth/oauth20/jwks/OIDC-LUXID")
resp.raise_for_status()
return jwt.PyJWKClient(f"{base}/mga/sps/oauth/oauth20/jwks/OIDC-LUXID")

def inspect_token(id_token, client_id, env='production'):
issuer = f"https://login{'-uat' if env == 'uat' else ''}.luxid.lu"
jwks_client = get_jwks(env)
signing_key = jwks_client.get_signing_key_from_jwt(id_token)

payload = jwt.decode(
id_token,
signing_key.key,
algorithms=["RS256"],
audience=client_id,
issuer=issuer,
)
return payload

Using cURL and manual inspection

For quick payload inspection without signature verification:

# Extract and decode the payload section of a JWT
ID_TOKEN="eyJ..."

# Split on '.' and base64-decode the second segment
echo "$ID_TOKEN" | cut -d '.' -f2 | base64 -d 2>/dev/null | jq .
caution

This shows the payload but does not verify the signature. Use it only for quick development checks, not for any security-sensitive decision.


Common issues

"Key not found" after key rotation

LuxID rotates signing keys periodically. If your application caches the JWKS aggressively, a token signed with the new key will fail validation until your cache is refreshed.

Best practice: cache the JWKS with a short TTL (5-15 minutes) and re-fetch when a kid is not found in the cached keyset. Most JOSE libraries handle this automatically.

See Token validation issues for a full troubleshooting guide.

Token appears valid but claims are missing

If expected claims (for example, email or phone_number) are absent from the payload, the most likely causes are:

  • The corresponding scope was not requested in the authorisation request.
  • The claim is not configured in the Claim Template for your application.
  • The user has not granted consent for that claim.

Use the Attribute preview tool to diagnose the exact cause before changing code.

aud does not match your client ID

If the aud value in the token is not your Client ID, you must reject the token. This can happen if:

  • You are accidentally using the wrong token (for example, a token from a different application's flow).
  • A token is being replayed from a different application.
danger

Never accept a token whose aud does not match your registered Client ID.

Updated 2026-05-22