TooTallNate / proxy-agents

Node.js HTTP Proxy Agents Monorepo
https://proxy-agents.n8.io
871 stars 229 forks source link

Add Kerberos auth support #313

Open segevfiner opened 1 month ago

segevfiner commented 1 month ago

Some networks use proxies with Kerberos (AKA Negotiate/SPNEGO/GSSAPI) based authentication, it would be nice if this package supported it, it can likely be implemented using https://www.npmjs.com/package/kerberos. (Note that Negotiate requires multiple round trips when it falls back to NTLM on Windows)

segevfiner commented 1 month ago

Here is how VS Code implemented it: https://github.com/microsoft/vscode/blob/9c2b9327e5cdce9583ef6cfe5072102b150cf7cb/src/vs/workbench/api/node/proxyResolver.ts#L151-L159

Without support for multi-step negotiate that resolves to NTLM, as they only do one step. And they also cache domains that requested Negotiate, so they don't have to hit them without credentials just to get 407 again.

segevfiner commented 1 month ago

It looks like they use an extension and patches of this library though, we will need an additional callback to be able to asynchronously add such credentials, the current headers callback is sync, and also won't allow handling the 407 and auto-detecting to use such auth, or saving state about the request.

segevfiner commented 1 month ago

Looks like they had to extend HttpsProxyAgent to add the necessary callback: https://github.com/microsoft/vscode-proxy-agent/blob/e390847e6fc7e32c45cabcee54e544e6821629ed/src/agent.ts#L158-L229. A fuller implementation will also need to do the same for HttpProxyAgent. And we can decide if we want to make it more robust so it can properly perform multi-step Negotiate. Alternatively, it can be implemented internally, with an optional peer dependency on Kerberos so it is only used if it is installed if you don't want to depend on it directly, as it is a native module.

EDIT: Actually the callback should already be robust enough for multi-step, since you can probably store the KerberosClient on the state the callback get's passed instead of just a boolean like their current implementation does.