The Fiskil Data Provider uses JSON Web Tokens (JWTs) to authenticate to your Resource Server. JWTs are well-suited to Open Data platforms since they support fine-grained access control and short lifetimes. Requests to your API include a JWT in the Authorization header using the Bearer scheme.Each JWT is signed by the Data Provider. The public keys are exposed via a JWKS endpoint (shared during onboarding). Tokens include a kidJOSE header so your server can select the correct JWK.The JWKS URL for your instance is available in the Console under Settings → Domains.
Here’s a complete Node.js example showing how to validate JWTs from Fiskil’s Data Provider:
const jwt = require('jsonwebtoken');const jwksClient = require('jwks-rsa');// Configure JWKS client (replace with your actual JWKS URL)const client = jwksClient({ jwksUri: 'https://cdr.your-domain.com/.well-known/jwks.json', cache: true, cacheMaxAge: 3600000, // 1 hour rateLimit: true, jwksRequestsPerMinute: 5});// Store used JTIs to prevent replay attacksconst usedJtis = new Set();function getKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { if (err) { return callback(err); } const signingKey = key.getPublicKey(); callback(null, signingKey); });}async function validateFiskilJWT(token, requestUrl, expectedIssuer) { try { // Verify and decode the JWT const decoded = jwt.verify(token, getKey, { algorithms: ['PS256', 'RS256'], issuer: expectedIssuer, audience: requestUrl, clockTolerance: 30 // Allow 30 second clock skew }); // Additional validation checks const now = Math.floor(Date.now() / 1000); // Check token hasn't expired if (decoded.exp <= now) { throw new Error('Token has expired'); } // Check subject matches issuer if (decoded.sub !== expectedIssuer) { throw new Error('Subject claim does not match expected issuer'); } // Check for JTI replay attack if (usedJtis.has(decoded.jti)) { throw new Error('Token has already been used (replay attack)'); } // Store JTI to prevent replay usedJtis.add(decoded.jti); // Clean up old JTIs periodically (implement based on your needs) // You might want to use Redis or another storage for production return { valid: true, claims: decoded }; } catch (error) { return { valid: false, error: error.message }; }}// Express.js middleware examplefunction authenticateJWT(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Missing or invalid Authorization header' }); } const token = authHeader.substring(7); // Remove 'Bearer ' prefix const requestUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`; const expectedIssuer = 'https://cdr.your-domain.com'; // Replace with your domain validateFiskilJWT(token, requestUrl, expectedIssuer) .then(result => { if (result.valid) { req.jwt = result.claims; next(); } else { console.error('JWT validation failed:', result.error); res.status(401).json({ error: 'Invalid token' }); } }) .catch(error => { console.error('JWT validation error:', error); res.status(401).json({ error: 'Authentication failed' }); });}// Usage in your Express routeapp.get('/v1/energy/customer/:customerId/accounts', authenticateJWT, (req, res) => { // Access validated JWT claims via req.jwt console.log('Authenticated request from:', req.jwt.iss); console.log('Request ID:', req.jwt.jti); // Your API logic here res.json({ accounts: [] });});
This example stores used JTIs in memory. For production systems, use a distributed cache like Redis to prevent replay attacks across multiple server instances.
Consider implementing JTI cleanup logic to remove expired entries and prevent memory leaks. You can use the exp claim to determine when to clean up each JTI.
JWT is a battle-tested specification for sharing cryptographically secured claims. Use a production-grade library to fetch JWKS and verify JWTs in your chosen programming language. For more information on JWT see the references below.
Security is a complex topic and often a trade-off is being made between strictness and ease-of-use. If the above security mechanisms do not meet your expectations then please reach out to the Fiskil team and we can discuss alternative security schemes to ensure a smooth onboarding process while protecting your customers’ sensitive information.