"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const Cache = require("node-cache");
require("isomorphic-fetch");
const mergeAuthHeader = (token, req) => (Object.assign({}, req, { headers: Object.assign({}, req.headers, { Authorization: `Bearer ${token}` }) }));
const calculateExpirationTime = (expiresIn) => Date.now() + expiresIn * 1000 - 1 * 60 * 60 * 1000;
exports.createAuthMiddlewareForIntrospectionFlow = (options) => {
    let pendingTasks = {};
    const tokenCache = new Cache();
    return (next) => (request, response) => __awaiter(this, void 0, void 0, function* () {
        if ((request.headers && request.headers.authorization) ||
            (request.headers && request.headers.Authorization)) {
            return next(request, response);
        }
        const oauthTokenToValidate = request.headers && request.headers['X-Custom-OAuth-Token'];
        if (!oauthTokenToValidate) {
            return next(request, response);
        }
        const cached = tokenCache.get(oauthTokenToValidate);
        if (cached) {
            if (Date.now() < cached.expiresIn) {
                return next(mergeAuthHeader(oauthTokenToValidate, request), response);
            }
            return response.reject(new Error('Token has expired'));
        }
        pendingTasks[oauthTokenToValidate] = pendingTasks[oauthTokenToValidate] || {
            isFetching: false,
            tasks: []
        };
        pendingTasks[oauthTokenToValidate].tasks.push({ request, response });
        if (pendingTasks[oauthTokenToValidate].isFetching) {
            return;
        }
        pendingTasks[oauthTokenToValidate].isFetching = true;
        try {
            const basicAuth = new Buffer(`${options.admin.clientId}:${options.admin.clientSecret}`).toString('base64');
            const body = `token=${oauthTokenToValidate}`;
            const authResponse = yield fetch(`${options.authHost}/oauth/introspect`, {
                method: 'POST',
                headers: {
                    Authorization: `Basic ${basicAuth}`,
                    'Content-Length': Buffer.byteLength(body).toString(),
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body
            });
            if (!authResponse.ok) {
                return;
            }
            const { active: isTokenValid, scope: tokenScopes, exp: expiresIn } = yield authResponse.json();
            if (!isTokenValid) {
                console.log(isTokenValid, tokenScopes, expiresIn);
                return response.reject(new Error('Token is not valid'));
            }
            tokenCache.set(oauthTokenToValidate, {
                tokenScopes,
                expiresIn: calculateExpirationTime(expiresIn)
            });
            const executionQueue = [...pendingTasks[oauthTokenToValidate].tasks];
            delete pendingTasks[oauthTokenToValidate];
            executionQueue.forEach(task => next(mergeAuthHeader(oauthTokenToValidate, task.request), task.response));
        }
        catch (err) {
            response.reject(err);
        }
    });
};
//# sourceMappingURL=introspection-middleware.js.map