This guide uses the Client Credentials grant type (machine-to-machine, no user login screen). A client application authenticates with its own credentials and receives a short-lived access token. You will configure the Mule OAuth 2.0 Provider module, build the required flows, enforce the token via API Manager policy, and wire consumer applications. By the end, your organisation owns the entire authentication chain.
When to use this pattern: internal API-to-API authentication where all consumers are Mule applications you control, proof-of-concept environments, or organisations on Anypoint Platform who want centralised token governance without adding a vendor. For production user-facing flows with external clients, evaluate an enterprise IdP (Okta, Azure AD, Auth0) instead.
The three roles
| Role | Component | Responsibility |
|---|---|---|
| Authorization Server | Mule OAuth 2.0 Provider | Issues and validates access tokens. Exposes /token and /validate. |
| Resource Server | Protected Mule API | Accepts requests. API Manager policy verifies the Bearer token before the flow executes. |
| Client Application | Consumer Mule App | Obtains a token from /token using its Client ID and Secret, then calls the protected API. |
Step 1: Set up the OAuth Provider project
The Mule OAuth 2.0 Provider is not a built-in module: it is an Anypoint Exchange asset. In Anypoint Studio, open File > New > Project from Template, search for "Mule OAuth 2.0 Provider" in Exchange, and import it. Add the provider module and Object Store connector; the provider requires Object Store to persist client registrations and tokens between restarts.
<!-- pom.xml dependencies -->
<dependency>
<groupId>com.mulesoft.modules</groupId>
<artifactId>mule-oauth2-provider-module</artifactId>
<version>1.0.4</version>
<classifier>mule-plugin</classifier>
</dependency>
<dependency>
<groupId>org.mule.connectors</groupId>
<artifactId>mule-objectstore-connector</artifactId>
<version>1.2.2</version>
<classifier>mule-plugin</classifier>
</dependency>Step 2: Configure the global elements
The global config declares which grant types are supported, where clients and tokens are persisted, and the token TTL. Two Object Stores are required: one for registered clients, one for active tokens. Keep Object Store TTLs aligned with your token TTL. Mismatched TTLs cause tokens to be accepted by the Object Store but rejected by the provider, or vice versa.
<oauth2provider:config
name="OAuth_Provider_Config"
clientStore="clientObjectStore"
tokenStore="tokenObjectStore"
supportedGrantTypes="CLIENT_CREDENTIALS"
tokenTtlSeconds="3600">
<oauth2provider:client-validation-rate-limiter>
<oauth2provider:period-rate-limiter
maximumFailureCount="5"
authorizationBetweenAttemptsPeriodInMillis="1000"
lockingPeriodInMillis="5000"/>
</oauth2provider:client-validation-rate-limiter>
</oauth2provider:config>
<!-- Client registry (persistent, 30-day TTL) -->
<os:config name="clientObjectStore">
<os:object-store alias="clientStore" persistent="true" maxEntries="1000"
entryTtl="30" entryTtlUnit="DAYS"/>
</os:config>
<!-- Token registry (persistent, matches tokenTtlSeconds) -->
<os:config name="tokenObjectStore">
<os:object-store alias="tokenStore" persistent="true" maxEntries="50000"
entryTtl="1" entryTtlUnit="HOURS"/>
</os:config>Step 3: Build the provider flows
You only hand-code two flows. The /token endpoint is auto-generated by the module when tokenConfig is active.
Create Client flow: an administrative endpoint. In production, restrict it with an IP allowlist or Basic Auth so only your platform team can register new client applications.
<flow name="createClientFlow">
<http:listener config-ref="HTTP_Listener_Config"
path="/createClient" allowedMethods="POST"/>
<oauth2provider:create-client
config-ref="OAuth_Provider_Config"
clientId="#[payload.client_id]"
clientSecret="#[payload.client_secret]"
clientName="#[payload.client_name]"
type="CONFIDENTIAL">
<oauth2provider:scopes>
<oauth2provider:scope value="PUBLIC_READ"/>
<oauth2provider:scope value="EMPLOYEES_ONLY"/>
</oauth2provider:scopes>
</oauth2provider:create-client>
<set-payload
value='#[{ "status": "registered", "client_id": payload.client_id }]'
mimeType="application/json"/>
<error-handler ref="global-error-handler"/>
</flow>Validate Token flow: API Manager calls this on every inbound request to a policy-protected API. Keep it lean: one Validate Token operation, no logging overhead on the critical path.
<flow name="validateTokenFlow">
<http:listener config-ref="HTTP_Listener_Config"
path="/validate" allowedMethods="GET"/>
<oauth2provider:validate-token
config-ref="OAuth_Provider_Config"
accessToken="#[attributes.queryParams.access_token]"
resourceOwnerRoles="#[attributes.queryParams.scopes]"/>
<set-payload
value='#[{ "uid": vars.tokenMetadata.clientId, "scope": vars.tokenMetadata.scopes }]'
mimeType="application/json"/>
<error-handler>
<on-error-continue type="OAUTH2-PROVIDER:TOKEN_UNAUTHORIZED">
<set-payload value='#[{ "error": "invalid_token" }]' mimeType="application/json"/>
</on-error-continue>
</error-handler>
</flow>Step 4: Deploy to CloudHub
Critical: JDK 8 required for the OAuth 2.0 Provider module. The module is incompatible with JDK 17 as of Mule 4.6.x. Deploying with JDK 17 (the Mule 4.6 default) causes the provider to fail at startup. Pin JDK 8 explicitly in mule-artifact.json. Watch for this on every CloudHub 2.0 migration.
// mule-artifact.json
{
"minMuleVersion": "4.6.0",
"javaSpecificationVersions": ["1.8"]
}Step 5: Test locally with Postman
Before deploying, validate the full flow locally:
# 1. Register a client
POST http://localhost:8081/createClient
Content-Type: application/json
{ "client_id": "my-app", "client_secret": "s3cr3t", "client_name": "My App" }
# Expected: 201 { "status": "registered", "client_id": "my-app" }
# 2. Request a token
POST http://localhost:8081/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=my-app&client_secret=s3cr3t&scope=PUBLIC_READ
# Expected: 200 { "access_token": "...", "token_type": "bearer", "expires_in": 3600 }
# 3. Validate the token
GET http://localhost:8081/validate?access_token=<token>&scopes=PUBLIC_READ
# Expected: 200 with token metadataStep 6: Apply the policy in API Manager
In API Manager, open your protected API instance and apply the OAuth 2.0 Access Token Enforcement using Mule OAuth Provider policy. Set the Access Token validation endpoint URL to your deployed provider's /validate URL. On each inbound request, API Manager extracts the Bearer token, calls /validate, and either forwards the request or returns 401; your flow code never handles token validation.
Add the auto-discovery ID from API Manager to your protected API's mule-artifact.json so the runtime looks up and enforces the policy at startup.
Step 7: Consuming OAuth-secured APIs
On the consumer side, use the Mule OAuth 2.0 module (not the provider module). It handles token acquisition, caching, and automatic refresh transparently; your flow code only calls the HTTP connector. The module caches the access token and reuses it until expiry; it does not call /token on every request.
<http:request-config name="Protected_API_Config">
<http:request-connection host="${protected.api.host}" port="443" protocol="HTTPS">
<http:authentication>
<oauth:client-credentials-grant-type
clientId="${oauth.client_id}"
clientSecret="${oauth.client_secret}"
tokenUrl="https://${oauth.provider.host}/oauth/token"
scopes="PUBLIC_READ">
<oauth:token-handler
accessTokenExpr="#[payload.access_token]"
expirationRegEx="expires_in"/>
</oauth:client-credentials-grant-type>
</http:authentication>
</http:request-connection>
</http:request-config>Production considerations
Key constraints to plan for: client registrations expire after 30 days by default, so build a re-registration job or increase the TTL for long-lived system accounts. With multiple CloudHub workers, use Object Store v2 (not in-memory mode) for all token and client stores to ensure shared state. Object Store has no native token revocation API; to revoke a token, delete its Object Store entry by key via the Object Store REST API.
Consider moving to an enterprise IdP (Okta, Azure AD) when external clients need authentication, user-delegated access is required, multi-tenant governance is needed, your security team requires MFA or federation, or token volume exceeds CloudHub Object Store throughput limits.
Troubleshooting reference
| Symptom | Root cause and fix |
|---|---|
| Provider fails to start on CloudHub | JDK version mismatch. Set javaSpecificationVersions to 1.8 in mule-artifact.json. |
| 401 on every token request | Client not registered. Confirm /createClient was called with correct client_id and client_secret. |
| Policy returns 401 even with valid token | Validate URL in API Manager does not match deployed provider URL, or scope mismatch. Verify both. |
| Object Store entries disappear after CloudHub restart | persistent="true" not set, or Object Store v2 not enabled in Runtime Manager. |
| Token expires before stated TTL | Object Store entryTtl and tokenTtlSeconds are mismatched. Align them. |
What you have built
Full control over the machine-to-machine authentication chain without adding an external dependency. The pattern is straightforward: deploy the provider module, register clients via /createClient, issue tokens via /token, and enforce the policy in API Manager. The single biggest operational constraint is the JDK 8 requirement: pin it explicitly in mule-artifact.json and you avoid the most common deployment failure. For everything beyond internal API-to-API flows, the same API Manager policy works identically when the token validation endpoint is pointed at an enterprise IdP.
