Contact Us 1-800-596-4880

Implementing OAuth 2.0 Authentication

This discussion presents the details of implementing OAuth 2.0 authentication, including concepts, the annotations used to support OAuth 2, and the code changes required to support OAuth 2 in your connector implementation.

Assumptions

This document assumes you have created a connector project, installed and tested it in Mule Studio, and are ready to implement authentication on your connector. It assumes you are familiar with the various authentication methods and have compared the authentication support options in DevKit and selected OAuth 2.0 as your option of choice.

This document also assumes that you are familiar with the general structure of an Anypoint connector built with DevKit, as discussed in Anypoint Connector Concepts, including the idea of a @Connector class and a client class.

Implementing OAuth 2.0 Authentication

The following sections provide details for how to implement OAuth 2.0 authentication support in the @Connector class and in the client class.

@OAuth2 Annotation

To add OAuth authentication to your connector, you need to use the class-level @OAuth `annotation. This annotation triggers DevKit to create two message processors: `authorize and unauthorize. These processors are used to initiate and kill the an OAuth session respectively.

The following table describes all parameters for the @OAuth2 annotation.

Parameter Description Required? Default Value

accessTokenUrl

URL defined by the service provider to obtain an access token.

authorizationUrl

Defined by the service provider, the URL where the resource owner is redirected in order to grant authorization to the consumer. You can override this value when configuring the processor by setting a different value in the config element. When creating a flow, you can override this value by entering your desired value in the authorize element.

authorizationParameters

Comma-delimited list of annotations, in the form:

@OAuthAuthorizationParameter(name = "xxx", type = xxx, description = "xxx")

The type cannot be a collection or a complex type.

verifierRegex

A Java regular expression to extract the verifier from the service provider response, after the resource owner authorizes the consumer.

code=([^&]+)

accessTokenRegex

A Java regular expression to extract the access token from the service provider response.

"access_token":"([^&]+?)"

expirationRegex

A Java regular expression to extract the expiration time of the access token (in seconds) from the service provider response. If this regular expression is not found in the service provider response (because the regular expression is wrong or the access token never expires), the access token will be treated as if it would never expire.

"expires_in":([^&]+?),

refreshTokenRegex

A Java regular expression to extract a refresh token from the callback during the authorization flow.

"\"refresh_token\":\"([^&]+?)\""

callbackPath

If the service provider only accepts a known redirect URL. Assign this parameter to the path inside your domain (denoted by the fullDomain environment variable) that will be registered with the service provider as a redirect URL. If left empty (meaning the service provider accepts any URL as redirect URL), Mule uses a random path.

<random path>

