Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxyUrl option doesn't work behind a corporate proxy #6527

Open
rasulsafa opened this issue Sep 30, 2023 · 14 comments
Open

proxyUrl option doesn't work behind a corporate proxy #6527

rasulsafa opened this issue Sep 30, 2023 · 14 comments
Assignees
Labels
breaking change confidential-client Issues regarding ConfidentialClientApplications feature Feature requests. msal-node Related to msal-node package

Comments

@rasulsafa
Copy link

rasulsafa commented Sep 30, 2023

Core Library

MSAL Node (@azure/msal-node)

Core Library Version

1.18.3

Wrapper Library

Not Applicable

Wrapper Library Version

N/A

Public or Confidential Client?

Confidential

Description

For the past year or so, I have been using msal-node behind a corporate proxy with the workaround posted by another user here (overriding the networkClient with proxy-compatible versions of sendGetRequestAsync and sendPostRequestAsync). This has been working well, and now that msal-node natively supports proxies I wanted to try it out. Unfortunately, passing the proxyUrl parameter and disabling the custom networkClient yields an Endpoints Resolution Error

ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientAuthError: openid_config_error: Could not retrieve endpoints. Check your authority and verify the .well-known/openid-configuration endpoint returns the required endpoints. Attempted to retrieve endpoints from: https://login.microsoftonline.com/my tenant/v2.0/.well-known/openid-configuration

I investigated msal-nodes implementation of proxy compatibility to see if I could figure out the issue. Connection to the corporate proxy seemed to succeed. Log statements I put in the networkRequestViaProxy function showed that connection to the proxy was succeeding with 200 Connection established However, accessing microsoft's login servers seemed to be yielding a 400 Bad Request.

{
  headers: {
    'Content-Type': 'text/html',
    'Cache-Control': 'no-cache',
    'Content-Length': '2488',
    'X-Frame-Options': 'deny',
    'Proxy-Connection': 'Close'
  },
  body: {
    error: 'client_error',
    error_description: 'A client error occured.\n' +
      'Http status code: 400\n' +
      'Http status message: badrequest\n' +
      'Headers: {"Content-Type":"text/html","Cache-Control":"no-cache","Content-Length":"2488","X-Frame-Options":"deny","Proxy-Connection":"Close"}'
  },
  status: 400
}

And these are the contents of the payload variable in that function:

GET https://login.microsoftonline.com/my tenant/v2.0/.well-known/openid-configuration HTTP/1.1
Host: login.microsoftonline.com
Connection: close

The existing proxy implementation using http.request and manually writing http requests through the socket seems overly complex and dated and doesnt support the Proxy-Authorization header - it would be great if it were rewritten to use fetch and https-proxy-agent. They're far newer and cleaner, and it's how the MS Graph Client implements proxy support. I've never had a single issue with the Graph Clients proxy support.

Error Message

ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientAuthError: openid_config_error: Could not retrieve endpoints. Check your authority and verify the .well-known/openid-configuration endpoint returns the required endpoints. Attempted to retrieve endpoints from: https://login.microsoftonline.com/my tenant/v2.0/.well-known/openid-configuration

Msal Logs

No response

MSAL Configuration

const clientConfig = {
  auth: {
    clientId: vault.azure_ad_client_id,
    authority: 'https://login.microsoftonline.com/my tenant/',
    clientCertificate: {
      thumbprint: vault.azure_ad_cert_thumbprint,
      privateKey: Buffer.from(vault.azure_ad_decrypted_private_key_base64, 'base64').toString(),
    },
  },
  system: {
     //networkClient: proxyClient,
     proxyUrl: 'http://host:port'
  },
};

Relevant Code Snippets

//This is the implementation of the networkClient that works through the proxy without any issues

import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';

export const proxyUrl = 'http://host:port';
export const proxyAgent = new HttpsProxyAgent(proxyUrl);

export const proxyClient = {
  sendGetRequestAsync: async (url, options) => {
    const response = await fetch(url, { agent: proxyAgent, ...options });
    const json = await response.json();
    const headers = response.headers.raw();
    const obj = {
      headers: Object.create(Object.prototype, headers),
      body: json,
      status: response.status,
    };
    return obj;
  },
  sendPostRequestAsync: async (url, options) => {
    const sendingOptions = options || {};
    sendingOptions.method = 'post';
    const response = await fetch(url, { agent: proxyAgent, ...sendingOptions });
    const json = await response.json();
    const headers = response.headers.raw();
    const obj = {
      headers: Object.create(Object.prototype, headers),
      body: json,
      status: response.status,
    };
    return obj;
  },
};

Reproduction Steps

  1. Setup a proxy
  2. Attempt to use msal-node with proxyUrl

Expected Behavior

The expected behavior is the 200 Ok response and msal-node being able to fetch the token

Identity Provider

Azure AD / MSA

Browsers Affected (Select all that apply)

