Skip to content

Instantly share code, notes, and snippets.

@joawan
Created October 10, 2019 06:11
Show Gist options
  • Save joawan/f1cefbb8ee5b8fc90503ce32f21181a8 to your computer and use it in GitHub Desktop.
Save joawan/f1cefbb8ee5b8fc90503ce32f21181a8 to your computer and use it in GitHub Desktop.
Token introspection middleware with cache
const tokenIntrospection = require('token-introspection');
const crypto = require('crypto');
const createError = require('http-errors');
const throwIfTrue = require('throw-if-true');
const Cache = require('../lib/cache');
const wrap = require('../lib/routeWrapper');
const logger = require('../lib/logger');
const cache = new Cache();
function cacheKey(token) {
const hash = crypto.createHash('sha256');
hash.update(token);
const digest = hash.digest('base64');
return `token:${digest}`;
}
function expressTokenIntrospection(opts = {}) {
if (!opts.enabled) {
return (req, res, next) => next();
}
const maxTTL = opts.maxTTL || 2 * 60 * 60;
const introspect = tokenIntrospection(opts);
return wrap(async (req, res, next) => {
throwIfTrue(!req.token, createError.BadRequest, 'Token missing from request');
const key = cacheKey(req.token);
let updateCache = false;
let token = await cache.get(key);
if (!token) {
try {
token = await introspect(req.token, 'access_token');
updateCache = true;
logger.debug(`Got valid token introspection response: ${JSON.stringify(token)}`);
} catch (err) {
if (err instanceof tokenIntrospection.errors.TokenNotActiveError) {
throw new createError.Unauthorized(err.message);
}
logger.error(`Token introspection failed: ${err.message}`);
throw new createError.InternalServerError('An unknown error occurred when introspecting token');
}
} else {
logger.debug(`Found token in cache: ${JSON.stringify(token)}`);
}
req.token = token;
next(); // Intentionally continue execution before caching
if (updateCache && token.exp) {
const expiresIn = Math.ceil(token.exp - (Date.now() / 1000));
const ttl = Math.min(maxTTL, expiresIn);
if (ttl <= 0) {
return;
}
logger.debug(`Updating token '${token.jti}' in cache for client ${token.client_id}, expires in ${ttl}s`);
try {
cache.set(key, token, ttl);
} catch (err) {
logger.error(`Failed to cache token '${token.jti}' for client ${token.client_id}, and ttl ${ttl}s`);
}
}
});
}
module.exports = expressTokenIntrospection;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment