/*! @azure/msal-common v16.11.1 2026-07-07 */
'use strict';
import { AuthorityType } from './AuthorityType.mjs';
import { isOpenIdConfigResponse } from './OpenIdConfigResponse.mjs';
import { UrlString } from '../url/UrlString.mjs';
import { createClientAuthError } from '../error/ClientAuthError.mjs';
import { CIAM_AUTH_URL, DSTS, ADFS, AuthorityMetadataSource, AZURE_REGION_AUTO_DISCOVER_FLAG, RegionDiscoveryOutcomes, NOT_APPLICABLE, AAD_INSTANCE_DISCOVERY_ENDPT, INVALID_INSTANCE, DEFAULT_AUTHORITY_HOST, AAD_TENANT_DOMAIN_SUFFIX, KNOWN_PUBLIC_CLOUDS, FORWARD_SLASH, DEFAULT_COMMON_TENANT, REGIONAL_AUTH_PUBLIC_CLOUD_SUFFIX, AADAuthority } from '../utils/Constants.mjs';
import { EndpointMetadata, getCloudDiscoveryMetadataFromHardcodedValues, getCloudDiscoveryMetadataFromNetworkResponse, InstanceDiscoveryMetadataAliases } from './AuthorityMetadata.mjs';
import { createClientConfigurationError } from '../error/ClientConfigurationError.mjs';
import { ProtocolMode } from './ProtocolMode.mjs';
import { AzureCloudInstance } from './AuthorityOptions.mjs';
import { isCloudInstanceDiscoveryResponse } from './CloudInstanceDiscoveryResponse.mjs';
import { isCloudInstanceDiscoveryErrorResponse } from './CloudInstanceDiscoveryErrorResponse.mjs';
import { RegionDiscovery } from './RegionDiscovery.mjs';
import { AuthError } from '../error/AuthError.mjs';
import { AuthorityUpdateCloudDiscoveryMetadata, AuthorityUpdateEndpointMetadata, AuthorityUpdateMetadataWithRegionalInformation, AuthorityGetEndpointMetadataFromNetwork, RegionDiscoveryDetectRegion, AuthorityGetCloudDiscoveryMetadataFromNetwork } from '../telemetry/performance/PerformanceEvents.mjs';
import { invokeAsync } from '../utils/FunctionWrappers.mjs';
import { generateAuthorityMetadataExpiresAt, updateAuthorityEndpointMetadata, isAuthorityMetadataExpired, updateCloudDiscoveryMetadata } from '../cache/utils/CacheHelpers.mjs';
import { endpointResolutionError, endSessionEndpointNotSupported, openIdConfigError } from '../error/ClientAuthErrorCodes.mjs';
import { invalidAuthorityMetadata, untrustedAuthority, invalidCloudDiscoveryMetadata, issuerValidationFailed } from '../error/ClientConfigurationErrorCodes.mjs';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * The authority class validates the authority URIs used by the user, and retrieves the OpenID Configuration Data from the
 * endpoint. It will store the pertinent config data in this object for use during token calls.
 * @internal
 */