Adding OAuth 2 Support

  1. From the service provider’s API documentation, obtain the precise values for the variables listed in the table below. These variables reference URLs employed during the authorization process; for details on these steps of the process, consult the OAuth 2 specification.

    Variable Description

    accessTokenUrl

    URL from which to obtain the access token

    authorizationUrl

    URL from which to obtain authorization to resource access

  2. In your connector, use the @OAuth2 annotation as in the example below.

    @Connector(name = "oauth2connector")
    @OAuth2(authorizationUrl = "http://someUrl", accessTokenUrl = "http://someOtherUrl")
    public class OAuth2Module
    {
  3. Create two String instance fields to hold your Consumer Key and Consumer Secret, then annotate them with @OAuthConsumerKey and @OAuthConsumerecret respectively. Also, annotate them with @Configurable so that anyone using the connector can pass in their own credentials. Ensure these instance variables have public getters and setters.

    @Module(name = "oauth2module")
    @OAuth2(authorizationUrl = "http://someUrl", accessTokenUrl = "http://someOtherUrl")
    public class OAuth2Module
    {
        @Configurable
        @OAuthConsumerKey
        private String apiKey;
    
        @Configurable
        @OAuthConsumerSecret
        private String apiSecret;
  4. In the methods that access the protected resources (annotated with @Processor), add one String parameter and annotate it with @OAuthAccessToken.

    @Processor
    public Object accessProtectedResource(@OAuthAccessToken String accessToken, ...)
    {

When invoked, a method that contains parameters annotated with @OAuthAccessToken initiates the following activities:

  1. The first time a protected resource is accessed, the user is redirected to the authorization URL of the service provider to grant or deny access for the consumer to the protected resource.

  2. During subsequent access requests, Mule includes the access token (contained within the parameters annotated with @OAuthAccessToken) in the request to the service provider. Refer to Oauth 2.0 specification for more details.

Access Token Expiration

If you have specified a proper regular expression (using the expirationRegex parameter for the @OAuth2 annotation), and an API’s access token expires, DevKit automatically detects the expiration. In such cases, it triggers the OAuth2 flow again.

Client Class Changes: Passing the Access Token

The OAuth 2.0 support in DevKit provides the support for OAuth2 at the @Connector class-level. However, the client class will probably have to include logic to actually pass the access token with the request when it calls the web service. Because OAuth2 is not a formalized and strict standard, the specifics of how access tokens are passed with requests will depend upon the implementation of the target web service.

The target API provider will provide Java sample code that illustrates how to pass tokens to their service. When implementing your client class, use the API provider’s sample code as a reference.

For example, Foursquare supports OAuth 2.0 authentication, and expects the client to pass the access token as a query parameter. The OAuth 2.0 sample Foursquare connector, which implements an operation usersGetList, illustrates how to do this.

  • In the @Connector class OAuth2ExampleConnector , the connector passes the accessToken as a parameter to the client class operation client.usersGetList():

    @OAuthProtected
    @Processor
    public UsersListResponse usersGetList(
        @Optional @Default("self") String userId,
        @Optional @Default("") String group,
        @Optional @Default("") String location)
      throws Oauth2ConnectorExampleTokenExpiredException,
             Oauth2ConnectorExampleException {
            return client.usersGetList(accessToken, userId, group, location);
        }
  • In the client class FourSquareClient , the method usersGetList() adds the accessToken query parameter to the Jersey WebResource wr before making the GET request :

    public UsersListResponse usersGetList(String accessToken, String userId, String group, String location)
                throws Oauth2ConnectorExampleTokenExpiredException, Oauth2ConnectorExampleException {
    
            logger.info("Calling usersGetList - AccessToken: " + accessToken);
    
            URI uri = UriBuilder.fromPath(apiUrl).path("/{apiVersion}/users/{USER_ID}/lists").build(apiVersion, userId);
            WebResource wr = jerseyClient.resource(uri);
    
    
            // Warning!... queryParam does not modify the current WebResource. Instead it returns a new instance.
            // So, if you do not assign the result WebResource to the one that makes the call, the param will never be added
            wr = wr.queryParam("oauth_token", accessToken);
    
    ....
    
        try {
                logger.info(wr.toString());
                String res = wr.type(MediaType.APPLICATION_JSON_TYPE).get(String.class);
                logger.info("Response: " + res);
                result = jacksonMapper.readValue(res, UsersListResponse.class);
            }
    
    ...

Other services will require similar changes at the client level, but will differ in details, such as sending the token as a header. Also, this example illustrates the use of OAuth 2 with a RESTful web service using Jersey Client; for a SOAP-based web service, the client class changes will again be analogous, but the specifics will be different.

Using your OAuth2 Authenticated Connector

Authorizing the Connector

Before a consumer can execute any operation that requires authorization, the resource owner must grant access to the connector to access the protected resource. When it receives an authorization request, Mule redirects the resource owner’s browser to the service provider authorization page. Any subsequent attempts to access a protected resource fills the parameters annotated with @OAuthAccessToken. Mule includes the access token in the request to the service provider. See example below.

<linkedin:config apiKey="${api.key}" apiSecret="${api.secret}"/>
...
    <flow name="authorize">
        <http:inbound-endpoint host="localhost" port="8080" path="/authorize"/>
        <linkedin:authorize/>
    </flow>

Configuring Mule

  1. Configure the connector by passing the consumer key and consumer secret for your application as supplied by the service provider. The code sample below illustrates an example of such configuration.

    <oauth2module:config apiKey="${api.key}" apiSecret="${api.secret}"/>
    ...
        <flow name="sampleFlow">
            <oauth2module:access-protected-resource />
        </flow>
  2. Configure a simple flow that accesses a protected resource. If the connector has not been authorized by OAuth, the consumer operation throws a NotAuthorizedException.

Customizing the Callback

When the user grants access to the protected resource, the service provider makes an HTTP callback.

The callback passes an authorization code that Mule uses later to obtain the access token. To handle the callback, Mule dynamically creates an HTTP inbound endpoint, then passes the endpoint’s URL to the service provider. Thus, you do not need to complete any specific configuration to make an HTTP callback.

By default, Mule uses a host and port (determined by the fullDomain environment variable and the http.port) to construct a URL to send to the service provider. Where you need to use non-default values for host and port, add the configuration as per the code example below.

<oauth2module:config apiKey="${api.key}" apiSecret="${api.secret}">
<oauth2module:oauth-callback-config domain="SOME_DOMAIN" remotePort="SOME_PORT" />
</oauth2module:config>

For details on how Mule handles callbacks, see HTTP Callbacks.

Adding Secure Socket Layer (SSL)

When Mule automatically launches an HTTP inbound endpoint to handle the OAuth callback, it uses the HTTP connector by default. Where the service provider requires HTTPS, you can configure Mule to pass your own HTTPS connector (see example below).

...

<https:connector name="httpsConnector">
<https:tls-key-store path="keystore.jks" keyPassword="mule2012" storePassword="mule2012"/>
</https:connector>
...
<oauth2module:config apiKey="${api.key}" apiSecret="${api.secret}">
<oauth2module:oauth-callback-config domain="localhost" localPort="${http.port}" remotePort="${http.port}" async="true" connector-ref="httpsConnector"/>
</oauth2module:config> ...
For details on configuring an HTTPS connector, consult the HTTPS Transport Reference.

See Also