How to Create a JWT Validation Endpoint
When a user is redirected to Xeni Quick Builder with a JWT token, Xeni calls your validation endpoint to confirm the token is authentic and retrieve the user's identity. This article explains how to build that endpoint.
How It Works
- Your server generates a JWT and redirects the user to Xeni.
- Xeni extracts the token from the SSO URL.
- Xeni sends a POST request to your JWT Validation Endpoint with the token.
- Your endpoint verifies the token signature, checks expiry, and returns the user data.
- If validation succeeds, the user is logged into Quick Builder.
Endpoint Specification
Your endpoint must accept the following request and return the appropriate response.
URL
POST /api/jwt-validation/validate
You choose the full URL (e.g., https://api.yourplatform.com/api/jwt-validation/validate). This URL is registered in the Xeni admin panel.
Request
Xeni sends a POST request with a JSON body:
{
"jwtToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}| Field | Type | Description |
|---|---|---|
jwtToken | string | The full JWT token string that was passed in the SSO redirect URL |
Success Response (200)
When the token is valid and not expired, return HTTP 200 with the following body:
{
"status": true,
"msg": "User token validated",
"data": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com",
"userid": "usrabc123"
}
}| Field | Type | Description |
|---|---|---|
status | boolean | Must be true for successful validation |
msg | string | Human-readable status message |
data.firstname | string | User's first name from the token claims |
data.lastname | string | User's last name from the token claims |
data.email | string | User's email address from the token claims |
data.user_id | string | User's unique identifier from the token claims |
Error Response (401)
When the token is invalid, expired, or cannot be verified, return HTTP 401:
{
"status": false,
"msg": "Invalid or expired token",
"data": null
}| Field | Type | Description |
|---|---|---|
status | boolean | Must be false for failed validation |
msg | string | Human-readable error message |
data | null | Must be null when validation fails |
Implementation Example
Node.js (Express)
const express = require('express');
const jwt = require('jsonwebtoken');
const SECRETKEY = process.env.JWTSECRET; // 32+ character secret
const app = express();
app.use(express.json());
app.post('/api/jwt-validation/validate', (req, res) => {
const { jwtToken } = req.body;
if (!jwtToken) {
return res.status(401).json({
status: false,
msg: 'No token provided',
data: null
});
}
try {
const decoded = jwt.verify(jwtToken, SECRET_KEY, {
algorithms: ['HS256']
});
return res.status(200).json({
status: true,
msg: 'User token validated',
data: {
firstname: decoded.firstname,
lastname: decoded.lastname,
email: decoded.email,
userid: decoded.userid
}
});
} catch (error) {
let message = 'Invalid or expired token';
if (error.name === 'TokenExpiredError') {
message = 'Token has expired';
} else if (error.name === 'JsonWebTokenError') {
message = 'Invalid token signature';
}
return res.status(401).json({
status: false,
msg: message,
data: null
});
}
});
Python (Flask)
from flask import Flask, request, jsonify
import jwt
import os
app = Flask(name)
SECRETKEY = os.environ.get('JWTSECRET') # 32+ character secret
@app.route('/api/jwt-validation/validate', methods=['POST'])
def validate_jwt():
body = request.get_json()
token = body.get('jwtToken') if body else None
if not token:
return jsonify({
'status': False,
'msg': 'No token provided',
'data': None
}), 401
try:
decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return jsonify({
'status': True,
'msg': 'User token validated',
'data': {
'firstname': decoded['firstname'],
'lastname': decoded['lastname'],
'email': decoded['email'],
'userid': decoded['userid']
}
}), 200
except jwt.ExpiredSignatureError:
return jsonify({
'status': False,
'msg': 'Token has expired',
'data': None
}), 401
except jwt.InvalidTokenError:
return jsonify({
'status': False,
'msg': 'Invalid token signature',
'data': None
}), 401
Validation Checklist
Your endpoint should verify the following before returning a success response:
- Token is present — The
jwtTokenfield exists and is not empty. - Signature is valid — The token was signed with the correct shared secret using HS256.
- Token is not expired — The
expclaim is in the future. - Required claims are present — The token contains
userid,email,firstname, andlast_name. - Issuer is correct — Optionally verify that the
issclaim matches your expected issuer.
Security Considerations
- Use HTTPS — Your validation endpoint must be served over HTTPS. Xeni will not call HTTP endpoints.
- Rate limit the endpoint — Protect against brute-force attempts by limiting requests per IP or per token.
- Log validation failures — Record failed validation attempts for security monitoring.
- Do not expose internal errors — Return generic error messages to the caller. Log detailed errors internally.
Next Steps
Once your validation endpoint is live and accessible, configure it in the Xeni admin panel so that Quick Builder knows where to send token verification requests.