class Authority {
    constructor(authority, networkInterface, cacheManager, authorityOptions, logger, correlationId, performanceClient, managedIdentity) {
        this.canonicalAuthority = authority;
        this._canonicalAuthority.validateAsUri();
        this.networkInterface = networkInterface;
        this.cacheManager = cacheManager;
        this.authorityOptions = authorityOptions;
        this.regionDiscoveryMetadata = {
            region_used: undefined,
            region_source: undefined,
            region_outcome: undefined,
        };
        this.logger = logger;
        this.performanceClient = performanceClient;
        this.correlationId = correlationId;
        this.managedIdentity = managedIdentity || false;
        this.regionDiscovery = new RegionDiscovery(networkInterface, this.logger, this.performanceClient, this.correlationId);
    }
    /**
     * Get {@link AuthorityType:type}
     * @param authorityUri {@link IUri}
     * @private
     */
    getAuthorityType(authorityUri) {
        // CIAM auth url pattern is being standardized as: <tenant>.ciamlogin.com
        if (authorityUri.HostNameAndPort.endsWith(CIAM_AUTH_URL)) {
            return AuthorityType.Ciam;
        }
        const pathSegments = authorityUri.PathSegments;
        if (pathSegments.length) {
            switch (pathSegments[0].toLowerCase()) {
                case ADFS:
                    return AuthorityType.Adfs;
                case DSTS:
                    return AuthorityType.Dsts;
            }
        }
        return AuthorityType.Default;
    }
    // See above for AuthorityType
    get authorityType() {
        return this.getAuthorityType(this.canonicalAuthorityUrlComponents);
    }
    /**
     * ProtocolMode enum representing the way endpoints are constructed.
     */
    get protocolMode() {
        return this.authorityOptions.protocolMode;
    }
    /**
     * Returns authorityOptions which can be used to reinstantiate a new authority instance
     */
    get options() {
        return this.authorityOptions;
    }
    /**
     * A URL that is the authority set by the developer
     */
    get canonicalAuthority() {
        return this._canonicalAuthority.urlString;
    }
    /**
     * Sets canonical authority.
     */
    set canonicalAuthority(url) {
        this._canonicalAuthority = new UrlString(url, this.correlationId);
        this._canonicalAuthority.validateAsUri();
        this._canonicalAuthorityUrlComponents = null;
    }
    /**
     * Get authority components.
     */
    get canonicalAuthorityUrlComponents() {
        if (!this._canonicalAuthorityUrlComponents) {
            this._canonicalAuthorityUrlComponents =
                this._canonicalAuthority.getUrlComponents();
        }
        return this._canonicalAuthorityUrlComponents;
    }
    /**
     * Get hostname and port i.e. login.microsoftonline.com
     */
    get hostnameAndPort() {
        return this.canonicalAuthorityUrlComponents.HostNameAndPort.toLowerCase();
    }
    /**
     * Get tenant for authority.
     */
    get tenant() {
        return this.canonicalAuthorityUrlComponents.PathSegments[0];
    }
    /**
     * OAuth /authorize endpoint for requests
     */
    get authorizationEndpoint() {
        if (this.discoveryComplete()) {
            return this.replacePath(this.metadata.authorization_endpoint);
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * OAuth /token endpoint for requests
     */
    get tokenEndpoint() {
        if (this.discoveryComplete()) {
            return this.replacePath(this.metadata.token_endpoint);
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    get deviceCodeEndpoint() {
        if (this.discoveryComplete()) {
            return this.replacePath(this.metadata.token_endpoint.replace("/token", "/devicecode"));
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * OAuth logout endpoint for requests
     */
    get endSessionEndpoint() {
        if (this.discoveryComplete()) {
            // ROPC policies may not have end_session_endpoint set
            if (!this.metadata.end_session_endpoint) {
                throw createClientAuthError(endSessionEndpointNotSupported, this.correlationId);
            }
            return this.replacePath(this.metadata.end_session_endpoint);
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * OAuth issuer for requests
     */
    get selfSignedJwtAudience() {
        if (this.discoveryComplete()) {
            return this.replacePath(this.metadata.issuer);
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * Jwks_uri for token signing keys
     */
    get jwksUri() {
        if (this.discoveryComplete()) {
            return this.replacePath(this.metadata.jwks_uri);
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * Returns a flag indicating that tenant name can be replaced in authority {@link IUri}
     * @param authorityUri {@link IUri}
     * @private
     */
    canReplaceTenant(authorityUri) {
        return (authorityUri.PathSegments.length === 1 &&
            !Authority.reservedTenantDomains.has(authorityUri.PathSegments[0]) &&
            this.getAuthorityType(authorityUri) === AuthorityType.Default &&
            this.protocolMode !== ProtocolMode.OIDC);
    }
    /**
     * Replaces tenant in url path with current tenant. Defaults to common.
     * @param urlString
     */
    replaceTenant(urlString) {
        return urlString.replace(/{tenant}|{tenantid}/g, this.tenant);
    }
    /**
     * Replaces path such as tenant or policy with the current tenant or policy.
     * @param urlString
     */
    replacePath(urlString) {
        let endpoint = urlString;
        const cachedAuthorityUrl = new UrlString(this.metadata.canonical_authority, this.correlationId);
        const cachedAuthorityUrlComponents = cachedAuthorityUrl.getUrlComponents();
        const cachedAuthorityParts = cachedAuthorityUrlComponents.PathSegments;
        const currentAuthorityParts = this.canonicalAuthorityUrlComponents.PathSegments;
        currentAuthorityParts.forEach((currentPart, index) => {
            let cachedPart = cachedAuthorityParts[index];
            if (index === 0 &&
                this.canReplaceTenant(cachedAuthorityUrlComponents)) {
                const tenantId = new UrlString(this.metadata.authorization_endpoint, this.correlationId).getUrlComponents().PathSegments[0];
                /**
                 * Check if AAD canonical authority contains tenant domain name, for example "testdomain.onmicrosoft.com",
                 * by comparing its first path segment to the corresponding authorization endpoint path segment, which is
                 * always resolved with tenant id by OIDC.
                 */
                if (cachedPart !== tenantId) {
                    this.logger.verbose(`1q3g2x ${cachedPart} ${tenantId}`, this.correlationId);
                    cachedPart = tenantId;
                }
            }
            if (currentPart !== cachedPart) {
                endpoint = endpoint.replace(`/${cachedPart}/`, `/${currentPart}/`);
            }
        });
        return this.replaceTenant(endpoint);
    }
    /**
     * The default open id configuration endpoint for any canonical authority.
     */
    get defaultOpenIdConfigurationEndpoint() {
        const canonicalAuthorityHost = this.hostnameAndPort;
        if (this.canonicalAuthority.endsWith("v2.0/") ||
            this.authorityType === AuthorityType.Adfs ||
            (this.protocolMode === ProtocolMode.OIDC &&
                !this.isAliasOfKnownMicrosoftAuthority(canonicalAuthorityHost))) {
            return `${this.canonicalAuthority}.well-known/openid-configuration`;
        }
        return `${this.canonicalAuthority}v2.0/.well-known/openid-configuration`;
    }
    /**
     * Boolean that returns whether or not tenant discovery has been completed.
     */
    discoveryComplete() {
        return !!this.metadata;
    }
    /**
     * Perform endpoint discovery to discover aliases, preferred_cache, preferred_network
     * and the /authorize, /token and logout endpoints.
     */
    async resolveEndpointsAsync() {
        const metadataEntity = this.getCurrentMetadataEntity();
        const cloudDiscoverySource = await invokeAsync(this.updateCloudDiscoveryMetadata.bind(this), AuthorityUpdateCloudDiscoveryMetadata, this.logger, this.performanceClient, this.correlationId)(metadataEntity);
        this.canonicalAuthority = this.canonicalAuthority.replace(this.hostnameAndPort, metadataEntity.preferred_network);
        const endpointSource = await invokeAsync(this.updateEndpointMetadata.bind(this), AuthorityUpdateEndpointMetadata, this.logger, this.performanceClient, this.correlationId)(metadataEntity);
        this.updateCachedMetadata(metadataEntity, cloudDiscoverySource, {
            source: endpointSource,
        });
        this.performanceClient?.addFields({
            cloudDiscoverySource: cloudDiscoverySource,
            authorityEndpointSource: endpointSource,
        }, this.correlationId);
    }
    /**
     * Returns metadata entity from cache if it exists, otherwise returns a new metadata entity built
     * from the configured canonical authority
     * @returns
     */
    getCurrentMetadataEntity() {
        let metadataEntity = this.cacheManager.getAuthorityMetadataByAlias(this.hostnameAndPort, this.correlationId);
        if (!metadataEntity) {
            metadataEntity = {
                aliases: [],
                preferred_cache: this.hostnameAndPort,
                preferred_network: this.hostnameAndPort,
                canonical_authority: this.canonicalAuthority,
                authorization_endpoint: "",
                token_endpoint: "",
                end_session_endpoint: "",
                issuer: "",
                aliasesFromNetwork: false,
                endpointsFromNetwork: false,
                expiresAt: generateAuthorityMetadataExpiresAt(),
                jwks_uri: "",
            };
        }
        return metadataEntity;
    }
    /**
     * Updates cached metadata based on metadata source and sets the instance's metadata
     * property to the same value
     * @param metadataEntity
     * @param cloudDiscoverySource
     * @param endpointMetadataResult
     */
    updateCachedMetadata(metadataEntity, cloudDiscoverySource, endpointMetadataResult) {
        if (cloudDiscoverySource !== AuthorityMetadataSource.CACHE &&
            endpointMetadataResult?.source !==
                AuthorityMetadataSource.CACHE) {
            // Reset the expiration time unless both values came from a successful cache lookup
            metadataEntity.expiresAt =
                generateAuthorityMetadataExpiresAt();
            metadataEntity.canonical_authority = this.canonicalAuthority;
        }
        const cacheKey = this.cacheManager.generateAuthorityMetadataCacheKey(metadataEntity.preferred_cache, this.correlationId);
        this.cacheManager.setAuthorityMetadata(cacheKey, metadataEntity, this.correlationId);
        this.metadata = metadataEntity;
    }
    /**
     * Update AuthorityMetadataEntity with new endpoints and return where the information came from
     * @param metadataEntity
     */
    async updateEndpointMetadata(metadataEntity) {
        const localMetadata = this.updateEndpointMetadataFromLocalSources(metadataEntity);
        // Further update may be required for hardcoded metadata if regional metadata is preferred
        if (localMetadata) {
            if (localMetadata.source ===
                AuthorityMetadataSource.HARDCODED_VALUES) {
                // If the user prefers to use an azure region replace the global endpoints with regional information.
                if (this.authorityOptions.azureRegionConfiguration?.azureRegion) {
                    if (localMetadata.metadata) {
                        const hardcodedMetadata = await invokeAsync(this.updateMetadataWithRegionalInformation.bind(this), AuthorityUpdateMetadataWithRegionalInformation, this.logger, this.performanceClient, this.correlationId)(localMetadata.metadata);
                        updateAuthorityEndpointMetadata(metadataEntity, hardcodedMetadata, false);
                        metadataEntity.canonical_authority =
                            this.canonicalAuthority;
                    }
                }
            }
            return localMetadata.source;
        }
        // Get metadata from network if local sources aren't available
        let metadata = await invokeAsync(this.getEndpointMetadataFromNetwork.bind(this), AuthorityGetEndpointMetadataFromNetwork, this.logger, this.performanceClient, this.correlationId)();
        if (metadata) {
            // Validate the issuer returned by the OIDC discovery document.
            this.validateIssuer(metadata.issuer);
            // If the user prefers to use an azure region replace the global endpoints with regional information.
            if (this.authorityOptions.azureRegionConfiguration?.azureRegion) {
                metadata = await invokeAsync(this.updateMetadataWithRegionalInformation.bind(this), AuthorityUpdateMetadataWithRegionalInformation, this.logger, this.performanceClient, this.correlationId)(metadata);
            }
            updateAuthorityEndpointMetadata(metadataEntity, metadata, true);
            return AuthorityMetadataSource.NETWORK;
        }
        else {
            // Metadata could not be obtained from the config, cache, network or hardcoded values
            throw createClientAuthError(openIdConfigError, this.defaultOpenIdConfigurationEndpoint, this.correlationId);
        }
    }
    /**
     * Updates endpoint metadata from local sources and returns where the information was retrieved from and the metadata config
     * response if the source is hardcoded metadata
     * @param metadataEntity
     * @returns
     */
    updateEndpointMetadataFromLocalSources(metadataEntity) {
        this.logger.verbose("1fi0kc", this.correlationId);
        const configMetadata = this.getEndpointMetadataFromConfig();
        if (configMetadata) {
            this.logger.verbose("06t0uj", this.correlationId);
            updateAuthorityEndpointMetadata(metadataEntity, configMetadata, false);
            return {
                source: AuthorityMetadataSource.CONFIG,
            };
        }
        this.logger.verbose("151k0p", this.correlationId);
        const hardcodedMetadata = this.getEndpointMetadataFromHardcodedValues();
        if (hardcodedMetadata) {
            updateAuthorityEndpointMetadata(metadataEntity, hardcodedMetadata, false);
            return {
                source: AuthorityMetadataSource.HARDCODED_VALUES,
                metadata: hardcodedMetadata,
            };
        }
        else {
            this.logger.verbose("1imop5", this.correlationId);
        }
        // Check cached metadata entity expiration status
        const metadataEntityExpired = isAuthorityMetadataExpired(metadataEntity);
        if (this.isAuthoritySameType(metadataEntity) &&
            metadataEntity.endpointsFromNetwork &&
            !metadataEntityExpired) {
            // No need to update
            this.logger.verbose("16uq31", "");
            return { source: AuthorityMetadataSource.CACHE };
        }
        else if (metadataEntityExpired) {
            this.logger.verbose("0uoibc", "");
        }
        return null;
    }
    /**
     * Compares the number of url components after the domain to determine if the cached
     * authority metadata can be used for the requested authority. Protects against same domain different
     * authority such as login.microsoftonline.com/tenant and login.microsoftonline.com/tfp/tenant/policy
     * @param metadataEntity
     */
    isAuthoritySameType(metadataEntity) {
        const cachedAuthorityUrl = new UrlString(metadataEntity.canonical_authority, this.correlationId);
        const cachedParts = cachedAuthorityUrl.getUrlComponents().PathSegments;
        return (cachedParts.length ===
            this.canonicalAuthorityUrlComponents.PathSegments.length);
    }
    /**
     * Parse authorityMetadata config option
     */
    getEndpointMetadataFromConfig() {
        if (this.authorityOptions.authorityMetadata) {
            try {
                return JSON.parse(this.authorityOptions.authorityMetadata);
            }
            catch (e) {
                throw createClientConfigurationError(invalidAuthorityMetadata, this.correlationId);
            }
        }
        return null;
    }
    /**
     * Gets OAuth endpoints from the given OpenID configuration endpoint.
     *
     * @param hasHardcodedMetadata boolean
     */
    async getEndpointMetadataFromNetwork() {
        const options = {};
        /*
         * TODO: Add a timeout if the authority exists in our library's
         * hardcoded list of metadata
         */
        const openIdConfigurationEndpoint = this.defaultOpenIdConfigurationEndpoint;
        this.logger.verbose(`1y65x6 ${openIdConfigurationEndpoint}`, this.correlationId);
        try {
            const response = await this.networkInterface.sendGetRequestAsync(openIdConfigurationEndpoint, options);
            const isValidResponse = isOpenIdConfigResponse(response.body);
            if (isValidResponse) {
                return response.body;
            }
            else {
                this.logger.verbose("1koyv8", this.correlationId);
                return null;
            }
        }
        catch (e) {
            this.logger.verbose(`0a9wik ${e}`, this.correlationId);
            return null;
        }
    }
    /**
     * Get OAuth endpoints for common authorities.
     */
    getEndpointMetadataFromHardcodedValues() {
        if (this.hostnameAndPort in EndpointMetadata) {
            return EndpointMetadata[this.hostnameAndPort];
        }
        return null;
    }
    /**
     * Update the retrieved metadata with regional information.
     * User selected Azure region will be used if configured.
     */
    async updateMetadataWithRegionalInformation(metadata) {
        const userConfiguredAzureRegion = this.authorityOptions.azureRegionConfiguration?.azureRegion;
        if (userConfiguredAzureRegion) {
            if (userConfiguredAzureRegion !==
                AZURE_REGION_AUTO_DISCOVER_FLAG) {
                this.regionDiscoveryMetadata.region_outcome =
                    RegionDiscoveryOutcomes.CONFIGURED_NO_AUTO_DETECTION;
                this.regionDiscoveryMetadata.region_used =
                    userConfiguredAzureRegion;
                return Authority.replaceWithRegionalInformation(metadata, userConfiguredAzureRegion, this.correlationId);
            }
            const autodetectedRegionName = await invokeAsync(this.regionDiscovery.detectRegion.bind(this.regionDiscovery), RegionDiscoveryDetectRegion, this.logger, this.performanceClient, this.correlationId)(this.authorityOptions.azureRegionConfiguration
                ?.environmentRegion, this.regionDiscoveryMetadata);
            if (autodetectedRegionName) {
                this.regionDiscoveryMetadata.region_outcome =
                    RegionDiscoveryOutcomes.AUTO_DETECTION_REQUESTED_SUCCESSFUL;
                this.regionDiscoveryMetadata.region_used =
                    autodetectedRegionName;
                return Authority.replaceWithRegionalInformation(metadata, autodetectedRegionName, this.correlationId);
            }
            this.regionDiscoveryMetadata.region_outcome =
                RegionDiscoveryOutcomes.AUTO_DETECTION_REQUESTED_FAILED;
        }
        return metadata;
    }
    /**
     * Updates the AuthorityMetadataEntity with new aliases, preferred_network and preferred_cache
     * and returns where the information was retrieved from
     * @param metadataEntity
     * @returns AuthorityMetadataSource
     */
    async updateCloudDiscoveryMetadata(metadataEntity) {
        const localMetadataSource = this.updateCloudDiscoveryMetadataFromLocalSources(metadataEntity);
        if (localMetadataSource) {
            return localMetadataSource;
        }
        // Fallback to network as metadata source
        const metadata = await invokeAsync(this.getCloudDiscoveryMetadataFromNetwork.bind(this), AuthorityGetCloudDiscoveryMetadataFromNetwork, this.logger, this.performanceClient, this.correlationId)();
        if (metadata) {
            updateCloudDiscoveryMetadata(metadataEntity, metadata, true);
            return AuthorityMetadataSource.NETWORK;
        }
        // Metadata could not be obtained from the config, cache, network or hardcoded values
        throw createClientConfigurationError(untrustedAuthority, this.correlationId);
    }
    updateCloudDiscoveryMetadataFromLocalSources(metadataEntity) {
        this.logger.verbose("1tpqlr", this.correlationId);
        this.logger.verbosePii(`1fy7uz ${this.authorityOptions.knownAuthorities ||
            NOT_APPLICABLE}`, this.correlationId);
        this.logger.verbosePii(`08zabj ${this.authorityOptions.authorityMetadata ||
            NOT_APPLICABLE}`, this.correlationId);
        this.logger.verbosePii(`1o1kv3 ${metadataEntity.canonical_authority || NOT_APPLICABLE}`, this.correlationId);
        const metadata = this.getCloudDiscoveryMetadataFromConfig();
        if (metadata) {
            this.logger.verbose("1nakio", this.correlationId);
            updateCloudDiscoveryMetadata(metadataEntity, metadata, false);
            return AuthorityMetadataSource.CONFIG;
        }
        // If the cached metadata came from config but that config was not passed to this instance, we must go to hardcoded values
        this.logger.verbose("1x74aj", this.correlationId);
        const hardcodedMetadata = getCloudDiscoveryMetadataFromHardcodedValues(this.hostnameAndPort);
        if (hardcodedMetadata) {
            this.logger.verbose("0by47c", this.correlationId);
            updateCloudDiscoveryMetadata(metadataEntity, hardcodedMetadata, false);
            return AuthorityMetadataSource.HARDCODED_VALUES;
        }
        this.logger.verbose("0r2fzy", this.correlationId);
        const metadataEntityExpired = isAuthorityMetadataExpired(metadataEntity);
        if (this.isAuthoritySameType(metadataEntity) &&
            metadataEntity.aliasesFromNetwork &&
            !metadataEntityExpired) {
            this.logger.verbose("1uffgh", "");
            // No need to update
            return AuthorityMetadataSource.CACHE;
        }
        else if (metadataEntityExpired) {
            this.logger.verbose("0uoibc", "");
        }
        return null;
    }
    /**
     * Parse cloudDiscoveryMetadata config or check knownAuthorities
     */
    getCloudDiscoveryMetadataFromConfig() {
        // CIAM does not support cloud discovery metadata
        if (this.authorityType === AuthorityType.Ciam) {
            this.logger.verbose("04y84h", this.correlationId);
            return Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
        }
        // Check if network response was provided in config
        if (this.authorityOptions.cloudDiscoveryMetadata) {
            this.logger.verbose("0gszr3", this.correlationId);
            try {
                this.logger.verbose("1iifkx", this.correlationId);
                const parsedResponse = JSON.parse(this.authorityOptions.cloudDiscoveryMetadata);
                const metadata = getCloudDiscoveryMetadataFromNetworkResponse(parsedResponse.metadata, this.hostnameAndPort);
                this.logger.verbose("0q67e3", "");
                if (metadata) {
                    this.logger.verbose("0hzfao", this.correlationId);
                    return metadata;
                }
                else {
                    this.logger.verbose("1ajz3u", this.correlationId);
                }
            }
            catch (e) {
                this.logger.verbose("1wq5tu", this.correlationId);
                throw createClientConfigurationError(invalidCloudDiscoveryMetadata, this.correlationId);
            }
        }
        // If cloudDiscoveryMetadata is empty or does not contain the host, check knownAuthorities
        if (this.isInKnownAuthorities(this.hostnameAndPort)) {
            this.logger.verbose("0mt9al", this.correlationId);
            return Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
        }
        return null;
    }
    /**
     * Called to get metadata from network if CloudDiscoveryMetadata was not populated by config
     *
     * @param hasHardcodedMetadata boolean
     */
    async getCloudDiscoveryMetadataFromNetwork() {
        const instanceDiscoveryEndpoint = `${AAD_INSTANCE_DISCOVERY_ENDPT}${this.canonicalAuthority}oauth2/v2.0/authorize`;
        const options = {};
        /*
         * TODO: Add a timeout if the authority exists in our library's
         * hardcoded list of metadata
         */
        let match = null;
        try {
            const response = await this.networkInterface.sendGetRequestAsync(instanceDiscoveryEndpoint, options);
            let typedResponseBody;
            let metadata;
            if (isCloudInstanceDiscoveryResponse(response.body)) {
                typedResponseBody =
                    response.body;
                metadata = typedResponseBody.metadata;
                this.logger.verbosePii(`1vglyt ${typedResponseBody.tenant_discovery_endpoint}`, this.correlationId);
            }
            else if (isCloudInstanceDiscoveryErrorResponse(response.body)) {
                this.logger.warning(`062uto ${response.status}`, this.correlationId);
                typedResponseBody =
                    response.body;
                if (typedResponseBody.error === INVALID_INSTANCE) {
                    this.logger.error("1x90tm", this.correlationId);
                    return null;
                }
                this.logger.warning(`0wchdm ${typedResponseBody.error}`, this.correlationId);
                this.logger.warning(`1s5mpv ${typedResponseBody.error_description}`, this.correlationId);
                this.logger.warning("1yhqpw", this.correlationId);
                metadata = [];
            }
            else {
                this.logger.error("0768g0", this.correlationId);
                return null;
            }
            this.logger.verbose("1lrobr", this.correlationId);
            match = getCloudDiscoveryMetadataFromNetworkResponse(metadata, this.hostnameAndPort);
        }
        catch (error) {
            if (error instanceof AuthError) {
                this.logger.error(`0vwhc7 ${error.errorCode} ${error.errorMessage}`, this.correlationId);
            }
            else {
                const typedError = error;
                this.logger.error(`0s2z41 ${typedError.name} ${typedError.message}`, this.correlationId);
            }
            return null;
        }
        // Custom Domain scenario, host is trusted because Instance Discovery call succeeded
        if (!match) {
            this.logger.warning("0jp28q", this.correlationId);
            this.logger.verbose("130sd8", this.correlationId);
            match = Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
        }
        return match;
    }
    /**
     * Helper function to determine if a host is included in the knownAuthorities config option.
     */
    isInKnownAuthorities(host) {
        const normalizedHost = host.toLowerCase();
        const matches = this.authorityOptions.knownAuthorities.filter((authority) => {
            return (authority &&
                UrlString.getDomainFromUrl(authority, this.correlationId).toLowerCase() === normalizedHost);
        });
        return matches.length > 0;
    }
    /**
     * helper function to populate the authority based on azureCloudOptions
     * @param authorityString
     * @param azureCloudOptions
     */
    static generateAuthority(authorityString, azureCloudOptions) {
        let authorityAzureCloudInstance;
        if (azureCloudOptions &&
            azureCloudOptions.azureCloudInstance !== AzureCloudInstance.None) {
            const tenant = azureCloudOptions.tenant
                ? azureCloudOptions.tenant
                : DEFAULT_COMMON_TENANT;
            authorityAzureCloudInstance = `${azureCloudOptions.azureCloudInstance}/${tenant}/`;
        }
        return authorityAzureCloudInstance
            ? authorityAzureCloudInstance
            : authorityString;
    }
    /**
     * Creates cloud discovery metadata object from a given host
     * @param host
     */
    static createCloudDiscoveryMetadataFromHost(host) {
        return {
            preferred_network: host,
            preferred_cache: host,
            aliases: [host],
        };
    }
    /**
     * helper function to generate environment from authority object
     */
    getPreferredCache() {
        if (this.managedIdentity) {
            return DEFAULT_AUTHORITY_HOST;
        }
        else if (this.discoveryComplete()) {
            return this.metadata.preferred_cache;
        }
        else {
            throw createClientAuthError(endpointResolutionError, this.correlationId);
        }
    }
    /**
     * Returns whether or not the provided host is an alias of this authority instance
     * @param host
     */
    isAlias(host) {
        return this.metadata.aliases.indexOf(host) > -1;
    }
    /**
     * Returns whether or not the provided host is an alias of a known Microsoft authority for purposes of endpoint discovery
     * @param host
     */
    isAliasOfKnownMicrosoftAuthority(host) {
        return InstanceDiscoveryMetadataAliases.has(host);
    }
    /**
     * Validates the `issuer` returned by an OIDC discovery document against
     * this authority, per
     * https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation
     *
     * The issuer is accepted when ANY of the following holds:
     *  1. The issuer scheme + host + port match the authority's (path may
     *     differ). Applies to all authorities.
     *  2. The authority is a Microsoft cloud authority (public, sovereign,
     *     or CIAM), the issuer is HTTPS, and the issuer host is in the known
     *     Microsoft authority host set.
     *  3. Same as (2), but the issuer host is a single-label regional variant
     *     of a known Microsoft host (e.g. `westus.login.microsoftonline.com`).
     *  4. Same as (2), but the issuer host matches the CIAM tenant pattern
     *     `{tenant}.ciamlogin.com` with an optional `/{tenant}[.onmicrosoft.com][/v2.0]`
     *     path.
     *  5. The issuer host is HTTPS and is explicitly listed in the
     *     developer-configured `knownAuthorities`. This covers scenarios where
     *     the OIDC discovery document returns an issuer host that differs from
     *     the authority (e.g., a GUID-based issuer for a name-based CIAM authority).
     *
     * @param issuer The `issuer` value returned in the OIDC discovery document.
     * @throws ClientConfigurationError("issuer_validation_failed") on failure.
     */
    validateIssuer(issuer) {
        if (!issuer) {
            throw createClientConfigurationError(issuerValidationFailed, this.correlationId);
        }
        // Parse with the WHATWG URL API. URL normalizes scheme + host to lowercase per RFC 3986.
        let issuerUrl;
        try {
            issuerUrl = new URL(issuer);
        }
        catch {
            throw createClientConfigurationError(issuerValidationFailed, this.correlationId);
        }
        const issuerScheme = issuerUrl.protocol;
        const issuerHost = issuerUrl.host;
        const authorityScheme = (this.canonicalAuthorityUrlComponents.Protocol || "").toLowerCase();
        const authorityHost = (this.canonicalAuthorityUrlComponents.HostNameAndPort || "").toLowerCase();
        // Rule 1: Same scheme and host
        const matchesAuthorityOrigin = this.matchesAuthorityOrigin(issuerScheme, issuerHost, authorityScheme, authorityHost);
        // Rule 2: The issuer host is a well-known Microsoft authority host (HTTPS only)
        const matchesKnownMicrosoftHost = issuerScheme === "https:" &&
            this.isAliasOfKnownMicrosoftAuthority(issuerHost);
        /*
         * Rule 3: The issuer host is a regional variant ({region}.{host}) of a well-known host
         * (HTTPS only). E.g. westus2.login.microsoft.com
         */
        const matchesRegionalMicrosoftHost = issuerScheme === "https:" &&
            this.matchesRegionalMicrosoftHost(issuerHost);
        /*
         * Rule 4: CIAM-specific validation. In a CIAM scenario the issuer is expected to
         * have "{tenant}.ciamlogin.com" as the host, even when using a custom domain.
         */
        const matchesCiamTenantPattern = this.matchesCiamTenantPattern(issuerUrl, authorityHost, this.canonicalAuthorityUrlComponents.PathSegments);
        /*
         * Rule 5: The issuer host is explicitly listed in the developer-configured
         * knownAuthorities. This covers scenarios where the OIDC discovery document
         * returns an issuer with a different host than the authority
         * (e.g., a GUID-based issuer for a name-based authority).
         */
        const matchesKnownAuthority = issuerScheme === "https:" && this.isInKnownAuthorities(issuerHost);
        // Each rule is an independent boolean; the issuer is valid if ANY rule matches.
        if (matchesAuthorityOrigin ||
            matchesKnownMicrosoftHost ||
            matchesRegionalMicrosoftHost ||
            matchesCiamTenantPattern ||
            matchesKnownAuthority) {
            return;
        }
        // issuer validation fails if none of the above rules are satisfied
        throw createClientConfigurationError(issuerValidationFailed, this.correlationId);
    }
    /**
     * Rule 1: The issuer scheme + host (and port) match the authority's. Path
     * may differ. Applies to all authorities.
     */
    matchesAuthorityOrigin(issuerScheme, issuerHost, authorityScheme, authorityHost) {
        return issuerScheme === authorityScheme && issuerHost === authorityHost;
    }
    /**
     * Rule 3: The issuer host is a regional variant
     * (`{region}.{host}`) of a known Microsoft authority host.
     * E.g. `westus2.login.microsoft.com`.
     */
    matchesRegionalMicrosoftHost(issuerHost) {
        const firstDot = issuerHost.indexOf(".");
        if (firstDot > 0 && firstDot < issuerHost.length - 1) {
            const hostWithoutRegion = issuerHost.substring(firstDot + 1);
            return this.isAliasOfKnownMicrosoftAuthority(hostWithoutRegion);
        }
        return false;
    }
    /**
     * Rule 4: The issuer matches one of the well-known CIAM tenant patterns
     * (`https://{tenant}.ciamlogin.com[/{tenant}[.onmicrosoft.com][/v2.0]]`).
     *
     * The bare tenant name is extracted from the authority's first path segment
     * when available (stripping the `.onmicrosoft.com` suffix that
     * `transformCIAMAuthority` adds), or otherwise from the leftmost label of
     * the authority host (to support CIAM custom domain scenarios).
     *
     * Both `/{tenant}` and `/{tenant}.onmicrosoft.com` path forms are accepted
     * because the OIDC issuer may use either form depending on the authority URL
     * that was used to trigger discovery.
     */
    matchesCiamTenantPattern(issuerUrl, authorityHost, authorityPathSegments) {
        /*
         * authorityPathSegments[0] is the first path segment of the *authority
         * URL* after transformCIAMAuthority runs (e.g. "contoso.onmicrosoft.com").
         * Additional CIAM issuer path segments such as "/v2.0" are part of the
         * issuer string, not the authority URL's PathSegments.
         */
        const pathSegment = authorityPathSegments[0];
        /*
         * Extract the bare tenant name: strip the .onmicrosoft.com suffix when
         * present (introduced by transformCIAMAuthority), or fall back to the
         * first label of the authority hostname for non-transformed/custom-domain
         * CIAM authorities.
         */
        const tenantName = pathSegment
            ? pathSegment.endsWith(AAD_TENANT_DOMAIN_SUFFIX)
                ? pathSegment.slice(0, -AAD_TENANT_DOMAIN_SUFFIX.length)
                : pathSegment
            : authorityHost.split(".")[0];
        if (!tenantName) {
            return false;
        }
        const ciamBaseURL = `https://${tenantName}${CIAM_AUTH_URL}`;
        const validCiamPatterns = [
            ciamBaseURL,
            `${ciamBaseURL}/${tenantName}`,
            `${ciamBaseURL}/${tenantName}/v2.0`,
            `${ciamBaseURL}/${tenantName}${AAD_TENANT_DOMAIN_SUFFIX}`,
            `${ciamBaseURL}/${tenantName}${AAD_TENANT_DOMAIN_SUFFIX}/v2.0`, // https://{tenant}.ciamlogin.com/{tenant}.onmicrosoft.com/v2.0
        ];
        /*
         * Compose the canonical issuer string from URL components and strip any
         * trailing slashes from the path so it can be compared to the pattern set.
         */
        const issuerPath = issuerUrl.pathname.replace(/\/+$/, "");
        const normalizedIssuer = `${issuerUrl.protocol}//${issuerUrl.host}${issuerPath}`;
        return validCiamPatterns.some((pattern) => pattern === normalizedIssuer);
    }
    /**
     * Checks whether the provided host is that of a public cloud authority
     *
     * @param authority string
     * @returns bool
     */
    static isPublicCloudAuthority(host) {
        return KNOWN_PUBLIC_CLOUDS.indexOf(host) >= 0;
    }
    /**
     * Rebuild the authority string with the region
     *
     * @param host string
     * @param region string
     */
    static buildRegionalAuthorityString(host, region, correlationId, queryString) {
        // Create and validate a Url string object with the initial authority string
        const authorityUrlInstance = new UrlString(host, correlationId);
        authorityUrlInstance.validateAsUri();
        const authorityUrlParts = authorityUrlInstance.getUrlComponents();
        let hostNameAndPort = `${region}.${authorityUrlParts.HostNameAndPort}`;
        if (this.isPublicCloudAuthority(authorityUrlParts.HostNameAndPort)) {
            hostNameAndPort = `${region}.${REGIONAL_AUTH_PUBLIC_CLOUD_SUFFIX}`;
        }
        // Include the query string portion of the url
        const url = UrlString.constructAuthorityUriFromObject({
            ...authorityUrlInstance.getUrlComponents(),
            HostNameAndPort: hostNameAndPort,
        }, correlationId).urlString;
        // Add the query string if a query string was provided
        if (queryString)
            return `${url}?${queryString}`;
        return url;
    }
    /**
     * Replace the endpoints in the metadata object with their regional equivalents.
     *
     * @param metadata OpenIdConfigResponse
     * @param azureRegion string
     */
    static replaceWithRegionalInformation(metadata, azureRegion, correlationId) {
        const regionalMetadata = { ...metadata };
        regionalMetadata.authorization_endpoint =
            Authority.buildRegionalAuthorityString(regionalMetadata.authorization_endpoint, azureRegion, correlationId);
        regionalMetadata.token_endpoint =
            Authority.buildRegionalAuthorityString(regionalMetadata.token_endpoint, azureRegion, correlationId);
        if (regionalMetadata.end_session_endpoint) {
            regionalMetadata.end_session_endpoint =
                Authority.buildRegionalAuthorityString(regionalMetadata.end_session_endpoint, azureRegion, correlationId);
        }
        return regionalMetadata;
    }
    /**
     * Transform CIAM_AUTHORIY as per the below rules:
     * If no path segments found and it is a CIAM authority (hostname ends with .ciamlogin.com), then transform it
     *
     * NOTE: The transformation path should go away once STS supports CIAM with the format: `tenantIdorDomain.ciamlogin.com`
     * `ciamlogin.com` can also change in the future and we should accommodate the same
     *
     * @param authority
     */
    static transformCIAMAuthority(authority, correlationId) {
        let ciamAuthority = authority;
        const authorityUrl = new UrlString(authority, correlationId);
        const authorityUrlComponents = authorityUrl.getUrlComponents();
        // check if transformation is needed
        if (authorityUrlComponents.PathSegments.length === 0 &&
            authorityUrlComponents.HostNameAndPort.endsWith(CIAM_AUTH_URL)) {
            const tenantIdOrDomain = authorityUrlComponents.HostNameAndPort.split(".")[0];
            ciamAuthority = `${ciamAuthority}${tenantIdOrDomain}${AAD_TENANT_DOMAIN_SUFFIX}`;
        }
        return ciamAuthority;
    }
}
// Reserved tenant domain names that will not be replaced with tenant id
Authority.reservedTenantDomains = new Set([
    "{tenant}",
    "{tenantid}",
    AADAuthority.COMMON,
    AADAuthority.CONSUMERS,
    AADAuthority.ORGANIZATIONS,
]);
/**
 * Extract tenantId from authority
 */
function getTenantFromAuthorityString(authority, correlationId) {
    const authorityUrl = new UrlString(authority, correlationId);
    const authorityUrlComponents = authorityUrl.getUrlComponents();
    /**
     * For credential matching purposes, tenantId is the last path segment of the authority URL:
     *  AAD Authority - domain/tenantId -> Credentials are cached with realm = tenantId
     *  B2C Authority - domain/{tenantId}?/.../policy -> Credentials are cached with realm = policy
     *  tenantId is downcased because B2C policies can have mixed case but tfp claim is downcased
     *
     * Note that we may not have any path segments in certain OIDC scenarios.
     */
    const tenantId = authorityUrlComponents.PathSegments.slice(-1)[0]?.toLowerCase();
    switch (tenantId) {
        case AADAuthority.COMMON:
        case AADAuthority.ORGANIZATIONS:
        case AADAuthority.CONSUMERS:
            return undefined;
        default:
            return tenantId;
    }
}
function formatAuthorityUri(authorityUri) {
    return authorityUri.endsWith(FORWARD_SLASH)
        ? authorityUri
        : `${authorityUri}${FORWARD_SLASH}`;
}
function buildStaticAuthorityOptions(authOptions) {
    const rawCloudDiscoveryMetadata = authOptions.cloudDiscoveryMetadata;
    let cloudDiscoveryMetadata = undefined;
    if (rawCloudDiscoveryMetadata) {
        try {
            cloudDiscoveryMetadata = JSON.parse(rawCloudDiscoveryMetadata);
        }
        catch (e) {
            throw createClientConfigurationError(invalidCloudDiscoveryMetadata, "");
        }
    }
    return {
        canonicalAuthority: authOptions.authority
            ? formatAuthorityUri(authOptions.authority)
            : undefined,
        knownAuthorities: authOptions.knownAuthorities,
        cloudDiscoveryMetadata: cloudDiscoveryMetadata,
    };
}

export { Authority, buildStaticAuthorityOptions, formatAuthorityUri, getTenantFromAuthorityString };
//# sourceMappingURL=Authority.mjs.map
