All You Need to Know About Angular Proxy Configuration
A complete guide on setting up a proxy in Angular for API calls behind a corporate proxy or with windows authentication
Angular Developers use Angular CLI for local development. One of the most common needs is to set up a proxy in the local dev environment to avoid CORS error when the Angular app sends an HTTP request to an API residing on a different domain. Angular CLI makes the process relatively easy but can be tricky in some edge cases.In this article, I am going to discuss how to set up a dev server proxy for
- proxy to API in localhost
- proxy to external API within a corporate network
- proxy to API using Windows authentication (IIS)
- multiple schemes in one WWW-Authenticate header
Setup a proxy to connect to API in localhost
We can start the local dev server using the command below with Angular CLI.
1 | ng serve |
The command invokes the internal dev server based on webpack dev server . By default, the dev server runs on [http://localhost:4200](http://localhost:4200./)
.
When the Angular app needs to call its backend API, which is also hosted locally at [http://localhost:3000](http://localhost:3000./),
then we will encounter a CORS error because the HTTP call uses a different origin (localhost:3000
).
1 | this.http.get('http://locahost:3000/api/') |
The CORS issue can be resolved by configuring the Angular dev server proxy. A sample proxy config can be created below
1 | // proxy.conf.json |
As illustrated by the following diagram, the proxy sits between the Angular app and the backend API and translates the “api/v1
” calls to backend API. The CORS error doesn’t happen because the call to API is the same origin ([localhost:4200](http://localhost:4200./)/api
) now.
To make the proxy configuration take effect, it must be passed into the ng serve
command.
1 | ng serve --proxy-config proxy.conf.json |
Connect to external API behind corporate Proxy
Often, we worked within a corporate network, and the Angular app in a local dev environment also needed to connect to an external API. Using the previous example, we might need to call http://abc.company.com/api in the Angular app instead of calling http://localhost:3000/api.
Accessing an external API behind a corporate proxy requires HTTP_PROXY
and HTTPS_PROXY
environment variables to be configured. If the proxy utilizes an SSL certificate, the secure
flag must be false to bypass the certificate verification.
To handle corporate proxy, we need to create a proxy.conf.js
as below.
1 | const HttpsProxyAgent = require('https-proxy-agent'); |
In the above example, we proxy a request like api/v1/client
to an external server https://api.abc.com/v1/client
. When a corporate proxy is required, we set up an HTTPS agent object in the proxy based on HTTP_PROXY
and HTTPS_PROXY
environment variables. The secure:false
option is added to handle the custom SSL certificate in the corporate proxy.
To use the new js configuration for the Angular app, run the following
1 | ng serve --proxy-config proxy.conf.js |
It is worth noting that there are two types of agents: HttpsProxyAgent
and HttpProxyAgent,
it is necessary to choose the appropriate one based on the environment setting.
Proxy to API using Windows authentication (IIS)
Many companies rely on the Microsoft ecosystem, and Windows authentication is widely used. However, the situation can be tricky if the Angular App connects to an API service hosted with IIS protected by Windows authentication.
The typical problem is that the call from the Angular app to the API will return 401 when using the proxy setting in the local dev environment.
For example, /api/v1/../login
is an Endpoint protected by Windows Authentication, the request to the API from a locally running Angular App receives 401 unauthorized responses. Below is the screenshot of the network tab in Chrome dev tools.
failed request
The root cause of the issue is that Windows authentication is connection-based, but the Proxy breaks the keep-live connection.
Under the hood of Windows authentication, it uses either Kerberos or NTLM; either protocol will require a keep-live connection to keep the authentication state.
When the /api/v1/../login
is called, the browser tries to establish a connection with the IIS server via NTLM negotiation handshake, which includes three parts. They are the Type-1 message, the Type-2, and the Type-3 message. You can find more details of the NTLM handshake here . You may already notice two HTTP calls shown for the same request in the above screenshot. They are the first two parts of the handshake.
Because the request is proxying locally, the three handshake messages were sent in 3 separate requests(sockets) through the proxy. Thus, a keep-live connection can’t be kept in the process, so the last message didn’t occur.
To fix the issue, We need to configure the proxy to maintain a single connection between the browser and IIS server. The[agentkeepalive](https://github.com/node-modules/agentkeepalive#readme)
package can help us to achieve this goal. The updated proxy configuration is shown below.
1 | const Agent = require("agentkeepalive"); |
The updated proxy config set the maxSockets
to 1, keepAlive
flag to true and set the timeout to 30 seconds, which is long enough to complete the handshake. This config aims to make http.Agent
to maintain a keep-live connection between the browser and IIS server via proxy in the authentication process.
Now, the /api/v1/../login
API request should work.
success request
The network tab logs above after successful authentication with the new config. We can see the three requests during the handshake, and the last one returns HTTP 200 success status.
Multiple schemes in one WWW-Authenticate header
Another possible cause of the error from Windows authentication is the www-authenticate
header. According to RFC 7235, it is okay to have multiple authentication schemes in one www-authenticate
header field, although it can make the field difficult to be parsed.
agents will need to take special care in parsing the WWW-
Authenticate or Proxy-Authenticate header field value if it contains
more than one challenge, or if more than one WWW-Authenticate header
field is provided, since the contents of a challenge may itself
contain a comma-separated list of authentication parameters.
The reality is that the browser support is questionable . Below is an example of the www-authenticate header with two schemes.
WWW-Authenticate: Negotiate, NTLM
Some browsers may not be able to parse the above correctly, and this will break the NTLM handshake process. To resolve this issue, we can utilize the proxyRes
callback in the [http-proxy-middleware](https://www.npmjs.com/package/http-proxy-middleware)
as below
1 | const onProxyRes = function (proxyRes, req, res) { |
The full proxy configuration looks like below.
1 | const Agent = require("agentkeepalive"); |
With the new callback added, the multiple schemes in the www-authenticate
header will be sent in multiple lines, and the NTLM negotiation handshake can continue.
1 | // From the original response header |
Summary
This article discusses how to avoid CORS issues by setting up a proxy using Angular CLI for local development. I hope it is useful if you’re working on Angular behind a corporate proxy and/or using IIS with Windows authentication.
Happy programming!
- Title: All You Need to Know About Angular Proxy Configuration
- Author: Sunny Sun
- Created at : 2023-03-05 11:14:56
- Updated at : 2024-07-07 14:23:21
- Link: http://coffeethinkcode.com/2023/03/05/all-you-need-to-know-about-angular-proxy-configuration/
- License: This work is licensed under CC BY-NC-SA 4.0.