Securing OAuth Client Credentials in webMethods Integration Server with HashiCorp Vault
A practical, step-by-step guide to integrating IBM webMethods Integration Server with HashiCorp Vault on Rocky Linux, using AppRole authentication, REST APIs and the Outbound Password Store.
Introduction
Managing secrets is one of the most important aspects of enterprise integration security.
Integration platforms such as IBM webMethods Integration Server frequently need to authenticate against external systems using OAuth 2.0 Client Credentials. This typically involves storing a client_id and a client_secret to obtain access tokens from an authorisation server.
A common approach is to store these credentials directly in configuration files, environment variables or integration services. However, this creates several challenges:
- Sensitive credentials may become exposed in source code or deployment packages.
- Rotating secrets requires updating multiple applications.
- Access to credentials is difficult to audit centrally.
- Different environments require separate credential management.
- Developers and administrators may have unnecessary access to production secrets.
A better approach is to use a centralised secrets management platform.
In this tutorial, we will integrate IBM webMethods Integration Server with HashiCorp Vault, allowing Integration Server to retrieve OAuth credentials securely at runtime.
We will also use the native webMethods Outbound Password Store to protect the credentials required to authenticate against Vault itself.
1. Architecture Overview
Our laboratory consists of two separate Linux servers:
| Component | Technology | Purpose |
|---|---|---|
| Secrets Manager | HashiCorp Vault 2.2.0 | Centralised storage of OAuth credentials |
| Operating System | Rocky Linux 9.7 | Hosts HashiCorp Vault |
| Integration Platform | IBM webMethods Integration Server | Consumes secrets through REST APIs |
| Authentication | HashiCorp AppRole | Authenticates Integration Server |
| Local Credential Storage | Outbound Password Store | Protects the Vault AppRole SecretID |
| Communication | HTTPS, port 8200 | Secure communication between systems |
The integration follows this sequence:
webMethods Integration Server
|
| 1. Retrieve AppRole SecretID
v
Outbound Password Store
|
| 2. Authenticate using RoleID + SecretID
v
HashiCorp Vault
|
| 3. Return Vault Client Token
v
webMethods Integration Server
|
| 4. GET /v1/webmethods/data/lab/oauth/crm
v
HashiCorp Vault
|
| 5. Return OAuth Client Credentials
v
webMethods Integration Server
|
| 6. Request OAuth Access Token
v
OAuth Authorisation Server
The key principle is that OAuth client credentials are stored centrally in Vault rather than being hardcoded into webMethods Flow Services.
2. Installing HashiCorp Vault on Rocky Linux
Our Vault server runs Rocky Linux 9.7 on an x86_64 architecture.
First, install the required DNF plugins:
sudo dnf install -y dnf-plugins-core
Add the official HashiCorp repository:
sudo dnf config-manager --add-repo \
https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
Install Vault:
sudo dnf install -y vault
Verify the installation:
vault version
In our environment, this returned:
Vault v2.2.0
The RPM installation creates a systemd service and a default configuration file:
/etc/vault.d/vault.hcl
3. Configuring Vault Storage and HTTPS
For our laboratory, we used Vault’s file storage backend.
The relevant configuration in /etc/vault.d/vault.hcl is:
ui = true
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/tls-new.crt"
tls_key_file = "/opt/vault/tls/tls-new.key"
}
The listener binds to all network interfaces because Integration Server runs on a different virtual machine.
Access to port 8200 should be restricted through firewall rules to authorised systems.
3.1 Configuring TLS
The default certificate generated during installation did not include Subject Alternative Names (SANs).
Modern TLS clients require valid SAN entries to verify server identity.
We therefore generated a new self-signed certificate containing:
DNS:ibm-dev-05
DNS:localhost
IP Address:127.0.0.1
An example OpenSSL configuration is:
[req]
prompt = no
distinguished_name = dn
x509_extensions = v3_req
[dn]
CN = ibm-dev-05
O = HashiCorp Vault Lab
[v3_req]
subjectAltName = @alt_names
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
[alt_names]
DNS.1 = ibm-dev-05
DNS.2 = localhost
IP.1 = 127.0.0.1
Generate the certificate:
sudo openssl req -x509 -nodes \
-newkey rsa:3072 \
-keyout /opt/vault/tls/tls-new.key \
-out /opt/vault/tls/tls-new.crt \
-days 365 \
-config /tmp/vault-openssl.cnf
The private key must remain accessible only to the Vault service account.
For production, a certificate issued by a trusted internal or public Certificate Authority is preferable.
3.2 Starting Vault
Start the service:
sudo systemctl start vault
Check its status:
sudo systemctl status vault
Verify that Vault is listening:
sudo ss -lntp | grep 8200
The expected listener is:
0.0.0.0:8200
4. Initialising and Unsealing Vault
Before initialising Vault, configure the CLI:
export VAULT_ADDR="https://127.0.0.1:8200"
export VAULT_CACERT="/etc/pki/tls/certs/vault-lab.crt"
The CA certificate path points to an accessible copy of the public certificate.
Check the initial state:
vault status
For a new installation:
Initialized false
Sealed true
Initialise Vault:
vault operator init \
-key-shares=1 \
-key-threshold=1
For this laboratory, we use one unseal key. This is a simplified setup and should not be treated as a production security recommendation.
Vault generates:
- An Unseal Key
- An Initial Root Token
These values must be stored securely and never committed to source control.
Unseal Vault:
vault operator unseal
Authenticate as an administrator:
vault login
After successful unsealing, Vault should report:
Initialized true
Sealed false
5. Creating the KV Secrets Engine
HashiCorp Vault provides multiple secrets engines.
For OAuth Client Credentials, we chose Key-Value Secrets Engine version 2 (KV v2).
KV v2 supports versioned secrets, allowing credentials to be updated while maintaining version history.
Enable the engine:
vault secrets enable \
-path=webmethods \
-version=2 kv
Our secrets will be organised using the following logical structure:
webmethods/
lab/
oauth/
crm/
client_id
client_secret
5.1 Storing OAuth Credentials
For testing purposes, we created a fictional OAuth client:
vault kv put webmethods/lab/oauth/crm \
client_id="crm-client-lab" \
client_secret="test-secret-123"
This creates version 1 of the secret.
Verify the stored values:
vault kv get webmethods/lab/oauth/crm
Expected result:
Key Value
--- -----
client_id crm-client-lab
client_secret test-secret-123
These are demonstration credentials only.
For real credentials, avoid exposing sensitive values in shell history, command-line arguments or operational logs.
6. Creating a Vault Access Policy
We do not want Integration Server to authenticate using the Vault Root Token.
Instead, we will create a policy granting read-only access to the specific OAuth secret.
Create /tmp/webmethods-policy.hcl:
path "webmethods/data/lab/oauth/crm" {
capabilities = ["read"]
}
Notice the /data/ segment.
This is required when defining policies against KV v2 API paths.
Register the policy:
vault policy write \
webmethods-oauth \
/tmp/webmethods-policy.hcl
Verify:
vault policy read webmethods-oauth
The resulting policy grants permission to read the CRM credentials but does not allow modifying or deleting them.
This implements the principle of least privilege.
7. Configuring AppRole Authentication
HashiCorp Vault supports several authentication methods.
For a traditional Linux-hosted Integration Server, we selected AppRole because it is straightforward to integrate through REST APIs.
AppRole uses two credentials:
- RoleID: identifies the application role.
- SecretID: proves that the application is authorised to authenticate.
Together, they allow the application to request a temporary Vault token.
7.1 Enable AppRole
vault auth enable approle
7.2 Create the Integration Server Role
vault write auth/approle/role/webmethods-is \
token_policies="webmethods-oauth" \
token_ttl="1h" \
token_max_ttl="4h" \
token_no_default_policy=true \
secret_id_ttl="24h" \
secret_id_num_uses=0
Our laboratory configuration uses:
| Setting | Value |
|---|---|
| Role name | webmethods-is |
| Policy | webmethods-oauth |
| Initial token TTL | 1 hour |
| Maximum token TTL | 4 hours |
| SecretID TTL | 24 hours |
| SecretID usage limit | Unlimited |
The unlimited SecretID usage is convenient for testing but requires additional safeguards in production.
In particular, a SecretID with a 24-hour TTL must be replaced after expiry. The Outbound Password Store does not automatically renew it.
7.3 Obtain AppRole Credentials
Retrieve the RoleID:
vault read \
auth/approle/role/webmethods-is/role-id
Generate a SecretID:
vault write -f \
auth/approle/role/webmethods-is/secret-id
Both values will be required by Integration Server.
8. Testing the Vault REST API
Before implementing the Flow Service, we verified that AppRole authentication and secret retrieval worked through HTTPS.
8.1 Authenticate
Request:
POST /v1/auth/approle/login
Content-Type: application/json
Body:
{
"role_id": "<ROLE_ID>",
"secret_id": "<SECRET_ID>"
}
Vault responds with an authentication object containing a temporary client token:
{
"auth": {
"client_token": "<VAULT_TOKEN>",
"lease_duration": 3600,
"renewable": true
}
}
The exact token lifetime and renewal behaviour depend on the AppRole configuration.
8.2 Retrieve OAuth Credentials
Request:
GET /v1/webmethods/data/lab/oauth/crm
X-Vault-Token: <VAULT_TOKEN>
Response:
{
"data": {
"data": {
"client_id": "crm-client-lab",
"client_secret": "test-secret-123"
},
"metadata": {
"version": 1
}
}
}
The important detail is the nested data.data structure, which is specific to the KV v2 API response.
9. Integrating HashiCorp Vault with webMethods Integration Server
With Vault configured, we can implement the integration using standard webMethods Flow Services.
We created a service named:
securityLab.Vault.hashiCorpVault:getSecret
The initial implementation consists of two HTTP requests.
9.1 First HTTP Request: AppRole Login
Invoke:
pub.client:http
Configure:
| Input | Value |
|---|---|
url | https://ibm-dev-05:8200/v1/auth/approle/login |
method | post |
Content-Type | application/json |
| Request body | JSON containing RoleID and SecretID |
The HTTPS connection must use a truststore that trusts the Vault certificate and validates the server hostname.
The response is processed using:
pub.client:http
|
v
pub.string:bytesToString
|
v
pub.json:jsonStringToDocument
We then extract:
auth/client_token
This value is stored temporarily in the Pipeline as vaultToken.
9.2 Second HTTP Request: Retrieve the Secret
Invoke pub.client:http again:
| Input | Value |
|---|---|
url | https://ibm-dev-05:8200/v1/webmethods/data/lab/oauth/crm |
method | get |
X-Vault-Token | %vaultToken% |
Process the response using the same conversion services:
pub.string:bytesToString
|
v
pub.json:jsonStringToDocument
The resulting Pipeline contains:
document
|
+-- data
|
+-- data
|
+-- client_id
+-- client_secret
Finally, map the values to the service outputs:
document/data/data/client_id
-> clientId
document/data/data/client_secret
-> clientSecret
At this stage, we successfully tested the service and confirmed that Integration Server could authenticate against Vault and retrieve the stored credentials.
10. Solving the Secret Zero Problem
There is an important security question in this architecture:
If Vault protects our OAuth Client Secret, where do we store the SecretID used to access Vault?
Hardcoding the AppRole SecretID inside a Flow Service would simply move the original security problem elsewhere.
For our laboratory, we addressed this by using the native webMethods Outbound Password Store.
This allows Integration Server to store the Vault SecretID in its protected credential store rather than embedding it in the Flow Service implementation.
10.1 Registering the SecretID
We created a temporary administrative Flow Service:
securityLab.Vault.hashiCorpVault:configureVaultSecret
Its purpose is to register the AppRole SecretID.
The service uses:
pub.security.util:createSecureString
|
v
pub.security.outboundPasswords:setPassword
|
v
pub.security.util:destroySecureString
The SecretID is initially provided as a temporary service input.
createSecureString converts it into a WmSecureString, which is designed for controlled handling of sensitive character data.
The setPassword service stores the credential using the key:
vault.approle.secretId
Finally, destroySecureString clears the sensitive content held by the secure string object.
It is important to understand that destroying the secure string does not automatically erase other copies of the secret that may have been created as ordinary Java Strings.
The temporary administrative service should be restricted and removed when no longer needed.
10.2 Retrieving the SecretID at Runtime
We then modified our existing getSecret Flow Service.
Instead of receiving the SecretID as an input parameter, it retrieves the value internally.
The new sequence begins with:
pub.security.outboundPasswords:getPassword
|
v
pub.security.util:convertSecureString
|
v
Build AppRole Login JSON
The getPassword service uses:
key = vault.approle.secretId
The retrieved value is converted only when necessary to construct the AppRole login request.
The secure string is explicitly destroyed after use, and temporary plaintext values must be removed from the Pipeline as soon as they are no longer needed.
The secretId input was then removed from the public interface of getSecret.
This reduces the risk of accidentally exposing the credential to calling services.
10.3 Final Flow Service Structure
Our final design is:
getSecret
|
+-- outboundPasswords:getPassword
|
+-- util:convertSecureString
|
+-- Build AppRole Login JSON
|
+-- util:destroySecureString
|
+-- client:http
| POST /v1/auth/approle/login
|
+-- string:bytesToString
|
+-- json:jsonStringToDocument
|
+-- Extract Vault Token
|
+-- client:http
| GET /v1/webmethods/data/lab/oauth/crm
|
+-- string:bytesToString
|
+-- json:jsonStringToDocument
|
+-- Extract OAuth Credentials
|
+-- Clean Temporary Pipeline Data
|
+-- Return:
clientId
clientSecret
The service can now retrieve OAuth credentials without requiring the calling integration to supply the Vault AppRole SecretID.
This approach improves credential isolation, although it does not eliminate the Secret Zero Problem completely: the Outbound Password Store itself must be protected, and the AppRole SecretID still requires a secure rotation and distribution process.
11. Security Considerations
The laboratory demonstrates a working integration, but several improvements are necessary before adopting the architecture in production.
Credential Rotation
The AppRole SecretID in our example expires after 24 hours.
This means that simply storing it in the Outbound Password Store is not sufficient for unattended long-term operation.
A production implementation should include automated SecretID rotation and secure delivery, potentially using Vault’s response wrapping capabilities.
Token Caching
Our initial implementation authenticates to Vault whenever getSecret is executed.
A production implementation could cache the Vault client token until shortly before expiry, avoiding unnecessary authentication requests.
Similarly, OAuth access tokens should be cached according to their expires_in value rather than requesting a new token for every API invocation.
Logging and Pipeline Security
Special attention should be given to:
- Disabling sensitive Pipeline logging and audit capture.
- Restricting Flow Service execution through ACLs.
- Avoiding hardcoded credentials in MAP steps.
- Removing temporary Vault tokens and plaintext credentials.
- Preventing secrets from appearing in exception messages.
- Restricting access to Integration Server administrative interfaces.
A Drop Variable operation removes a value from the Pipeline, but does not guarantee that the underlying memory has been overwritten.
TLS and Network Security
The Vault endpoint should always use HTTPS with certificate validation enabled.
For production, certificates should be issued by a trusted Certificate Authority and renewed before expiry.
Network access to port 8200 should be limited to authorised clients.
High Availability
Our laboratory uses the file storage backend without high availability.
For production, HashiCorp Vault Integrated Storage (Raft) with an appropriate multi-node deployment is generally a more suitable option.
Backup, disaster recovery, monitoring and unseal procedures must also be considered.
12. What We Achieved
By the end of this exercise, we had implemented the core components of a centralised secrets management architecture:
| Capability | Result |
|---|---|
| Vault installation on Rocky Linux | Completed |
| HTTPS configuration | Completed |
| Vault initialisation and unseal | Completed |
| KV v2 secrets storage | Completed |
| Read-only Vault policy | Completed |
| AppRole authentication | Completed |
| REST API authentication test | Completed |
| REST API secret retrieval test | Completed |
| webMethods HTTP integration | Completed |
| OAuth credential retrieval | Completed |
| Outbound Password Store integration | Configured in the Flow Service design |
The key benefit is the separation of concerns.
HashiCorp Vault manages the OAuth credentials, while webMethods Integration Server retrieves them when needed.
The Outbound Password Store protects the AppRole SecretID used by Integration Server to access Vault.
13. Next Steps
The next logical evolution of this implementation is to create a reusable OAuth token management framework in webMethods.
Instead of exposing the clientId and clientSecret to every integration, we could implement a service such as:
common.oauth:getAccessToken
This service would:
- Identify the requested integration.
- Retrieve its credentials from Vault.
- Request an OAuth access token using the Client Credentials grant.
- Cache the access token until shortly before expiry.
- Return only the access token to the calling service.
This would provide a cleaner abstraction and further reduce the number of services handling client secrets.
For a larger integration landscape, the same architecture could support multiple environments, applications and credential types.
Conclusion
Integrating HashiCorp Vault with IBM webMethods Integration Server is relatively straightforward because both platforms support standard HTTPS and REST APIs.
There is no need to introduce a custom Java adapter for basic secrets retrieval.
Using Vault’s KV v2 engine, AppRole authentication, carefully scoped policies and the webMethods Outbound Password Store, we can build a practical foundation for centralised credential management.
The most important lesson is that secrets management is not simply about encrypting passwords. It is about controlling which applications can access credentials, how those applications authenticate, how secrets are rotated and how sensitive data is handled throughout its lifecycle.
This laboratory gives us a working starting point for building a more secure and maintainable OAuth integration architecture.
References
- HashiCorp Vault Documentation
- Vault KV Secrets Engine v2
- Vault AppRole Authentication
- Vault HTTP API
- IBM webMethods Integration Documentation
Environment: Rocky Linux 9.7, HashiCorp Vault 2.2.0, IBM webMethods Integration Server.
This implementation was developed in a laboratory environment. Production deployments require additional controls for credential rotation, availability, monitoring and operational security.