Contact Free trial Login

Client Credentials

Available since version 1.3.

Unlike the Authorization Code grant type, which focuses on authorizing an application that wants to act on a user’s behalf, the Client Credentials grant type focuses on authorizing an application to act on its own behalf. This grant type is better suited for machine-to-machine communication because it doesn’t require human intervention.

Develop a Client Credentials-Enabled Connector

You can add Client Credentials support at the ConnectionProvider level through the @ClientCredentials annotation.

Here is an oversimplified ConnectionProvider example for Anypoint Connector for Salesforce (Salesforce Connector):

@ClientCredentials(tokenUrl = "https://login.salesforce.com/services/oauth2/authorize")
public class SalesforceOAuthConnectionProvider<C> implements ConnectionProvider<SalesforceClient> {

   @Parameter
   @Optional(defaultValue = "34.0")
   private Double apiVersion;

  /**
   * Tailors the login page to the user's device type.
   */
  @OAuthParameter
  private String display;

  /**
   * Avoid interacting with the user
   */
  @OAuthParameter
  @Optional(defaultValue = "false")
  private boolean immediate;

  /**
   * Specifies how the authorization server prompts the user for reauthentication and reapproval
   */
  @OAuthParameter
  @Optional(defaultValue = "true")
  private boolean prompt;

  @OAuthCallbackValue(expression = "#[payload.instance_url]")
  private String instanceId;

  @OAuthCallbackValue(expression = "#[payload.id]")
  private String userId;

  private AuthorizationCodeState state;

  @Override
  public SalesforceClient connect() throws ConnectionException {
    if (state.getAccessToken() == null) {
      throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "accessToken"));
    }

    if (instanceId == null) {
      throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "instanceId"));
    }

    return new SalesforceClient(state.getAccessToken(), instanceId, apiVersion);
  }

  public void disconnect(SalesforceClient connection) {
    connection.close();
  }

  @Override
  public ConnectionValidationResult validate(SalesforceClient connection) {
    return success();
  }
}

The class implements ConnectionProvider, just like other connections in the SDK. Operations that are authenticated through the object operate over the SalesforceClient object that the provider returns. This is an important design consideration because it permits you to decouple the operation from the authentication method that is used because the connector could define another ConnectionProvider that uses basic authentication, and all the operations remain fully compatible.

@ClientCredentials

This annotation indicates that this connection provider requires an OAuth dance using the Client Credentials grant type. The annotation has the following attributes:

public @interface ClientCredentials {

  /**
   * @return The Url of the endpoint which provides the access tokens
   */
  String tokenUrl();

  /**
   * @return Expression to be used on the response of {@link #tokenUrl()} to extract the access token
   */
  String accessTokenExpr() default "#[payload.access_token]";

  /**
   * @return Expression to be used on the response of {@link #tokenUrl()} to extract the access token expiration
   */
  String expirationExpr() default "#[payload.expires_in]";

  /**
   * @return The default set of scopes to be requested, as a comma separated list. Empty string means no default scopes.
   */
  String defaultScopes() default "";

  /**
   * Allows to customize the placement that the client credentials will have in the request.
   *
   * @return the selected {@link CredentialsPlacement}. Defaults to {@link CredentialsPlacement#BASIC_AUTH_HEADER}
   */
  CredentialsPlacement credentialsPlacement() default BASIC_AUTH_HEADER;

}

Connection Management Strategy

The provider in the @ClientCredentials example does not implement any specialization of the ConnectionProvider interface, which means that the OAuth mechanism can be combined with the other connection management strategies. The connection objects can be pooled, cached, or created anew each time, depending on which interface is used (for example, PoolingConnectionProvider, CachedConnectionProvider, ConnectionProvider, and so on). For more information about connectivity management, see the connectivity reference.

Be aware of the semantics of using the unspecialized (for example, not PollingConnectionProvider, CachedConnectionProvider) ConnectionProvider interface in this scenario. In a regular, “non-OAuth” connection provider, using the unspecialized interface means that each time a component requires a connection, a new one is created, and it is destroyed when the component is finished. Although this will remain true for the OAuth case, it does not mean that the OAuth dance is performed again. New connection objects are created, but the same access token is reused as long as it remains valid.

Regular Parameters versus OAuth Parameters

This ConnectionProvider can have parameters, just like any other connection provider. However, you must distinguish regular parameters from the concept of @OAuthParameter.

An OAuthParameter is included as a custom parameter while performing the OAuth dance. So, for example, while the apiVersion parameter is something that the connection provider uses to create the SalesforceClient, the immediate parameter is actually sent on the OAuth request to the service provider.

From the module’s point of view, it is just another parameter for which the user provides a value. You can combine these parameters with @Optional, @Expression, and all the other annotations you can use with the traditional @Parameter annotation. In the DSL, regular and OAuth parameters appear together. The module’s end user should not notice any difference.

requestAlias Parameter

Some custom OAuth parameters include characters that are not supported in Java, for example Api-Key. Since you cannot use - as part of a field name, the @OAuthParameter annotation has an optional parameter called requestAlias:

@OAuthParameter(requestAlias = "api-key")
private String apiKey;

@OAuthCallbackValue Annotation

Callback values are extracted from the response that the service provider sends through the OAuth callback. Although most service providers return only standard items (such as access and refresh tokens, expiration information, and so on), others return additional items (such as user and instance IDs, in the case of Salesforce).

The annotation includes an expression that is applied to the response to extract the value. That value is then assigned to the field for the connection provider to use. When the connect(), validate(), or disconnect() methods are invoked, the fields are set and usable.

@ClientCredentialsState

Every ConnectionProvider annotated with @ClientCredentials must contain one (and only one) field of type ClientCredentialsState.

It is a simple immutable POJO that contains information regarding the outcome of the OAuth dance. It contains the following information:

public interface ClientCrendentialsState {

  /**
   * @return The obtained access token
   */
  String getAccessToken();

  /**
   * @return The access token's expiration. The actual format of it depends on the OAuth provider
   */
  Optional<String> getExpiresIn();
}

Through this object, the provider gains access to the accessToken and other standard information that was obtained during the OAuth dance. The original Salesforce example shows how the connect() method uses this POJO to create the client.

Configure a Client Credentials Connector

An important aspect of OAuth-enabled modules is how to use them. As much as the SDK does to hide the complexities of the OAuth protocol, things like the OAuth dance are unavoidable. The SDK’s approach to this problem is to standardize the experience of all OAuth modules to keep simplifying the user experience. This section discusses the steps a user must take to use the module.

Synthetic Parameters

In addition to everything explicitly defined in the ConnectionProvider, the SDK automatically adds some extra parameters and injects the proper behavior for them.

Parameter Name Required Expressions Default Value Description

clientId

YES

SUPPORTED

N.A.

The OAuth clientId value as registered with the service provider.

clientSecret

YES

NOT_SUPPORTED

N.A.

The OAuth clientSecret value as registered with the service provider.

tokenUrl

NO

SUPPORTED

Value provided in the @ClientCredentials annotation.

The service provider’s token endpoint URL.

scopes

NO

SUPPORTED

The value provided in the @ClientCredentials annotation.

The OAuth scopes to be requested during the dance. If not provided, this value defaults to those in the annotation.

objectStore

NO

NOT_SUPPORTED

N.A.

A reference to the ObjectStore used for storing each resource owner ID’s data. If not specified, the runtime automatically provisions a default one.

About the Use of Expressions

The synthetic parameters table identifies many of the synthetic parameters that accept expressions. Using expressions there has the same effect as using expressions in a regular parameter: it turns the configuration dynamic.

OAuth Connection DSL

The following example shows generated DSL:

<sfdc:config name="salesforce">
    <sfdc:client-credentials-connection display="PAGE" immediate="FALSE" prompt="CONSENT">
        <sfdc:oauth-client-credentials clientId="${sfdc.consumerkey}" clientSecret="${sfdc.consumersecret}" tokenUrl="http://..." />
        <sfdc:oauth-store-config objectStore="oauthObjectStore" />
</sfdc:config>

Regular and OAuth parameters are all shown at the connection provider level, as they would be in any other provider. The parameters related to the Client Credentials grant type are placed on a child element called <oauth-client-credentials>. The parameters related to ObjectStore are placed in a child element called <oauth-store-config>.

Configuring a Custom ObjectStore

The obtained access tokens are stored in an Object Store. By default, the SDK stores them in the app’s default Object Store, but users can define their own custom Object Store, as shown in this example:

<os:object-store name="tokenStore"
   entryTtl="1"
   entryTtlUnit="HOURS"
   maxEntries="100"
   persistent="true"
   expirationInterval="30"
   expirationIntervalUnit="MINUTES" />

<sfdc:config name="salesforce">
    <sfdc:client-credentials-connection display="PAGE" immediate="FALSE" prompt="CONSENT">
        <sfdc:oauth-client-credentials clientId="${sfdc.consumerkey}" clientSecret="${sfdc.consumersecret}" tokenUrl="http://..." />
        <sfdc:oauth-store-config objectStore="tokenStore" />
</sfdc:config>

Refresh an Expired Client Credentials Access Token

Most access tokens have a limited lifespan: typically about 30 to 60 minutes after being issued. This is why most providers also supply a refresh token, which eliminates the need to perform the OAuth dance again, although this is not a standard that is strictly enforced by providers. There is not an enforced standard that the SDK automatically detects an expired token and replaces it. Because different APIs communicate that in different ways, the AccessTokenExpiredException exception exists, shown below:

public void someOperation(@Connection SalesforceRestClient client) {
    Response response = client.performSomeOperation();
    if (response.getStatusCode() == 401) {
        throw new AccessTokenExpiredException();
    }
}

This example uses an imaginary REST client to perform an operation:

  • It assumes that this REST client was created through a ConnectionProvider annotated with @ClientCredentials.

  • It performs the operation and receives a Response object that contains information about the HTTP call.

  • The example assumes that a status code of 401 (UNAUTHORIZED) means that the token is expired.

  • It throws the AccessTokenExpiredException.

When the SDK detects this exception, it automatically performs the refresh dance and retries the operation with a new access token. As noted earlier, different APIs notify you of an access token expiration in different ways. Some give you clients that throw exceptions, while others give you custom messages. You should research the remote API to determine the best way to account for this.

Invalidating Access Tokens

Multitenancy implies the ability to invalidate the access token of a particular resourceOwnerId, causing the associated token information to be deleted. To invalidate an access token, the SDK automatically adds an operation called unauthorize to every OAuth-enabled module. Given the following example, you might invalidate the token obtained like this:

<sfdc:unauthorize config-ref="salesforce"/>

Note that this operation does not invalidate the token on the service provider. It removes the service provider from Mule’s caches and Object Stores.

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub