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://api.chainit.online/oauth/<orgId>/.well-known/jwks.json

Example:

https://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://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://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://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
}