Authentication in SOAP services is more complex than in REST APIs. Where REST typically uses a single Authorization header or an API key, SOAP services can authenticate at the HTTP transport layer, inside the SOAP envelope's header, or both. Different services use different methods, and the WSDL may or may not tell you which one is required.
This guide covers the authentication methods you'll encounter in the real world, with working code examples for each.
Method 1: WS-Security UsernameToken
WS-Security (Web Services Security) is the standard authentication mechanism for SOAP services. The most common profile is UsernameToken, which embeds credentials directly in the SOAP header.
Plain Text Password
The simplest WS-Security variant sends the username and password in plain text within the SOAP header:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>myuser</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
mypassword
</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<!-- Your request here -->
</soap:Body>
</soap:Envelope>
Important: Plain text WS-Security credentials must always be sent over HTTPS. Without TLS, the password is visible to anyone intercepting the traffic.
Password Digest (Nonce-Based)
For higher security, the PasswordDigest profile hashes the password with a nonce (random value) and a timestamp. The server never sees the raw password — it verifies the digest using its stored copy.
<wsse:UsernameToken>
<wsse:Username>myuser</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">
<!-- Base64(SHA-1(Nonce + Created + Password)) -->
kYe4gEOP9FwUBGHExPnGcHtaink=
</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
tKUH8ab3Rokm4t6IAlgx0Q==
</wsse:Nonce>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
2026-03-15T10:00:00Z
</wsu:Created>
</wsse:UsernameToken>
Generating the digest in Node.js:
import crypto from "crypto";
function createPasswordDigest(password, nonce, created) {
const nonceBuffer = Buffer.from(nonce, "base64");
const hash = crypto.createHash("sha1");
hash.update(Buffer.concat([
nonceBuffer,
Buffer.from(created, "utf-8"),
Buffer.from(password, "utf-8"),
]));
return hash.digest("base64");
}
const nonce = crypto.randomBytes(16).toString("base64");
const created = new Date().toISOString();
const digest = createPasswordDigest("mypassword", nonce, created);
Common pitfall: The Created timestamp must be within the server's acceptable window (usually 5 minutes). If your server's clock is skewed, the request will be rejected even with correct credentials.
Method 2: HTTP Basic Authentication
Some SOAP services use standard HTTP Basic Auth instead of (or in addition to) WS-Security. This is the simplest approach but provides the least flexibility.
curl -u "myuser:mypassword" \
-H "Content-Type: text/xml" \
-d @request.xml \
"https://api.example.com/Service"
In Node.js:
const credentials = Buffer.from("myuser:mypassword").toString("base64");
const response = await fetch("https://api.example.com/Service", {
method: "POST",
headers: {
"Content-Type": "text/xml;charset=UTF-8",
Authorization: `Basic ${credentials}`,
SOAPAction: '"http://example.com/GetUser"',
},
body: soapEnvelope,
});
How to know if Basic Auth is required: The WSDL sometimes specifies this in a <wsp:Policy> section with <sp:HttpBasicAuthentication/>. More often, the API documentation simply tells you to use Basic Auth. If you see a 401 Unauthorized response with a WWW-Authenticate: Basic header, Basic Auth is expected.
Method 3: SAML Token Authentication
Enterprise SOAP services — especially those in federated identity environments — may require SAML (Security Assertion Markup Language) tokens. A SAML assertion is an XML document issued by an Identity Provider (IdP) that proves the caller's identity.
The workflow:
- Authenticate with the IdP (using your credentials or a certificate)
- Receive a SAML assertion token
- Include the SAML assertion in the SOAP header's
<wsse:Security>element
<wsse:Security>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_abc123" IssueInstant="2026-03-15T10:00:00Z">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<saml:Subject>
<saml:NameID>myuser@example.com</saml:NameID>
</saml:Subject>
<!-- Conditions, AuthnStatement, etc. -->
</saml:Assertion>
</wsse:Security>
SAML-based authentication is significantly more complex to implement from scratch. If you're facing this requirement, use a dedicated library like passport-saml (Node.js) or your platform's SSO framework.
Method 4: Custom SOAP Headers
Some services implement their own authentication schemes using custom SOAP headers. This is common with services that predate WS-Security or have proprietary security requirements.
<soap:Header>
<auth:Credentials xmlns:auth="http://example.com/auth">
<auth:ApiKey>your-api-key-here</auth:ApiKey>
<auth:Signature>hmac-sha256-signature</auth:Signature>
<auth:Timestamp>2026-03-15T10:00:00Z</auth:Timestamp>
</auth:Credentials>
</soap:Header>
There's no standard for custom headers — you'll need to follow the service provider's documentation exactly. Pay close attention to namespace URIs, element ordering, and whether the signature covers the entire body or just specific fields.
Method 5: SSL Client Certificates (Mutual TLS)
Some high-security SOAP services require client certificate authentication. The server validates your certificate during the TLS handshake, before any HTTP or SOAP-level communication occurs.
import https from "https";
import fs from "fs";
const agent = new https.Agent({
cert: fs.readFileSync("client-cert.pem"),
key: fs.readFileSync("client-key.pem"),
ca: fs.readFileSync("server-ca.pem"), // Server's CA if not in default trust store
});
const response = await fetch("https://api.example.com/Service", {
method: "POST",
agent,
headers: { "Content-Type": "text/xml" },
body: soapEnvelope,
});
Debugging mutual TLS:
openssl s_client -connect api.example.com:443 \
-cert client-cert.pem \
-key client-key.pem \
-CAfile server-ca.pem
If the handshake fails, the output will show exactly where it went wrong — expired certificate, untrusted CA, or key mismatch.
Figuring Out Which Method You Need
When documentation is sparse, use these signals to determine the required authentication method:
-
Check the WSDL's policy section. Look for
<wsp:Policy>elements containing<sp:UsernameToken>,<sp:HttpBasicAuthentication>,<sp:IssuedToken>, or<sp:X509Token>. -
Send an unauthenticated request and examine the error. The error response often hints at the expected method:
401withWWW-Authenticate: Basic→ HTTP Basic Auth- SOAP fault mentioning
wsse:Security→ WS-Security - TLS handshake failure → Likely requires client certificate
-
Ask the provider. SOAP services often have separate integration guides that describe authentication requirements more clearly than the WSDL.
Simplifying SOAP Authentication
SOAP authentication is unnecessarily complex for most use cases. WS-Security headers require precise XML construction, nonce generation, and timestamp management. Client certificate setup varies across deployment environments. SAML token flows add an entire IdP integration.
If you're tired of building and debugging SOAP security headers, SOAPless handles authentication transparently. You configure your SOAP credentials once in the SOAPless dashboard — whether it's WS-Security, Basic Auth, or client certificates — and your application authenticates with SOAPless using a simple API key. The SOAP-level authentication complexity stays out of your codebase entirely.
Whatever method your service requires, the code examples above should get you authenticated and making successful calls.