"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApplePortalSessionService = void 0;
const helpers_1 = require("../../common/helpers");
const yok_1 = require("../../common/yok");
const crypto = require("crypto");
class ApplePortalSessionService {
    constructor($applePortalCookieService, $errors, $httpClient, $logger, $prompter) {
        this.$applePortalCookieService = $applePortalCookieService;
        this.$errors = $errors;
        this.$httpClient = $httpClient;
        this.$logger = $logger;
        this.$prompter = $prompter;
        this.loginConfigEndpoint = "https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com";
        this.defaultLoginConfig = {
            authServiceUrl: "https://idmsa.apple.com/appleautcodh",
            authServiceKey: "e0b80c3bf78523bfe80974d320935bfa30add02e1bff88ec2166c6bd5a706c42",
        };
    }
    createUserSession(credentials, opts) {
        return __awaiter(this, void 0, void 0, function* () {
            const loginResult = yield this.login(credentials, opts);
            if (!opts || !opts.sessionBase64) {
                if (loginResult.isTwoFactorAuthenticationEnabled) {
                    const authServiceKey = (yield this.getLoginConfig()).authServiceKey;
                    yield this.handleTwoFactorAuthentication(loginResult.scnt, loginResult.xAppleIdSessionId, authServiceKey, loginResult.hashcash);
                }
                const sessionResponse = yield this.$httpClient.httpRequest({
                    url: "https://appstoreconnect.apple.com/olympus/v1/session",
                    method: "GET",
                    headers: {
                        Cookie: this.$applePortalCookieService.getUserSessionCookie(),
                    },
                });
                this.$applePortalCookieService.updateUserSessionCookie(sessionResponse.headers["set-cookie"]);
            }
            const userDetailsResponse = yield this.$httpClient.httpRequest({
                url: "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/user/detail",
                method: "GET",
                headers: {
                    "Content-Type": "application/json",
                    Cookie: this.$applePortalCookieService.getUserSessionCookie(),
                },
            });
            this.$applePortalCookieService.updateUserSessionCookie(userDetailsResponse.headers["set-cookie"]);
            const userdDetails = JSON.parse(userDetailsResponse.body).data;
            const result = Object.assign(Object.assign(Object.assign({}, userdDetails), loginResult), { userSessionCookie: this.$applePortalCookieService.getUserSessionCookie() });
            return result;
        });
    }
    createWebSession(contentProviderId) {
        return __awaiter(this, void 0, void 0, function* () {
            const webSessionResponse = yield this.$httpClient.httpRequest({
                url: "https://appstoreconnect.apple.com/olympus/v1/session",
                method: "POST",
                body: {
                    provider: {
                        providerId: contentProviderId,
                    },
                },
                headers: {
                    Accept: "application/json, text/plain, */*",
                    "Accept-Encoding": "gzip, deflate, br",
                    "X-Csrf-Itc": "itc",
                    "Content-Type": "application/json;charset=UTF-8",
                    "X-Requested-With": "olympus-ui",
                    Cookie: this.$applePortalCookieService.getUserSessionCookie(),
                },
            });
            const webSessionCookie = this.$applePortalCookieService.getWebSessionCookie(webSessionResponse.headers["set-cookie"]);
            return webSessionCookie;
        });
    }
    login(credentials, opts) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            const result = {
                scnt: null,
                xAppleIdSessionId: null,
                isTwoFactorAuthenticationEnabled: false,
                areCredentialsValid: true,
                hashcash: null,
            };
            if (opts && opts.sessionBase64) {
                const decodedSession = Buffer.from(opts.sessionBase64, "base64").toString("utf8");
                this.$applePortalCookieService.updateUserSessionCookie([decodedSession]);
                result.isTwoFactorAuthenticationEnabled =
                    decodedSession.indexOf("DES") > -1;
            }
            else {
                try {
                    yield this.loginCore(credentials);
                }
                catch (err) {
                    const statusCode = err && err.response && err.response.status;
                    const bits = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.headers["x-apple-hc-bits"];
                    const challenge = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.headers["x-apple-hc-challenge"];
                    const hashcash = makeHashCash(bits, challenge);
                    result.hashcash = hashcash;
                    result.areCredentialsValid = statusCode !== 401 && statusCode !== 403;
                    result.isTwoFactorAuthenticationEnabled = statusCode === 409;
                    if (result.isTwoFactorAuthenticationEnabled &&
                        opts &&
                        opts.requireApplicationSpecificPassword &&
                        !opts.applicationSpecificPassword) {
                        this.$errors
                            .fail(`Your account has two-factor authentication enabled but --appleApplicationSpecificPassword option is not provided.
To generate an application-specific password, please go to https://appleid.apple.com/account/manage.
This password will be used for the iTunes Transporter, which is used to upload your application.`);
                    }
                    if (result.isTwoFactorAuthenticationEnabled &&
                        opts &&
                        opts.requireInteractiveConsole &&
                        !(0, helpers_1.isInteractive)()) {
                        this.$errors
                            .fail(`Your account has two-factor authentication enabled, but your console is not interactive.
For more details how to set up your environment, please execute "ns publish ios --help".`);
                    }
                    const headers = (err && err.response && err.response.headers) || {};
                    result.scnt = headers.scnt;
                    result.xAppleIdSessionId = headers["x-apple-id-session-id"];
                }
            }
            return result;
        });
    }
    loginCore(credentials) {
        return __awaiter(this, void 0, void 0, function* () {
            const loginConfig = yield this.getLoginConfig();
            const loginUrl = `${loginConfig.authServiceUrl}/auth/signin`;
            const headers = {
                "Content-Type": "application/json",
                "X-Requested-With": "XMLHttpRequest",
                "X-Apple-Widget-Key": loginConfig.authServiceKey,
                Accept: "application/json, text/javascript",
            };
            const body = {
                accountName: credentials.username,
                password: credentials.password,
                rememberMe: true,
            };
            const loginResponse = yield this.$httpClient.httpRequest({
                url: loginUrl,
                method: "POST",
                body,
                headers,
            });
            this.$applePortalCookieService.updateUserSessionCookie(loginResponse.headers["set-cookie"]);
        });
    }
    getLoginConfig() {
        return __awaiter(this, void 0, void 0, function* () {
            let config = null;
            try {
                const response = yield this.$httpClient.httpRequest({
                    url: this.loginConfigEndpoint,
                    method: "GET",
                });
                config = JSON.parse(response.body);
            }
            catch (err) {
                this.$logger.trace(`Error while executing request to ${this.loginConfigEndpoint}. More info: ${err}`);
            }
            return config || this.defaultLoginConfig;
        });
    }
    handleTwoFactorAuthentication(scnt, xAppleIdSessionId, authServiceKey, hashcash) {
        return __awaiter(this, void 0, void 0, function* () {
            const headers = {
                scnt: scnt,
                "X-Apple-Id-Session-Id": xAppleIdSessionId,
                "X-Apple-Widget-Key": authServiceKey,
                "X-Apple-HC": hashcash,
                Accept: "application/json",
            };
            const authResponse = yield this.$httpClient.httpRequest({
                url: "https://idmsa.apple.com/appleauth/auth",
                method: "GET",
                headers,
            });
            const data = JSON.parse(authResponse.body);
            const isSMS = data.trustedPhoneNumbers &&
                data.trustedPhoneNumbers.length === 1 &&
                data.noTrustedDevices; // 1 device and no trusted devices means sms was automatically sent.
            const multiSMS = data.trustedPhoneNumbers &&
                data.trustedPhoneNumbers.length !== 1 &&
                data.noTrustedDevices; // Not handling more than 1 sms device and no trusted devices.
            let token;
            if (data.trustedPhoneNumbers &&
                data.trustedPhoneNumbers.length &&
                !multiSMS) {
                const parsedAuthResponse = JSON.parse(authResponse.body);
                token = yield this.$prompter.getString(`Please enter the ${parsedAuthResponse.securityCode.length} digit code`, { allowEmpty: false });
                const body = {
                    securityCode: {
                        code: token.toString(),
                    },
                };
                let url = `https://idmsa.apple.com/appleauth/auth/verify/trusteddevice/securitycode`;
                if (isSMS) {
                    // No trusted devices means it must be sms.
                    body.mode = "sms";
                    body.phoneNumber = {
                        id: data.trustedPhoneNumbers[0].id,
                    };
                    url = `https://idmsa.apple.com/appleauth/auth/verify/phone/securitycode`;
                }
                yield this.$httpClient.httpRequest({
                    url,
                    method: "POST",
                    body,
                    headers: Object.assign(Object.assign({}, headers), { "Content-Type": "application/json" }),
                });
                const authTrustResponse = yield this.$httpClient.httpRequest({
                    url: "https://idmsa.apple.com/appleauth/auth/2sv/trust",
                    method: "GET",
                    headers,
                });
                this.$applePortalCookieService.updateUserSessionCookie(authTrustResponse.headers["set-cookie"]);
            }
            else if (multiSMS) {
                this.$errors.fail(`The NativeScript CLI does not support SMS authenticaton with multiple registered phone numbers.`);
            }
            else {
                this.$errors.fail(`Although response from Apple indicated activated Two-step Verification or Two-factor Authentication, NativeScript CLI don't know how to handle this response: ${data}`);
            }
        });
    }
}
exports.ApplePortalSessionService = ApplePortalSessionService;
yok_1.injector.register("applePortalSessionService", ApplePortalSessionService);
function makeHashCash(bits, challenge) {
    const version = 1;
    const dateString = getHashCanDateString();
    let result;
    for (let counter = 0;; counter++) {
        const hc = [version, bits, dateString, challenge, `:${counter}`].join(":");
        const shasumData = crypto.createHash("sha1");
        shasumData.update(hc);
        const digest = shasumData.digest();
        if (checkBits(+bits, digest)) {
            result = hc;
            break;
        }
    }
    return result;
}
function getHashCanDateString() {
    const now = new Date();
    return `${now.getFullYear()}${padTo2Digits(now.getMonth() + 1)}${padTo2Digits(now.getDate())}${padTo2Digits(now.getHours())}${padTo2Digits(now.getMinutes())}${padTo2Digits(now.getSeconds())}`;
}
function padTo2Digits(num) {
    return num.toString().padStart(2, "0");
}
function checkBits(bits, digest) {
    let result = true;
    for (let i = 0; i < bits; ++i) {
        result = checkBit(i, digest);
        if (!result)
            break;
    }
    return result;
}
function checkBit(position, buffer) {
    const bitOffset = position & 7; // in byte
    const byteIndex = position >> 3; // in buffer
    const bit = (buffer[byteIndex] >> bitOffset) & 1;
    return bit === 0;
}
//# sourceMappingURL=apple-portal-session-service.js.map