"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkAuthorization = void 0;
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const envs_1 = require("../envs");
const logging_1 = require("../logging");
const types_1 = require("../types");
const base64url_1 = require("../utils/base64url");
const hash_1 = require("../utils/hash");
const token_1 = require("./token");
const decodeRequestToken = (token) => {
    try {
        const decodedToken = jsonwebtoken_1.default.decode(token, { complete: true });
        if (!decodedToken || typeof (decodedToken === null || decodedToken === void 0 ? void 0 : decodedToken.payload) === 'string') {
            return;
        }
        return {
            headers: {
                accept: decodedToken.payload['h-accept'],
                acceptEncoding: decodedToken.payload['h-accept-encoding'],
                authorization: decodedToken.payload['h-authorization'],
                connection: decodedToken.payload['h-connection'],
                contentLength: decodedToken.payload['h-content-length'],
                contentType: decodedToken.payload['h-content-type'],
                host: decodedToken.payload['h-host'],
                userAgent: decodedToken.payload['h-user-agent'],
                clientId: decodedToken.payload['h-x-dfns-appid'],
                clientSecret: decodedToken.payload['h-x-dfns-appsecret'],
                nonce: decodedToken.payload['h-x-dfns-nonce'],
                userAction: decodedToken.payload['h-x-dfns-useraction'],
            },
            body: decodedToken.payload['r-body'] ? base64url_1.base64url.decode(decodedToken.payload['r-body']) : '',
            http: {
                authority: decodedToken.payload['r-authority'],
                method: decodedToken.payload['r-method'],
                path: decodedToken.payload['r-path'],
                scheme: decodedToken.payload['r-scheme'],
            },
        };
    }
    catch (e) {
        logging_1.logger.error('Failed to decode request JWT: ' + e);
    }
    return;
};
const verifyAuthBlock = async (authBlock) => {
    const signingPublicKey = await (0, token_1.getTokenPublicKey)();
    try {
        const decodedToken = jsonwebtoken_1.default.verify(authBlock.auth, signingPublicKey, {
            issuer: `auth.${(0, envs_1.maybeGetEnvParam)('BASE_DOMAIN')}`,
            audience: 'dfns',
            subject: 'dfns:auth',
            complete: true,
            algorithms: ['RS256'],
        });
        if (typeof decodedToken.payload === 'string') {
            logging_1.logger.error('Payload is not valid: ' + JSON.stringify(decodedToken));
            return;
        }
        const decodedRequest = decodeRequestToken(authBlock.request);
        if (!decodedRequest) {
            return;
        }
        const authInfo = decodedToken.payload['https://custom/app_metadata'];
        if (authInfo.app.hash != (0, hash_1.sha256)(authBlock.request)) {
            return;
        }
        return {
            request: decodedRequest,
            auth: authInfo,
        };
    }
    catch (e) {
        logging_1.logger.error('Failed to parse auth block: ' + e);
        logging_1.logger.error('Signing Key: ' + signingPublicKey);
        return;
    }
};
const normalizeBody = (body) => {
    if (!body) {
        return '';
    }
    return JSON.stringify(body);
};
const authIdentityFromAuthBlock = (authBlock) => {
    var _a, _b;
    if (((_a = authBlock.auth.user) === null || _a === void 0 ? void 0 : _a.identity.kind) === types_1.UserAuthKind.Employee) {
        return {
            kind: types_1.AuthIdentityKind.OrgEmployee,
            employeeId: authBlock.auth.user.identity.id,
            orgId: authBlock.auth.user.identity.orgId,
            isOrgOwner: false,
            username: authBlock.auth.user.identity.id,
            permissions: authBlock.auth.user.identity.permissions,
            scope: authBlock.auth.user.identity.scopes.join(' '),
        };
    }
    if (((_b = authBlock.auth.user) === null || _b === void 0 ? void 0 : _b.identity.kind) === types_1.UserAuthKind.Staff) {
        return {
            kind: types_1.AuthIdentityKind.DfnsStaff,
            permissions: authBlock.auth.user.identity.permissions,
            scope: authBlock.auth.user.identity.scopes.join(' '),
            username: authBlock.auth.user.identity.id,
        };
    }
    return {
        kind: types_1.AuthIdentityKind.OrgApiKey,
        apiKeyId: authBlock.auth.app.identity.id,
        orgId: authBlock.auth.app.identity.orgId,
        permissions: authBlock.auth.app.identity.permissions,
        scope: authBlock.auth.app.identity.scopes.join(' '),
    };
};
const checkAuthorization = async (authBlock, body) => {
    const decodedAuthBlock = await verifyAuthBlock(authBlock);
    if (!decodedAuthBlock) {
        logging_1.logger.error('Unable to parse JWT.');
        return;
    }
    if (normalizeBody(body) !== decodedAuthBlock.request.body) {
        logging_1.logger.error('Request body and signed body do not match.');
        return;
    }
    // check if the caller's token is valid
    // check if the user's token is valid
    if (!decodedAuthBlock.auth.authenticated || !decodedAuthBlock.auth.authorized) {
        logging_1.logger.error('Not authenticated or authorized.');
        return;
    }
    const authIdentity = authIdentityFromAuthBlock(decodedAuthBlock);
    return {
        authBlock: decodedAuthBlock,
        signedAttestationBlock: authBlock,
        authorization: decodedAuthBlock.request.headers.authorization,
        identity: authIdentity,
    };
};
exports.checkAuthorization = checkAuthorization;
//# sourceMappingURL=checkAuthorization.js.map