None (Server), Other

Regression

No response

Source

External (Customer)

@rasulsafa rasulsafa added bug-unconfirmed A reported bug that needs to be investigated and confirmed question Customer is asking for a clarification, use case or information. labels Sep 30, 2023
@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs: Attention 👋 Awaiting response from the MSAL.js team label Sep 30, 2023
@github-actions github-actions bot added confidential-client Issues regarding ConfidentialClientApplications msal-node Related to msal-node package labels Sep 30, 2023
@sameerag
Copy link
Member

sameerag commented Oct 2, 2023

cc @Robbie-Microsoft @bgavrilMS Can you folks check this?

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Author Feedback Awaiting response from issue author and removed Needs: Attention 👋 Awaiting response from the MSAL.js team labels Oct 2, 2023
@bgavrilMS
Copy link
Member

Hi @rasulsafa - if you just use https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/samples/msal-node-samples/custom-INetworkModule-and-network-tracing/HttpClientAxios.ts object (no MSAL), can you use it to make a call to https://login.microsoftonline.com/b458eb0a-178f-4197-8da4-514c0fe6f17b/v2.0/.well-known/openid-configuration ?

This extensibility point gives you full control over the HTTP stack, so it's up to you to set it in such a way as to take into account the proxy.

@bgavrilMS
Copy link
Member

Similar issue #6483

@rasulsafa
Copy link
Author

rasulsafa commented Oct 2, 2023

Hi @bgavrilMS, I was not able to get it working with an Axios-based custom networkClient. It appears proxy support on Axios is also currently bugged as well. I was however able to make it work with node-fetch and https-proxy-agent as outlined in my original message

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 👋 Awaiting response from the MSAL.js team and removed Needs: Author Feedback Awaiting response from issue author labels Oct 2, 2023
@bgavrilMS
Copy link
Member

bgavrilMS commented Oct 3, 2023

Thanks for the summary @rasulsafa. Much appreciated that you provided the implementation that works for you. Can you link the Graph SDK API on this please?

I will mark this as a bug then, to update the implementation to use to use fetch and https-proxy-agent as what we have is clearly not working.

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Author Feedback Awaiting response from issue author and removed Needs: Attention 👋 Awaiting response from the MSAL.js team labels Oct 3, 2023
@bgavrilMS bgavrilMS added bug A problem that needs to be fixed for the feature to function as intended. p2 P1 and P2 are priorities of the bug. P2 bugs should get fixed/closed within 3 months. and removed bug-unconfirmed A reported bug that needs to be investigated and confirmed question Customer is asking for a clarification, use case or information. Needs: Author Feedback Awaiting response from issue author labels Oct 3, 2023
@Robbie-Microsoft
Copy link
Collaborator

Hi @bgavrilMS, I was not able to get it working with an Axios-based custom networkClient. It appears proxy support on Axios is also currently bugged as well. I was however able to make it work with node-fetch and https-proxy-agent as outlined in my original message

What version of axios did you use when you tried this sample? It appears our samples use either 1.4.0 or 1.3.4. Other users have been able to get the axios implementation working before.

For insight, we switched to manually writing http requests through the socket because we didn't want to rely on 3rd party libraries. If it supports proxies, we would like to switch to node's native fetch as soon that moves out of the experimental phase.

@rasulsafa
Copy link
Author

rasulsafa commented Oct 4, 2023

@Robbie-Microsoft I had initially tried it with the latest version, 1.5.1. I just retested it with 1.4.0 and still get the endpoints resolution error. I understand not wanting to rely on third party libraries, but msal-node and the MS Graph Client having such disparate proxy support is pretty frustrating. The graph client as it is currently uses a fetch polyfill and is compatible with https-proxy-agent.

If there's unlikely to be any major change until Node's native fetch supports proxies, then could I suggest a happy medium in the meantime? Currently, msal-node doesn't support https-proxy-agent passed into customAgentOptions because in networkRequestViaHttps, the contents of customAgentOptions are presumed to be the options parameter of the native http(s).Agent class and passed into it's constructor, rather than being treated as an Agent object itself. https.request as you are using it, when used with https-proxy-agent, has complete proxy support. If I simply change customOptions.agent = new https.Agent(agentOptions); to customOptions.agent = agentOptions in HttpClient, and then in my config pass an https-proxy-agent object as such:

system: {
    customAgentOptions: new HttpsProxyAgent('http://proxy.XXXX.com:8080')
}

I'm able to successfully connect through the proxy. To preserve backwards compatibility, something like this can be done:

if(agentOptions.constructor && (agentOptions.constructor.prototype instanceof https.Agent || agentOptions.constructor.prorotype instanceof http.Agent))
    customOptions.agent = agentOptions;
else 
    customOptions.agent = https.Agent(agentOptions);

