http-proxy-middleware
http-proxy-middleware copied to clipboard
Api behind Basic authentication and NTLM authentication
Hi, I have api on iis server behind basic windows authentication and i cannot use cors. So I tried to use this module but however I configure it I cannot log into api and I get 401 every time
I tried
server.middleware = proxyMiddleware(
'/api',
{
target: 'API_HOST',
logLevel: 'debug'
}
);
server.middleware = proxyMiddleware(
'/api',
{
target: 'API_HOST',
logLevel: 'debug',
auth: 'LOGIN:PASS'
}
);
server.middleware = proxyMiddleware(
'/api',
{
target: 'http://LOGIN:PASS@API_HOST',
logLevel: 'debug'
}
);
Hi, I haven't used the auth
option personally yet.
the auth
option is provided by the underlying http-proxy library.
Did a search in their https://github.com/nodejitsu/node-http-proxy/tree/master/examples, but I could not find any examples how to use the auth
option.
But I did found a unit test: https://github.com/nodejitsu/node-http-proxy/blob/302d981dd2cf06dbf751b1f64e3dfea08d0f9476/test/lib-http-proxy-passes-web-incoming-test.js#L303
Their unit test looks like the 2nd case you've provided.
Is 'API_HOST' really your target
?
the target
should include the http:
or https:
protocol
You could try setting changeOrigin
to true
and see if that helps.
server.middleware = proxyMiddleware(
'/api',
{
target: 'API_HOST',
logLevel: 'debug',
auth: 'LOGIN:PASS',
changeOrigin: true
}
);
Yep API HOST is really my target, when i go to localhost:3000/api uri directly in the browser i get 401 error page and requests are forwarded to correct url but i cannot authorize. i get something in header like:
connection:close
content-type:text/html
date:Wed, 25 Nov 2015 06:53:58 GMT
server:Microsoft-IIS/7.5
Transfer-Encoding:chunked
www-authenticate:Negotiate, NTLM
x-powered-by:ASP.NET
from browse ajax request:
access-control-allow-credentials:true
access-control-allow-headers:Origin,X-Requested-With,Content-Type,Accept
access-control-allow-methods:GET,HEAD,POST,PUT,DELETE,OPTIONS
access-control-allow-origin:http://localhost:3000
connection:close
content-length:1293
content-type:text/html
date:Wed, 25 Nov 2015 07:00:03 GMT
server:Microsoft-IIS/7.5
www-authenticate:Negotiate, NTLM
x-powered-by:ASP.NET
http-proxy-middleware
uses http-proxy
to do the actual proxy work.
This is what I found in their NTLM authentication example: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/http/ntlm-authentication.js
You'll have to modify the headers.
The http-proxy
proxyRes
event is exposed via the http-proxy-middlware
option: onProxyRes
.
Example: https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/onProxyRes.md
If I combine the two examples you'll get this config:
var proxyMiddleware = require("http-proxy-middleware");
var onProxyRes = function (proxyRes, req, res) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
};
var options = {
target: 'API_HOST',
logLevel: 'debug',
auth: 'LOGIN:PASS',
onProxyRes: onProxyRes
};
var proxy = proxyMiddleware('/api', options);
Let me know if this works.
If it doesn't, you might want to ask the question at: https://github.com/nodejitsu/node-http-proxy/issues; since the authentication part is handled by the http-proxy
library.
I can see some progress for
var onProxyRes = function (proxyRes, req, res) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
};
server.middleware = proxyMiddleware(
'/api',
{
target: 'API_HOST',
logLevel: 'debug',
auth: 'login:pass',
onProxyRes: onProxyRes
}
);
right now on going on localhost:3000/api is infinite authorization popup, i will ask on the other project as You suggest
That sounds promising.
I noticed the usage of agentkeepalive
in their example;
Did a search on the nuts and bolts of the NTLM Authentication Scheme: ~http://www.innovation.ch/personal/ronald/ntlm.html~ https://web.archive.org/web/20210126065105/http://www.innovation.ch/personal/ronald/ntlm.html
NTLM Authentication Scheme for HTTP
Keeping the connection alive
This scheme authenticates connections, not requests. This manifests itself in that the network connection must be kept alive during the second part of the handshake
This explains why 'keep alive' is needed.
Updated configuration:
var Agent = require('agentkeepalive');
var proxyMiddleware = require("http-proxy-middleware");
var keepaliveAgent = new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:1000,
timeout: 60000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
});
var onProxyRes = function (proxyRes, req, res) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
};
var options = {
target: 'API_HOST',
logLevel: 'debug',
auth: 'LOGIN:PASS',
agent: keepaliveAgent,
onProxyRes: onProxyRes
};
var proxy = proxyMiddleware('/api', options);
@zychj1 any progress on this issue?
@chimurai I run in exactly the same problem and I can confirm that the configuration provided in your comment from Nov 26, 2015 (with keepalive and header rewriting) is solving the issue. Many thanks!
@pierreconstantial Glad to hear the example NTLM configuration solves the issue. Thanks for the confirmation!
hey @chimurai i'm having the same problem discussed on nodejs/node#8004,
I've tried to use the same configuration as here and also use the simple Agent
with keepAlive option of node's http
module.
I'm using webpack-dev-server
with the proxy property and having a WebApi server with NTLM authentication.
Thanks ahead for your help!
@EladBezalel can you provide a minimal setup in which the problem is exposed?
In my webpack.js:
{
devServer: {
'/api/*': {
target: 'http://localhost:12121',
logLevel: 'debug',
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:1000,
timeout: 60000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
}),
onProxyRes: proxyRes => {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
}
}
Yes I am
Not sure what causes the issue; Kinda turns into a guessing game... Can you provide a minimal working example?
I'll try to have it tomorrow, I also tried it without webpack-dev-server
just simple express and the middleware and got the same error..
Here it is: https://ufile.io/c3215 Three zips in there -
- proxy - the express server using http-proxy-middleware
- owin - source code of the c# console application using Owin that answers any request with status 200
- Debug - the exe of the owin server (listening in port 12345)
Run the owin server exe, and on your browser go to http://localhost:12345/api/whatever
and see that you get a page with {"status":"ok"}
in it,
now go to http://localhost:3000/api/whatever
and see how the proxy server dies with the same error from above..
thanks!
Thanks for the effort! This is what I see when I turn on the node debug logging:
$ NODE_DEBUG=http,net node index.js
[HPM] Proxy created: / -> http://localhost:12345
[HPM] Subscribed to http-proxy events: [ 'proxyRes', 'error', 'close' ]
NET 4776: listen2 null 3000 4 false undefined
NET 4776: _listen2: create a handle
NET 4776: bind to ::
NET 4776: onconnection
NET 4776: _read
NET 4776: Socket._read readStart
HTTP 4776: SERVER new http connection
[HPM] GET /api/whatever -> http://localhost:12345
NET 4776: createConnection [ { keepAliveTimeout: 30000,
timeout: 60000,
keepAliveMsecs: 1000,
maxFreeSockets: 10,
keepAlive: true,
maxSockets: 100,
path: null,
localAddress: undefined,
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 4,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: true,
keepAliveTimeout: 30000,
timeout: 60000,
maxSockets: 100,
maxFreeSockets: 10,
createSocketCount: 0,
closeSocketCount: 0,
errorSocketCount: 0,
requestCount: 0,
timeoutSocketCount: 0 },
headers:
{ 'accept-language': 'en-US,en;q=0.8,nl;q=0.6',
'accept-encoding': 'gzip, deflate, sdch',
dnt: '1',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36',
'upgrade-insecure-requests': '1',
'cache-control': 'max-age=0',
connection: 'keep-alive',
host: 'localhost:3000' },
method: 'GET',
secureProtocol: undefined,
ciphers: undefined,
ca: undefined,
cert: undefined,
passphrase: undefined,
key: undefined,
pfx: undefined,
socketPath: undefined,
hostname: 'localhost',
host: 'localhost',
port: '12345',
servername: 'localhost',
encoding: null } ]
NET 4776: pipe false null
NET 4776: connect: find host localhost
NET 4776: connect: dns options { family: undefined, hints: 1024 }
HTTP 4776: SERVER socketOnParserExecute 426
NET 4776: _read
NET 4776: _read wait for connection
HTTP 4776: outgoing message end.
NET 4776: afterConnect
NET 4776: _read
NET 4776: Socket._read readStart
NET 4776: onread 140
NET 4776: got data
HTTP 4776: AGENT incoming response!
HTTP 4776: AGENT isHeadResponse false
NET 4776: _read
HTTP 4776: AGENT socket keep-alive
HTTP 4776: outgoing message end.
[HPM] GET /api/whatever -> http://localhost:12345
HTTP 4776: SERVER socketOnParserExecute 524
_stream_writable.js:361
cb();
^
TypeError: cb is not a function
at afterWrite (_stream_writable.js:361:3)
at onwrite (_stream_writable.js:352:7)
at WritableState.onwrite (_stream_writable.js:89:5)
at Socket._writeGeneric (net.js:714:5)
at Socket._write (net.js:724:8)
at doWrite (_stream_writable.js:307:12)
at writeOrBuffer (_stream_writable.js:293:5)
at Socket.Writable.write (_stream_writable.js:220:11)
at Socket.write (net.js:650:40)
at ClientRequest.OutgoingMessage._writeRaw (_http_outgoing.js:167:23)
This might be a Node issue.
Found this thread: https://github.com/nodejs/node/issues/8650
Think your isolated example might be really helpful to solve the issue.
Hey @chimurai , I used you settings as described above but still stuck on the infinite authentication popup before those settings it was 401.
my web api authentication is as follow: <authentication mode="Windows"/>
devServer: {
port: METADATA.port,
host: METADATA.host,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
},
proxy: {
'/api': {
target: 'http://localhost:4640/',
logLevel: 'debug',
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:1000,
timeout: 60000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
}),
onProxyRes: proxyRes => {
console.log("onProxyRes event", proxyRes);
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
}
}
Reopening this issue, since multiple users are facing this issue. Hopefully someone more knowledgeable on Node + NTLM can help you guys out.
Thank you @chimurai!
@chimurai , I tried to investigate the issue. So first i created small node server with node-http-proxy:
Agent = new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:100000,
timeout: 6000000,
keepAliveTimeout: 90000 // free socket keepalive for 90 seconds
});
var proxy = httpProxy.createProxy({ target: 'http://localhost:4640', agent: Agent});
// Modify headers of the request before it gets sent
proxy.on('proxyRes', function (proxyRes) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
});
require('http').createServer(function (req, res) {
proxy.web(req, res);
}).listen(4000);
every thing works fine with those settings.
when i tried to do the same just with http-proxy-middleware and express server i got the error @EladBezalel got (cb).
So in the end i found a workaround:
in my webpack settings i created the small server mentiond above (based on node-http-proxy),
The webpack config should be the same but now we are forwarding the requests to the small proxy and the small proxy forward it to our IIS
webpack config:
devServer: {
port: METADATA.port,
host: METADATA.host,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
},
proxy: {
'/api': {
target: 'http://localhost:4000/',
logLevel: 'debug',
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:1000,
timeout: 60000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
}),
onProxyRes: proxyRes => {
console.log("onProxyRes event", proxyRes);
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
}
}
The target in webpack config is localhost:4000 (the small node server with http-proxy) The target in the small proxy server is localhost:4640 (my IIS server)
Now it works, hope it will help till the problem will be solved
@oshri551 it's still not working for me 😕 Care sharing minimal code?
@EladBezalel I've pushed to my githab repo the code example based on AngularClass webpack starter.
Please look at http-proxy.js and webpack.dev.js under config folder.
**Don't forget we need to install http-proxy and agentkeepalive in our dev dependencies **
keep me updated if it works...
I tried doing what @oshri551 shared and on Chrome I keep getting constant pop ups asking for user name and password, once entered it just prompts over and over again. However, in Edge I get the prompt message to enter credentials and once I enter them it works. I am using the Angular CLI in the project, and it would increase my development time by leaps and bounds because right now I am having to compile every time and dump code in wwwroot of asp.net core project.
Re-sharing @EladBezalel's minimal example: c3215-untitled.gz. source: https://github.com/chimurai/http-proxy-middleware/issues/39#issuecomment-260965382
Hoping someone can figure this one out.
Confirmed vanilla implementation works fine. https://github.com/chimurai/http-proxy-middleware/issues/39#issuecomment-269920667
var httpProxy = require('http-proxy');
var Agent = require('agentkeepalive');
var agent = new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:100000,
timeout: 6000000,
keepAliveTimeout: 90000 // free socket keepalive for 90 seconds
});
var proxy = httpProxy.createProxy({ target: 'http://localhost:12345', agent: agent});
// Modify headers of the request before it gets sent
proxy.on('proxyRes', function (proxyRes) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
});
require('http').createServer(function (req, res) {
proxy.web(req, res);
}).listen(4000);
Also express + http-proxy works
var express = require('express');
var httpProxy = require('http-proxy');
var Agent = require('agentkeepalive');
var agent = new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:100000,
timeout: 6000000,
keepAliveTimeout: 90000 // free socket keepalive for 90 seconds
});
var proxy = httpProxy.createProxy({ target: 'http://localhost:12345', agent: agent});
// Modify headers of the request before it gets sent
proxy.on('proxyRes', function (proxyRes) {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
});
var app = express();
app.use(function (req, res) {
proxy.web(req, res);
});
app.listen(4000);
Issue is likely in http-proxy-middleware. I'll have to investigate it further.
Just published v0.17.4
with a fix to npm.
@EladBezalel @oshri551 @bac42x can you please verify?
@chimurai, v0.17.4 fixed it for me! Thank you very much! I have been completely unsuccessful getting OWIN/Katana to handle CORS preflight after many hours of work. This is a lifesaver!
This works! Thank you @chimurai. I was able to wire up my Asp.net Core Web API's back end.
Glad to hear the fix works! :)
@chimurai, I tested it with express and it works ! ... but with webpack-dev-server it is still on the authentication popup loop. @Alex-Torres , @bac42x ... are you working with webpack-dev-server ?
@oshri551 webpack-dev-server is not updated yet, i made a pr to update this dependency, hope it'd be in soon.
@chimurai thanks for helping! I'll check it back tomorrow
@EladBezalel Take a look at webpack-dev-server package.json
This is the "http-proxy-middleware": "~0.17.1"
dependency so if you update the package it will choose 0.17.4 because of the tilde symbol.
So i already made the update and checked that the node_module contains the latest http-proxy-middleware version...
I will try to look further and find out why i still get the authentication popup loop.
keep me updated if it works for you...
@oshri551 When the major version is 0, it doesn't automatically upgrade patches, simply because it's considered pre-release and can break at any time.. I haven't got time to check it out, but i plan doing it this week so i'd keep you posted
@oshri551 @EladBezalel @chimurai
Also not working here, getting infinite popup loop.
I can also confirm that my http-proxy-middleware is at version 0.17.4 and using webpack-dev-server 2.4.1
Webpack config
proxy: {
"/api": {
target: PROXYTARGET,
changeOrigin: true,
logLevel: 'debug',
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs: 1000,
timeout: 60000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
}),
onProxyRes: proxyRes => {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
}
It's working! Thank you very much @chimurai
Hello,
@chimurai I keep having the authentication popup on my side. I fecthed the last version of webpack-dev-server.
I managed to create a little step by step process to reproduce the issue. I pushed it on a github project here: https://github.com/charleshetier/bench.ntlm.webpackserver
The readme contains the information on how to have the authentication popup. It may appear very weird but this is the only way I found to have this unwanted authentication popup almost for sure.
my agent (keepaliveagent) is setup this way:
return new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 1024,
keepAliveMsecs:1000,
timeout: 10000,
keepAliveTimeout: 5000
});
from the shrinkwrap, the http-proxy-middleware version is: 0.17.4
The server works only on windows...
Am I doing something wrong here?... Thanks a lot
Hello, Is there any known solution for windows authentication when using webpack dev server? I have tried to configure it in the same way as proposed earlier in this thread, but I still keep having the authentication popup.
@Hammer82, I was only able to solve it by modifying the server. In my case, this meant intercepting the OPTIONS request in our OWIN WebApi service by using the HttpListener.AuthenticationSchemeSelectorDelegate. I could not find a way to solve it from the client.
Hello, Any news about webpack-dev-server support yet ?
I think I found a solution to the infinite popups, I simply changed the maxSockets to 1. Probably not the best solution but it seems to work.
My Agent is set up this way:
return new Agent({
maxSockets: 1,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs:100000,
timeout: 600000,
keepAliveTimeout: 90000
});
Hope that helps!
Hello, Any news about webpack-dev-server support yet ?
@bjg96 Maybe it solves the problem in your specific context and this could be a clue to understand why it doesn't work in some specific cases. However as far as I understood the way the agent works, with maxsockets to 1, you won't be able to handle simultaneous requests. I mean the popup will appear if you are making 2 ajax requests at the same time for example
I havet still not found any solution for my implementation, so I decided for the moment to not use Windows authentication combined with webpack dev server.
Reopening issue since not all NTLM issues seem to be resolved.
@charleshetier Would be great if you can reproduce the issue in a vanilla setup. (Example: https://github.com/chimurai/http-proxy-middleware/issues/39#issuecomment-272003173)
Curious if the issue persists in your demo setup: https://github.com/charleshetier/bench.ntlm.webpackserver
My recommendation is that you replace webpack-dev-server with express, webpack-hot-middleware and webpack-dev-middleware. I am able to hit my secured API endpoints with the express proxy and then serve my React app with the other two middleware packages. It requires a bit more configuration, but it works really well for me.
This is my server.js configuration:
var webpack = require("webpack");
var Agent = require("agentkeepalive");
//var WebpackDevServer = require('webpack-dev-server');
var config = require("./webpack.config.js")({});
var https = require("https");
var proxy = require('http-proxy-middleware');
const express = require("express");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
var app = express();
var compiler = webpack(config);
app.use('/api', proxy(
{
target: 'http://localhost:58065/',
changeOrigin: true,
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs: 100000,
timeout: 6000000,
keepAliveTimeout: 90000 // free socket keepalive for 90 seconds
}),
onProxyRes: onProxyRes = (proxyRes) => {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
));
app.use(webpackDevMiddleware(compiler,
{
hot: true,
historyApiFallback: true,
contentBase: config.output.path,
publicPath: config.output.publicPath,
headers: { 'Access-Control-Allow-Origin': '*' }
}));
app.use(webpackHotMiddleware(compiler, {}));
app.listen(7071, 'localhost', function (err, result) {
if (err) {
return console.log(err);
}
console.log('Webpack Dev Server is fired up!!');
});
Just realized this may not help with Basic authentication or anything, but I couldn't even get Windows Integrated authentication working with webpack-dev-server.
@jndietz Can you please confirm if its working with windows integrated authentication ??
It is working for me. I can try to put together a demo project that utilizes all the libraries needed.
@imsontosh Here is a small project that utilizes express, webpack-dev-middleware, http-proxy-middleware
https://github.com/jndietz/csharp-windows-auth
Let me know if you are wanting to know something specific and I'll try to help out.
@jndietz Thanks for your effort. Working like charm. Cheers 👍
@jndietz, thanks for creating the example.
I noticed an interesting behavior running it. When a request takes a long time to run - it forces a user to enter credentials every time it receives a subsequent request. I basically experience the same issue running angular-cli proxy which uses webpack with http-proxy-middleware.
It is easy to reproduce if you put Thread.Sleep(TimeSpan.FromSeconds(5));
in the controller action and try to trigger an API action several times.
Do you have any ideas why it could be happening and how could be resolved?
CC @chimurai @imsontosh
thank you everyone for chiming in on this, I realize this is a closed issue but this helped solve an auth/CORS issue I had when trying to hit a MVC .NET 4.6 WebApi app which uses "Windows Authenticaton" and I couldn't get it to work during development for two reasons (CORS and NTLM not working well with axios).
My code for the proxy middleware is this:
import proxyMiddleware from 'http-proxy-middleware'
import Agent from 'agentkeepalive'
const bundler = webpack(config)
let middleware = [
proxyMiddleware('/api', {
changeOrigin: true,
target: 'http://codefest.example.gov/Team7',
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs: 100000,
timeout: 6000000,
keepAliveTimeout: 90000 // free socket keepalive for 90 seconds
}),
onProxyRes: (proxyRes) => {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
})
// ... removed for brevity
];
// ... removed for brevity
browserSync({
port: 3000,
ui: {
port: 3001
},
open: false,
server: {
baseDir: 'src',
middleware
}
});
The ending result was webpack/browserSync serving up my ReactJs UI at http://localhost:3000/
and during dev it would hit http://localhost:3000/api/values
browserSync would intercept the /api/values
and proxy it to the backend WebApi at http://codefest.example.gov/Team7/api/values
. This works well, thanks again for all of your inputs.
I just wanted to confirm that I'm still seeing an infinite series of pop-ups requesting authentication after trying various suggestions in this thread and ensuring that my dependencies include the fixes mentioned throughout.
I'm a little confused whether this issue is considered resolvable or not. As of now, has anyone successfully built a solution utilizing Webpack/http-proxy-middleware with NTLM authentication?
I still have this issue as well and have been monitoring to see if/when it gets resolved. IE11 seems to accept credentials from a single popup. No other browser does.
@xcjs , The solution from @jndietz worked for me while all the other solutions attempting to use webpack-dev-server caused me to get never ending authentication prompts in both Chrome and Firefox.
The authentication prompt still appears sometimes; it seems to be while Chrome dev tools are open and I try a full refresh. But it works pretty well.
@mike-schenk The solution from @jndietz unfortunately didn't have the same effect for me. I'm not expecting the prompt to never show, but I can't access the page due to the stream of authentication prompts.
@jalaz @xcjs Hey guys -- sorry that wasn't working for you. I will need to dive a bit deeper into it and see what exactly is going on. Currently stuck working on setting up an angular 1 application to be built with webpack so I might be able to take a look at it soon.
@jalaz @xcjs
I'm able to reproduce the issue in my project. I must not have seen that happen back when I made it.
With our Angular 1/Webpack config, we are going to be putting our UI code in with our web layer so we don't have any issues. If we weren't using Windows integrated authentication I don't suspect this would be as big of an issue. The alternative would be configuring the proxy as stated above, but then development activities would have to take place in Internet Explorer/Edge only and dealing with the username/password dialog from time to time.
The former was a better compromise for us. I still want to figure out a resolution for this :)
same here. I switched back from gulp to grunt because of this. btw. (grunt-connect-proxy) is doing it correct.
Update for BASIC authentication:
i figured out that it is necessary to forward explizit the authorization header. In case you want to enter the password over browser buildin popup: this should work.
proxy('/sap', {
target: 'http://' + configBuild.sap.server + ':' + configBuild.sap.port,
changeOrigin: true,
onProxyRes: function (proxyRes, req, res) {
if (proxyRes.headers['authorization']){
proxyRes.headers['authorization'] = req.headers['authorization'];
}
}
}
But i'm still looking for a solution in CL(Karma) environment.
Thank you after few hours of hair pulling .. this link resolved my issue
Here is my working configuration with Vue-cli 3 / .NET WebAPI
Install package
npm install agentkeepalive --save
Update vue.config.js
const Agent = require('agentkeepalive');
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost',
changeOrigin: true,
agent: new Agent({
maxSockets: 100,
keepAlive: true,
maxFreeSockets: 10,
keepAliveMsecs: 1000,
timeout: 60000,
freeSocketTimeout: 30000
}),
onProxyRes: proxyRes => {
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
}
}
}
}
}
The above configuration (and some variations messing with the numbers) partially works for me. When I hit my home page, which makes 3 API calls, it usually works, but sometimes, I'll get another auth prompt for one of the API calls, but not the others. When I navigate to another route on the site, I get the never-ending prompts for the new route's API calls.
I'm using Chrome and Vue UI , and the proxy is connecting to IIS Express on Windows 7 using NTLM authentication. Going directly to the site on IIS Express always works fine, but that doesn't give me Vue hot-reload.
I have been using the above configuration as well and worked almost flawlessly (except sometimes a logging prompt which could be solved by restarting the dev server). Since I upgraded my project to the latest version of Angular and Angular CLI, the solution is not working anymore. A bug has been opened here but I think the issue lies int http-proxy-middleware. [email protected] is not working with the above solution [email protected] is working
I have the exact same issue as @richardtallent, it works very intermittently, sometimes even perfectly for hours at a time and then will randomly start hitting me with multiple login pop-ups that don't seem to care whether I enter my credentials correctly or not.
Sometimes restarting my local web server fixes it, sometimes it doesn't. Rebuilding the project also sometimes fixes it.
I've played around with the agentkeepalive settings alot to no success (I have no idea what these settings actually do, and there's little explanation).
Will just have to go without hot reload for now
@richardtallent, @Perogy, I'm having this issue too. I've been dealing with it for about 3 months and I've been trying to find a pattern to it and haven't been able to come up with anything tangible. I've tried everything recommended here and spent quite a few hours digging into the requests myself. I'm using it to proxy calls through my dev server to our backend API to bypass CORS in development since CORS isn't required by our prod configuration. For some reason the last few days have been pretty unbearable, probably because we recently moved our API to the cloud.
Bump for the last four comments. I'm in the same boat. The suggestions here work sometimes, but sometimes the connection still putters out and starts throwing authentication prompts. I think it has something to do with keeping the authenticated TCP connection open. But my knowledge of server protocols is pretty limited.
I've had pretty good success with the agentkeepalive module, though for some reason it does NOT like signalr. If I try to route signalr calls through the proxy it just seems to break all the other calls and I wind up with the infinite login prompt. Maybe its an issue of websockets+ntlm?
I have the same issue as @richardtallent and @Perogy. I am temporarily using the work around that restricts the maxSockets to 1, which seems to minimize the frequency of the issue but am wondering if there is any update on a resolution?
I tried this instead of just fixing the www-authenticate header. it worked intermittently like once it work and 10 times it wont :(
proxy.on('proxyRes', function (proxyRes, req, res) {
// var key = 'www-authenticate';
// proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
var headersFirstKey = {};
var headers = {};
for(var i = 0; i < proxyRes.rawHeaders.length; i += 2) {
var headerKey = proxyRes.rawHeaders[i];
var headerValue = proxyRes.rawHeaders[i + 1];
var headerKeyLower = headerKey.toLowerCase().trim();
headerKey = headersFirstKey[headerKeyLower] || headerKey;
headersFirstKey[headerKeyLower] = headerKey;
headers[headerKey] = headers[headerKey] ? headers[headerKey] : [];
headers[headerKey].push(headerValue)
}
for(var j in headers) {
proxyRes.headers[j] = headers[j];
}
});
also tried:
require('https').createServer({
key: fs.readFileSync('key.key', 'utf8'),
cert: fs.readFileSync('crt.crt', 'utf8'),
passphrase: 'test',
rejectUnauthorized: false,
handshakeTimeout: 100000,
insecureHTTPParser: true, // maybe :|
maxHeaderSize: 10000000
},
function (req, res) {
proxy.web(req, res);
})
.on('secureConnection', (socket) => { // to keep connection open with client
socket.setKeepAlive(true, 10000).setTimeout(60000);
})
.listen(443);
Do not use the simple agentkeepalive use instead agentkeepalive-ntlm
https://github.com/pappan123/agentkeepalive-ntlm
Here is a quick description explaining the issue:
The main motivation for this fork from agentkeepalive was to support user specific NTLM sessions. For NTLM to work, the TCP connection has to be authorized for a user. When we use the base 'agentkeepalive', sockets are authorized using a combination of the host + port. eg yahoo.com:443. However if multiple users are trying to access a NTLM enabled site, the socket connections were getting mixed up between users. Changed the implementation to include 'host+port+cookieName' while assigning sockets
If you use the code shown by mikedevita it must work.
Here is my working configuration with Vue-cli 3 / .NET WebAPI
Install package
npm install agentkeepalive --save
Update vue.config.js
const Agent = require('agentkeepalive'); module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost', changeOrigin: true, agent: new Agent({ maxSockets: 100, keepAlive: true, maxFreeSockets: 10, keepAliveMsecs: 1000, timeout: 60000, freeSocketTimeout: 30000 }), onProxyRes: proxyRes => { var key = 'www-authenticate'; proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(','); } } } } }
This worked ! You are a gem :) <3 I was having CORS issues with .net core back end API where only windows auth could be enabled and no anonymous auth , this was causing preflight error. Your solution works perfectly with the proxy server.
Here is my working configuration with Vue-cli 3 / .NET WebAPI
Install package
npm install agentkeepalive --save
Update vue.config.js
const Agent = require('agentkeepalive'); module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost', changeOrigin: true, agent: new Agent({ maxSockets: 100, keepAlive: true, maxFreeSockets: 10, keepAliveMsecs: 1000, timeout: 60000, freeSocketTimeout: 30000 }), onProxyRes: proxyRes => { var key = 'www-authenticate'; proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(','); } } } } }
I had an issue with NTLM authentication when subsequent auth requests failed (usually after reloading the page). The issue turned out to be that Agent performed NTLM auth requests with different connections from the pool.
Changing maxFreeSockets to 1 solved the issue.
Hello -- I have information on what is causing this issue. My background isn't in the JavaScript/node world, meaning I don't have the relevant experience to go it alone. So if someone wants to chat about this issue. I'd love to tackle it together.
In short, there seems to be two issues. The first solved by
var key = 'www-authenticate';
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
Is that the proxy was collapsing the www-authenticate
headers into one line. Something like
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
into
WWW-Authenticate: Negotiate, NTLM
Seemingly, the browser will not begin the NTLM negotiation when it's collapsed. Even though, I think that's okay as per the standard. But the browser (I was using chrome) doesn't like it and won't reply to the challenge.
The problem with the infinite authentication dialogs comes from the real problem. Which is that NTLM negotiation requires a single TCP connection which is kept alive. The NTLM negotiation happens in 3 parts. The original request, the first challenge, and a second challenge. After the TCP socket is authenticated, as long as it stays alive each successive request will work without auth.
When we're proxying, there doesn't seem to be a connection between the incoming (from the browser), and the outgoing (to the IIS server). Why is this a problem? Because the 3 part handshake which happens in 3 separate requests through the proxy, can happen on 3 different outgoing sockets to the server. So the browser, and the server both get confused.
In order to mitigate this, I was able to set the outgoing agent configuration to use a single socket maxSockets : 1
, and keep alive to true, with a long timeout keepAlive : true, timeout : 999999
. This tells the http.Agent
to keep the outgoing sockets alive, for a long time. The maxSockets, tells it to only use one outgoing connection. This works, for the most part. However, it's slow, and it's not quite right. Because if you look at an actual interaction between the browser and IIS server, it'll use the same socket for multiple requests. However, if you wireshark between the browser and the proxy, and the proxy and the server. You see multiple requests on several incoming sockets from the browser, and they all share the single outgoing socket (which is what makes this work) to the server.
Ideally, with NTLM the proxy should maintain the browser connection one-to-one, with the server.
Hopefully this makes sense. I empathize with everyone in this thread who's been having this issue.