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:

ComponentTechnologyPurpose
Secrets ManagerHashiCorp Vault 2.2.0Centralised storage of OAuth credentials
Operating SystemRocky Linux 9.7Hosts HashiCorp Vault
Integration PlatformIBM webMethods Integration ServerConsumes secrets through REST APIs
AuthenticationHashiCorp AppRoleAuthenticates Integration Server
Local Credential StorageOutbound Password StoreProtects the Vault AppRole SecretID
CommunicationHTTPS, port 8200Secure 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:

SettingValue
Role namewebmethods-is
Policywebmethods-oauth
Initial token TTL1 hour
Maximum token TTL4 hours
SecretID TTL24 hours
SecretID usage limitUnlimited

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:

InputValue
urlhttps://ibm-dev-05:8200/v1/auth/approle/login
methodpost
Content-Typeapplication/json
Request bodyJSON 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:

InputValue
urlhttps://ibm-dev-05:8200/v1/webmethods/data/lab/oauth/crm
methodget
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:

CapabilityResult
Vault installation on Rocky LinuxCompleted
HTTPS configurationCompleted
Vault initialisation and unsealCompleted
KV v2 secrets storageCompleted
Read-only Vault policyCompleted
AppRole authenticationCompleted
REST API authentication testCompleted
REST API secret retrieval testCompleted
webMethods HTTP integrationCompleted
OAuth credential retrievalCompleted
Outbound Password Store integrationConfigured 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:

  1. Identify the requested integration.
  2. Retrieve its credentials from Vault.
  3. Request an OAuth access token using the Client Credentials grant.
  4. Cache the access token until shortly before expiry.
  5. 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

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.