The following steps provide a structured guide for integrating authentication with Fiskil’s Data Provider platform.
Authentication Flow
The diagram below illustrates how authentication works between the Fiskil Data Provider and your Resource Server:
JWT
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 kid JOSE header so your server can select the correct JWK.
The JWKS URL for your instance is available in the Console under Settings → Domains.
Validating the JWT
Once you have the public key you must validate the JWT. When you validate the JWT, ensure that:
- The signature is valid.
- The token is not expired (check the
exp claim).
- The
sub and iss claims match your Data Provider subdomain (provided during onboarding).
- The
aud is the URI of the resource being requested on the Data API.
- The
jti has not been used before.
Node.js Implementation Example
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 attacks
const 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 example
function 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 route
app.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.
Example: authenticated request
The example below shows a JWT used to authenticate a request to a hypothetical utilities provider, Acme Company.
GET /v1/energy/customer/bob/accounts HTTP/2
Host: data-api.acme-company.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImVjZDdjMjEyLWM0YmQtNGRhNi04ZjhkLTgyNjUxODdiNDIwMCJ9.eyJpc3MiOiJodHRwczovL2Nkci5hY21lLWVuZXJneS5jb20iLCJzdWIiOiJodHRwczovL2Nkci5hY21lLWVuZXJneS5jb20iLCJhdWQiOiJodHRwczovL2RhdGEtYXBpLmFjbWU... (truncated)
For security and readability, avoid logging full tokens. Log only minimal metadata (for example, the jti).
Decoded JWT
{
"alg": "PS256",
"typ": "JWT",
"kid": "ecd7c212-c4bd-4da6-8f8d-8265187b4200"
}
Claims
{
"iss": "https://cdr.acme-company.com",
"sub": "https://cdr.acme-company.com",
"aud": "https://data-api.acme-company.com/v1/energy/customer/bob/accounts",
"iat": 1691939022,
"exp": 1692939022,
"jti": "2e7da1da-e410-48ec-827f-e17658dd87b2"
}
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.
Firewall
While JWT is sufficient to authenticate the Data Provider with your API, we recommend adding a firewall IP allow-list to further protect your data.
- Fiskil provides IP addresses for allow-listing during onboarding.
- The IP addresses are also available in the Console under Settings → Resource Server.
Final Notes
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.