# SOAP security

WS-Security, body credentials and mutual TLS - each mechanism configured separately and combined freely.

SOAP endpoints authenticate in three distinct places - the SOAP header, the message body and the TLS layer - and real systems routinely require more than one at a time. Zato models each mechanism separately so that any combination is a matter of configuration:

- **WS-Security definitions** cover everything that goes into the `wsse:Security` header: UsernameToken, X.509 signing and encryption, SAML assertions
- **Body credentials** put a username and password inside the business message itself
- **Mutual TLS** presents a client certificate at the transport layer

Your service code stays identical whichever mechanisms a connection uses - security is applied by the platform when `invoke` runs, never by the service.

## WS-Security definitions {#ws-security-definitions}

A [WS-Security](https://zato.io/docs/security/ws-security.html) definition is created once, under **Security → WS-Security** in the Dashboard, and then attached to any number of connections through their Security tab. Each definition has a mode that determines what goes into the header.

### UsernameToken {#usernametoken}

The username and password are sent in a `wsse:UsernameToken` header element - the most common corporate SOAP setup. The password is entered separately, through the definition's **Change password** link, and can be sent either as text over TLS or in digest form, with an auto-generated nonce and timestamp, for endpoints that require it.

### X.509 {#x509}

The definition holds PEM material and two switches:

- **Sign** - the message is signed with your private key (**Signing key**), and your **Signing certificate chain** is included so the receiver can verify the signature
- **Encrypt** - the body is encrypted for the receiver using their **Peer certificate**

For verifying and decrypting what comes back, the definition also holds a **Decryption key** and **Trust anchors** - the CA certificates that peers' signatures are validated against. All of it is standard XML-DSig and XML-Encryption, the same W3C mechanics that WS-Security prescribes.

### SAML {#saml}

The definition generates a sender-vouches SAML 2.0 assertion on each request, built from the **Issuer**, **Subject** and **Audience** fields. With **Sign** enabled, the assertion includes an enveloped RSA-SHA256 signature computed with the same **Signing key** and **Signing certificate chain** fields the X.509 mode uses, as healthcare document-exchange profiles such as IHE XUA require of cross-community requests.

Externally issued assertions - tokens obtained from an identity provider rather than generated by the platform - can also be attached as raw bytes and pass through unmodified.

## Body credentials {#body-credentials}

Some endpoints do not look at the SOAP header at all - they expect the username and password as the first elements of the business message. Immunization registries following the CDC WSDL are the best-known example:

```xml
<sub:submitSingleMessage>
    <sub:username>my-user</sub:username>
    <sub:password>*******</sub:password>
    <sub:hl7Message>MSH|^~\&amp;|...</sub:hl7Message>
</sub:submitSingleMessage>
```

The **Body credentials** tab of an outgoing SOAP connection defines this mapping as a list of rows - each row names the element to create and, optionally, its 1-based position among the operation's children. Rows without a position become the first children in row order, which matches the common case of credentials-first layouts.

The values come from the security definition attached to the connection, and injection happens inside `invoke`, after your service has built the message - so the credentials never reach the service's code and cannot appear in its logs:

```python
# The service only builds business fields ..
request = SOAPMessage()
request.hl7Message = hl7_payload

# .. username and password are injected here, per the connection's mapping.
response = self.soap['My Registry'].invoke('submitSingleMessage', request)
```

Because the injected elements inherit the operation's namespace, they look to the receiving system exactly like any other field of the message.

## Mutual TLS {#mutual-tls}

Endpoints that authenticate clients at the TLS layer are covered by two fields on the connection's Security tab:

- **Client certificate** - a local path to a PEM file with the certificate, or with the certificate and key combined
- **Client key** - a local path to the key's PEM file, when it is kept separately

The paths point to files on the server - in containerized deployments, certificates are mounted into the container and the fields name the mount paths. Because this is a connection-level setting and not a security-definition type, it combines freely with everything above: one connection can present a client certificate *and* inject body credentials *and* sign its messages, which is precisely what some registries and national networks require.

The **TLS validation** dropdown on the same tab controls whether the outgoing connection verifies the server's own certificate - in [enmasse](https://zato.io/docs/admin/enmasse-reference.html#outgoing-soap-connections) this is the `tls_verify` key.

## Combining the mechanisms {#combining-the-mechanisms}

| Requirement | Configuration |
| --- | --- |
| Username and password in the SOAP header | WS-Security definition, UsernameToken mode |
| Username and password inside the message body | Any definition with credentials + Body credentials rows |
| Signed messages | WS-Security definition, X.509 mode with Sign |
| Encrypted messages | WS-Security definition, X.509 mode with Encrypt |
| Signed SAML user assertion | WS-Security definition, SAML mode with Sign |
| Client certificate at the TLS layer | Client certificate and key paths on the connection |
| Any combination of the above | Attach the definition and set the connection fields together |

## Configuration as code {#configuration-as-code}

Everything above is also plain YAML. A WS-Security definition deploys with [enmasse](https://zato.io/docs/admin/enmasse.html) like any other object - the mode and its fields map one-to-one from the Dashboard forms:

```yaml
security:

  - name: Registry Credentials
    type: wss
    username: my-registry-user
    password: Zato_Enmasse_Env.RegistryPassword
    mode: username_token
    use_digest: true
```

The [enmasse reference](https://zato.io/docs/admin/enmasse-reference.html#wss) documents every field of all three modes, with complete YAML examples for UsernameToken, X.509 and SAML definitions.

## Learn more {#learn-more}

- [SOAP integrations](https://zato.io/docs/dev/soap/) - Envelopes, versions and Python objects instead of XML
- [SOAP as Python objects](https://zato.io/docs/dev/soap/python-objects/) - Build requests and read responses with dot access, no schemas
- [MTOM and SwA attachments](https://zato.io/docs/dev/soap/attachments/) - Binary content out of the envelope and into MIME parts
- [SOAP mandates](https://zato.io/docs/dev/soap/mandates/) - What each national and sector profile requires, and how to meet it
- [SOAP tutorial](https://zato.io/tutorials/soap/calling.html) - Call any SOAP service with Python objects, start to finish
