Skip to main content

Token Validation

ChainIT issues three artifacts from the token endpoint. Treat each one correctly:

CredentialFormatSignature / verification
Access tokenJWTRS256 — verify using the org-scoped JWKS URL below
ID tokenJWTRS256 — verify using the same JWKS URL (signed by the same key)
Refresh tokenOpaque string (not a JWT)Do not parse as JWT; store and send only to the token endpoint

The JWKS Endpoint​

Public signing keys for both access tokens and ID tokens are published in JWKS format. The URL includes your organization ID (orgId).

URL pattern:

https://sandbox-api.chainit.online/oauth/<orgId>/.well-known/jwks.json

Example:

https://sandbox-api.chainit.online/oauth/00000000-0000-0000-0000-000000000000/.well-known/jwks.json

Replace <orgId> with your organization UUID (the same value shown in the Developer Portal under Application → Advanced next to JWKS URL).

Caching Keys

Always cache the JWKS response to avoid making a network request for every token validation. Most libraries handle this automatically with a background refresh.

Same JWKS for ID tokens

ID tokens are signed with the same RS256 key as access tokens. Use the JWKS URL above to verify both; you do not need your clientSecret to validate ID tokens.


Access token validation (RS256 + JWKS)​

A valid access JWT should pass the following checks:

  1. Signature: Verify RS256 using the public key from JWKS (kid in the JWT header must match a key in the set).
  2. Issuer (iss): Must exactly match the iss value inside the token for your environment (typically your ChainIT API base URL).
  3. Audience (aud): Must contain your application’s clientId where applicable.
  4. Expiration (exp): Current time must be before the expiration timestamp.
  5. Issued at (iat): Allow small clock skew; reject tokens from the future.

ID token validation (RS256 + JWKS)​

ID tokens are signed with the same RS256 key as access tokens. Verify them against the same JWKS endpoint and enforce the standard OIDC claims:

  1. Signature: Verify RS256 using the public key from JWKS (match the kid in the JWT header).
  2. Issuer (iss): Must match your ChainIT issuer URL.
  3. Audience (aud): Must equal your application’s clientId.
  4. Expiration (exp): Current time must be before the expiration timestamp.
  5. Nonce (nonce): If you supplied a nonce on the authorize request, it must match the token’s nonce claim.

Never verify an ID token using your clientSecret — ChainIT signs ID tokens asymmetrically (RS256).


Refresh tokens​

The refresh token is an opaque credential, not a JWT. Do not base64-decode it expecting a payload; store it securely and present it only when calling the token endpoint to obtain new tokens.


Implementation examples (access token)​

Using jwks-rsa and jsonwebtoken​

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

// Replace <<your_org_id>> with your organization UUID
const JWKS_URI = 'https://sandbox-api.chainit.online/oauth/<<your_org_id>>/.well-known/jwks.json';

const client = jwksClient({ jwksUri: JWKS_URI });

function getKey(header, callback){
client.getSigningKey(header.kid, function(err, key) {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}

const options = {
audience: '<<your_client_id>>',
issuer: 'https://sandbox-api.chainit.online',
algorithms: ['RS256']
};

jwt.verify(accessToken, getKey, options, function(err, decoded) {
if (err) {
console.error('Validation failed:', err.message);
} else {
console.log('Token is valid:', decoded);
}
});

Replace <<your_org_id>> and audience with values from your environment and token payload.

ID token (RS256 + JWKS) sketch​

The same getKey + JWKS setup works for ID tokens — only the audience and accepted algorithm matter:

jwt.verify(idToken, getKey, {
algorithms: ['RS256'],
audience: '<<your_client_id>>',
issuer: 'https://sandbox-api.chainit.online'
}, (err, decoded) => { /* ... */ });

Introspection endpoint​

If you cannot perform local validation (for example, your platform cannot use RS256 + JWKS), you can use the token introspection endpoint for access tokens.

Endpoint: POST /public-api/v1/auth/introspect

POST /public-api/v1/auth/introspect
Content-Type: application/json
Authorization: Basic {base64(clientId:clientSecret)}

{
"token": "eyJhbGciOiJSUzI1NiIsIn..."
}

Response:

{
"active": true,
"scope": "openid profile email",
"client_id": "<<your_client_id>>",
"sub": "user-uuid",
"exp": 1715600000
}