jamesiarmes/php-ntlm
A PHP library that adds NTLM authentication to SOAP clients, enabling PHP applications to connect to Microsoft Exchange Web Services and other Windows-integrated endpoints that require NTLM. It provides an NTLM-enabled SoapClient wrapper and supporting stream/config classes.
composer require jamesiarmes/php-ntlm
src/ and the tests/examples, not just the README. The package is old enough that the source is the real documentation. The main entry point is typically the JamesIarmes\PhpNtlm\Ntlm class.Typical manual handshake shape:
use JamesIarmes\PhpNtlm\Ntlm;
$ntlm = new Ntlm($username, $password, $domain, $workstation); // check constructor signature in your version
$type1 = $ntlm->getType1Message();
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_HTTPHEADER => [
'Authorization: NTLM ' . base64_encode($type1),
],
]);
$firstResponse = curl_exec($ch);
// Parse: WWW-Authenticate: NTLM <base64 Type 2 challenge>
$type2 = base64_decode($challengeBase64);
$type3 = $ntlm->getType3Message($type2);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: NTLM ' . base64_encode($type3),
]);
$secondResponse = curl_exec($ch);
If your release returns already-base64-encoded messages, do not double-encode. Verify that before wiring it into production.
Prefer native cURL NTLM first when possible:
curl_setopt_array($ch, [
CURLOPT_HTTPAUTH => CURLAUTH_NTLM,
CURLOPT_USERPWD => "$domain\\$username:$password",
]);
Use jamesiarmes/php-ntlm when you need explicit control over the NTLM messages or you are integrating with a client that cannot do native NTLM.
Wrap the handshake in a small client/service class. Do not scatter NTLM message generation, header parsing, and retry logic across controllers or jobs.
Reuse the same cURL handle or HTTP connection between the Type 1 and Type 3 requests. NTLM is connection-oriented; opening a new connection can invalidate the challenge.
Parse WWW-Authenticate carefully. Servers may return multiple schemes such as Negotiate and NTLM. Select the NTLM challenge, strip the scheme prefix, and base64-decode only the challenge token.
For Guzzle/cURL-backed clients, keep NTLM handling at the transport layer. If you use the package manually, encapsulate it behind a middleware or custom handler so application code still issues normal requests.
In long-running workers, expect re-authentication when connections are recycled. A 401 after a period of idle time is normal; rerun the handshake rather than caching a Type 3 header. Type 3 is tied to the server challenge and connection.
If you use jamesiarmes/php-ews, check whether its client already exposes NTLM configuration. This package may be a transitive dependency rather than something you should call directly.
Keep credentials in environment variables or a secret manager. Pass domain and username separately where possible; convert DOMAIN\user into the fields the library expects.
Because the package is unmaintained, isolate it behind an interface such as NtlmAuthenticator. That lets you swap in native cURL, a maintained fork, or another NTLM implementation without rewriting callers.
The package is old: last release was 2017 and maintenance is low. Test carefully on current PHP versions, especially PHP 8.x. Expect deprecation notices or extension assumptions.
NTLMv1 vs NTLMv2 matters. Modern servers often require NTLMv2. Confirm your release supports the variant your server demands; otherwise authentication will fail with unhelpful 401 responses.
Do not break connection affinity. Avoid CURLOPT_FRESH_CONNECT, avoid new handles between handshake steps, force HTTP/1.1 where needed, and watch out for load balancers that route the second request to a different backend.
Redirects are dangerous during the handshake. A redirect can strip or replay auth headers incorrectly. Disable redirects for the NTLM exchange and handle redirects after authentication.
Base64 handling is a common failure point:
Authorization header.Domain and username formatting cause many 401s. DOMAIN\user may not work everywhere; try separate domain/username fields. UPN-style user@domain is not universally supported by NTLM implementations.
Workstation is often optional but some servers require it. Set it explicitly if you see repeated 401s after credentials look correct.
Proxy authentication is separate. An NTLM proxy may require CURLOPT_PROXYAUTH and CURLOPT_PROXYUSERPWD; this library does not make proxy auth automatic.
Debug at the transport layer, but redact secrets. Use cURL verbose output only in development; never log Authorization headers or passwords. Log the stage: Type 1, challenge parse, Type 3, retry.
NTLM is authentication, not encryption. Always use HTTPS. NTLM challenge-response can still be relayed in poorly configured environments.
Extension points are limited but useful: subclass the NTLM class to adjust domain/workstation behavior, wrap it in your own authenticator, or replace it behind an adapter. Given the package’s age, a thin abstraction is the safest day-to-day pattern.
How can I help you explore Laravel packages today?