<dependency>
<groupId>oauth.signpost</groupId>
<artifactId>signpost-core</artifactId>
<version>1.2.1.1</version>
</dependency>
Implementing OAuth 1.0 Authentication
This page presents a detailed discussion of DevKit’s support for OAuth 1.0 authentication, and how to implement OAuth 1.0 support in a connector.
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 1.0 as your option of choice.
This document also assumes that you already have a general familiarity with the architecture of a connector, as presented in Anypoint Connector Concepts, especially the common division of the connector into a @Connector class and a client class. Implementing OAuth 1 support requires adding code to each class. In addition, you should be familiar with OAuth 1.0, as documented in the OAuth Core 1.0 documentation.
Overview of @OAuth Annotation
To implement OAuth 1.0, you need to use the @OAuth
annotation on the @Connector class.This annotation triggers DevKit’s support for OAuth 1.
A connector annotated with @OAuth gets two additional @Processor methods called authorize
and unauthorize
. These message processors initiate and end an OAuth session.
The following table describes all parameters for the @OAuth
annotation.
Parameter | Description | Required? | Default Value |
---|---|---|---|
|
The signature method for use in |
|
|
|
Defines where the OAuth 1.0a parameters should be included. |
|
|
|
URL defined by the service provider used to obtain an unauthorized request token. |
✓ |
|
|
URL defined by the service provider to obtain an access token. |
✓ |
|
|
URL defined by the service provider, where the resource owner is redirected in order to grant authorization to the consumer. |
✓ |
|
|
A Java regular expression used to extract the verifier from the service provider response, after the resource owner authorizes the consumer. |
✓ |
|
|
If the service provider only accepts a known redirect URL. Assign this parameter to the path inside your domain (denoted by the |
|
Implementing OAuth 1.0a Support
The following discussion summarizes changes required in the @Connector and client classes to support OAuth 1. A more detailed walkthrough of implementing OAuth 1.0a support for a RESTful Web Service with Jersey client is in Connector to RESTful Service with Jersey Client Example; this discussion focuses on the OAuth functionality as presented in a sample LinkedIn connector.
Maven Project Dependencies
To implement authorization with OAuth 1.0 using DevKit, you must add the following dependency to your Maven pom.xml
file:
Required API Information
From the service provider’s API documentation, obtain the 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 Authenticating With OAuth section of the OAuth 1.0a specification.
Variable | Description |
---|---|
|
URL from which to obtain the request token |
|
URL from which to obtain the access token |
|
URL from to obtain authorization to resource access |
You must also arrange access to the service’s API. The process is specific to each API provider. Typical steps for this process are described in Preparing API Access or a Sandbox Environment.
Adding @OAuth Annotation on the @Connector Class
Add the @OAuth annotation to your @Connector class, as shown below:
@Connector(name = "rest-jersey", schemaVersion = "1.0", friendlyName = "REST Jersey Example")
@OAuth(
requestTokenUrl = "https://api.dropbox.com/1/oauth/request_token",
accessTokenUrl = "https://api.dropbox.com/1/oauth/access_token",
authorizationUrl = "https://www.dropbox.com/1/oauth/authorize",
verifierRegex = "oauth_token=([^&]+)")
// If an Exception with message "OAuth authorization code could not be extracted from:" is thrown after authenticate, check the parameter returned in the response
// By default is looking for a "oauth_verifier=([^&]+)" verifierRegex
// Example of one of my responses "http://localhost:8090/callbackresponse?uid=SOME_ID&oauth_token=SOME_TOKEN"
public class RestJerseyConnector {
....
Adding OAuth-Related @Connector Class Properties
Your @Connector class needs OAuth-related @Configurable instance properties (with public getters and setters) to hold the OAuth consumer key (annotated with @OAuthConsumerKey) and consumer secret (annotated with @OAuthConsumerSecret). These will allow the user to specify their consumer key and secret when using the connector.
@Configurable @OAuthConsumerKey private String consumerKey;
@Configurable @OAuthConsumerSecret private String consumerSecret;
It also needs String properties to hold the access token and access token secret, with public getters and setters (not shown), annotated as shown below:
@OAuthAccessToken private String accessToken;
@OAuthAccessTokenSecret private String accessTokenSecret;
Adding @Processor Method Annotations
For any @Processor method to be protected, add the @OAuthProtected annotation, as shown:
@OAuthProtected @Processor
public void logInfo() {
logger.info(String.format("OAuthAccessToken=%s", getAccessToken()));
logger.info(String.format("OAuthAccessTokenSecret=%s", getAccessTokenSecret()));
}
When invoked, an @OAuthProtected @Processor method initiates the following activities:
-
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.
-
During subsequent access requests, Mule includes the access token and access token secret (contained within the parameters annotated with
@OAuthAccessToken
and@OAuthAccessTokenSecret
) in the request to the service provider. Refer to the OAuth 1.0a specification for more details.
Including OAuth Headers in Client Class
Most OAuth 1.0 implementations will use Jersey Client to access a RESTful API; some will use a Java client library specific to the application. But whatever client you are using, you will have to add code at the client class level to send the consumer key, consumer secret, access token, and access token secret along with the request.
In our Jersey client sample, this is performed by a helper method addSignHeader()
on the client class, shown here:
private WebResource addSignHeader(WebResource webResource) {
OAuthParameters params = new OAuthParameters();
params.signatureMethod("PLAINTEXT");
params.consumerKey(getConnector().getConsumerKey());
params.setToken(getConnector().getAccessToken());
OAuthSecrets secrets = new OAuthSecrets();
secrets.consumerSecret(getConnector().getConsumerSecret());
secrets.setTokenSecret(getConnector().getAccessTokenSecret());
OAuthClientFilter filter = new OAuthClientFilter(client.getProviders(), params, secrets);
webResource.addFilter(filter);
return webResource;
}
The connector passes all calls to the Dropbox API through this method to add the authentication headers specified by the OAuth 1 standard. Because this is specific to the use of Jersey client, a detailed walkthrough of this method and how it fits into the client class is not presented here. See the Connector to RESTful Service with Jersey Client Example for the full details.
Using Your OAuth 1 Connector
Once you have your connector built and installed, you can use it in a flow, as described in the following sections.
Authorizing the Connector
Before a consumer can execute any operation that requires authorization, the resource owner must grant access to the protected resource to the connector. 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
and @OAuthAccessTokenSecret
. Mule includes the access token and token secret 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 the Connector in a Flow
-
Configure the extension 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.
<linkedin:config apiKey="${api.key}" apiSecret="${api.secret}"/> ... <flow name="sampleFlow"> <linkedin:get-profile-for-current-user /> </flow>
-
Configure a simple flow that attempts to access 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 Callbacks. 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.
<linkedin:config apiKey="${api.key}" apiSecret="${api.secret}">
<linkedin:oauth-callback-config domain="SOME_DOMAIN" remotePort="SOME_PORT" />
</linkedin: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 code below.
...
<https:connector name="httpsConnector">
<https:tls-key-store path="keystore.jks" keyPassword="mule2012" storePassword="mule2012"/>
</https:connector>
...
<linkedin:config apiKey="${api.key}" apiSecret="${api.secret}">
<linkedin:oauth-callback-config domain="localhost" localPort="${http.port}" remotePort="${http.port}" async="true" connector-ref="httpsConnector"/>
</linkedin:config>
...
For more information on configuring an HTTPS connector, consult the HTTPS Transport Reference. |
See Also
-
To learn more about the Dropbox sample used in this discussion, see the source code in Github and the walkthrough of the construction of the sample in Connector to RESTful Service with Jersey Client Example, linenums.
-
Learn how to implement OAuth 2.0 in your connector.
-
Lean more about HTTP Callbacks, used in the OAuth authentication process.