NTLM ("NTLMSSP") is a challenge/response authentication protocol used by Windows-based servers (IIS, Exchange, internal corporate services, ...). Unlike Basic or Bearer authentication, it is a connection-oriented three-way handshake:
- the client sends a
NEGOTIATE_MESSAGE, - the server replies
401with aCHALLENGE_MESSAGE, - the client resends the request with an
AUTHENTICATE_MESSAGEcomputed from the challenge and the user's credentials (NTLMv2).
ReqNtlm implements this purely as a request step (sets the authorization
header for the current leg) and a response step (reacts to a 401 NTLM
challenge by resending the request). It does not ship its own HTTP
adapter — whichever adapter the request already uses (Req.Finch by
default, or anything else configured via Req.new(adapter: ...)) keeps
being used.
This means NTLM's requirement that all legs of the handshake share the same
TCP connection is only as good as that adapter's connection reuse for two
requests issued back-to-back to the same host. For Req.Finch, that is
generally the case for sequential (non-concurrent) requests against the same
{scheme, host, port}, but it's not a hard guarantee. If you need stronger
guarantees (e.g. under concurrent load), configure the adapter's connection
pool accordingly for the affected host (see Req.Finch options).
Usage
Mix.install([
{:req, "~> 0.5"},
{:req_ntlm, "~> 0.1.0"}
])
req = Req.new() |> ReqNtlm.attach()
Req.get!(req,
url: "http://intranet.example.com/private",
ntlm: [username: "bob", password: "secret", domain: "CORP"]
)The username may also be given in "DOMAIN\user" form, in which case
:domain can be omitted:
Req.get!(req,
url: "http://intranet.example.com/private",
ntlm: [username: "CORP\bob", password: "secret"]
)A {username, password} tuple is accepted as a shortcut too:
Req.get!(req, url: "...", ntlm: {"CORP\bob", "secret"})Request Options
:ntlm- credentials used to perform the NTLM handshake. One of:- a
{username, password}tuple, - a keyword list or map with
:username,:password, and optionally:domainand:workstation(defaults to"").
When this option is not set,
ReqNtlmdoes not interfere with the request at all.- a
Limitations
- Only NTLMv2 is implemented (there is no NTLMv1 downgrade).
- As explained above, the handshake relies on the configured adapter reusing
the same connection across the two requests it takes; it is not pinned to
a single socket by
ReqNtlmitself. - Message confidentiality/integrity (NTLM signing/sealing) and session key
exchange are not implemented — only the authentication handshake itself,
which is what's needed to get past a
401challenge.
Summary
Functions
Attaches the plugin.
Reacts to a 401 NTLM challenge by resending the request.
Sets the authorization header for the current leg of the handshake.
Functions
Attaches the plugin.
See the module documentation for usage.
Reacts to a 401 NTLM challenge by resending the request.
This response step is automatically added by attach/2. It only acts once
per request (to avoid loops): if the :ntlm option is set, no challenge
has been recorded yet, and the response is a 401 carrying an
NTLM <challenge> www-authenticate header, it records the challenge and
reruns the request — which will then send the AUTHENTICATE_MESSAGE via
put_ntlm_header/1 above. Any other response is returned unchanged.
Sets the authorization header for the current leg of the handshake.
This request step is automatically added by attach/2. When the :ntlm
option is set, it sends a NEGOTIATE_MESSAGE unless a challenge has
already been recorded by handle_ntlm_challenge/1, in which case it sends
the AUTHENTICATE_MESSAGE computed from that challenge.