This way, we check if the end user passed in their own custom agent object, and if they did, we use that. If they just passed in a dictionary of options, we create the Agent object for them and use that. We check if it's either an http or https agent, because both are valid in https.request (https-proxy-agent for some reason is a child of http.Agent.) This ensures that anyone can use their own proxy agent, backwards compatibility is preserved, and msal-node would have relatively easy proxy support with no extra third parties. The burden of using a third party proxy agent library falls on us end users. Simply doing npm i https-proxy-agent and passing the object into customAgentOptions is much easier than the status quo.

@bgavrilMS
Copy link
Member

Agreed, let's analyze the stance of Azure SDK and Microsoft Graph SDK and consolidate.

@danielspatacean94
Copy link

Hello,

Any news regarding this issue?
We're facing it too.

Behind a corporate proxy, there's no way to connect, currently.

@Tcharl
Copy link

Tcharl commented Feb 7, 2024

Also, the current implementation does not supports authenticated pxy

@Robbie-Microsoft
Copy link
Collaborator

Robbie-Microsoft commented Feb 7, 2024

@bgavrilMS and I are still discussing this, but have other priorities right now. In the meantime, you all can implement your own custom network modules. See this sample.

Note: Node's native fetch api is now stable in node v21
Time permitting, I would like to rewrite the msal-node HttpClient to use this when Node v22 is released in April. I'm still waiting for the Node folks to add docs for fetch so I can learn about how proxies are implemented in the api.

Additionally, we are not actively supporting msal-node v1 anymore unless there is a critical bug that needs to be addressed.

@Robbie-Microsoft Robbie-Microsoft self-assigned this Feb 7, 2024
@Robbie-Microsoft Robbie-Microsoft linked a pull request Mar 27, 2024 that will close this issue
@bgavrilMS bgavrilMS added feature Feature requests. breaking change and removed bug A problem that needs to be fixed for the feature to function as intended. p2 P1 and P2 are priorities of the bug. P2 bugs should get fixed/closed within 3 months. labels Apr 3, 2024
@bgavrilMS
Copy link
Member

We will revisit when we move to use fetch with a newer version of nodejs / a major version bump of this lib. Keeping this as feature request. It's too difficult to test all these scenarios otherwise.

Solution for folks wanting to use a proxy is to provide their own INetworkModule.

@AlexVFornazieri
Copy link

AlexVFornazieri commented Apr 23, 2024

Working INetworkModule implementation with Proxy

TROUBLESHOOTING

  • Duble check if any other lib or own fuction is requesting direct to login.microsoftonline.com, then change it to use same aproach in code bellow with "https-proxy-agent" and "node-fetch";
  • Getting NS LOOKUP or ENOTFOUND errors? Duble check if you correctly import the 'node-fetch', the native fetch will not work with https-proxy-agent;
import type { INetworkModule, NetworkRequestOptions, NetworkResponse } from "@azure/msal-node";
import { HttpsProxyAgent } from "https-proxy-agent";
import fetch from 'node-fetch';

const proxyUrl = process.env['HTTPS_PROXY'] || process.env['HTTP_PROXY'];
if (!proxyUrl) {
    throw new Error('Missing HTTP/S_PROXY env');
}

export const proxyAgent = new HttpsProxyAgent(proxyUrl);

export class CustomHttpClient implements INetworkModule {
    sendGetRequestAsync<T>(url: string, options?: NetworkRequestOptions): Promise<NetworkResponse<T>> {
        return this.sendRequestAsync(url, 'GET', options);
    }
    sendPostRequestAsync<T>(url: string, options?: NetworkRequestOptions): Promise<NetworkResponse<T>> {
        return this.sendRequestAsync(url, 'POST', options);
    }

    private async sendRequestAsync<T>(
        url: string,
        method: 'GET' | 'POST',
        options: NetworkRequestOptions = {},
    ): Promise<NetworkResponse<T>> {
        try {
            const requestOptions = {
                method: method,
                headers: options.headers,
                body: method === 'POST' ? options.body : undefined,
                agent: proxyAgent,
            };

            console.log('>>> url', url, requestOptions);

            const response = await fetch(url, requestOptions);
            const data = await response.json() as any;

            const headersObj: Record<string, string> = {};
            response.headers.forEach((value, key) => {
                headersObj[key] = value;
            });

            return {
                headers: headersObj,
                body: data,
                status: response.status,
            };
        } catch (err) {
            console.error('CustomRequest', err);
            throw new Error('Custom request error');
        }
    }
}

@rasulsafa
Copy link
Author

Duble check if you correctly import the 'node-fetch', the native fetch will not work with https-proxy-agent;

On a related note, if you're using Node 18+, native fetch breaks node-fetch and necessitates the usage of --no-experimental-fetch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change confidential-client Issues regarding ConfidentialClientApplications feature Feature requests. msal-node Related to msal-node package
Projects
None yet
Development

No branches or pull requests

8 participants