Using OAuth to Authorize External Applications

Abstract

Force.com lets you build external web applications that access data on the Force.com platform using the Oauth 1.0a protocol. OAuth is an open protocol that allows a website to access resources of another website without having to expose a user's credentials. Instead of supplying a username and password, OAuth allows users to hand out security tokens to specific sites for access to specific resources for a defined duration.

In this article we'll explain what OAuth is and why you should use it. We'll also configure a Remote Access Application in Force.com, and develop an application on Google App Engine that uses a Remote Access Application to authorize access to Force.com to display account and contact records. Finally, we'll look at some tips, tricks, available libraries and best practices to get you up and running in no time.

What is OAuth?

The OAuth website defines OAuth as "an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications." OAuth is all about sharing. The OAuth protocol enables a website (the consumer) to access protected resources from another web service (the service provider) through an API. The benefit of OAuth is that the API does not require users to disclose their provider credentials to consumers, and they can revoke the consumer's access to the service provider at any time. A large and ever-growing number of websites support OAuth including Google, Twitter, SmugMug, Yahoo!, Evernote and OpenSocial.

The OAuth documentation uses the analogy of a valet key to explain key concepts. When you park your car with a valet, you don't give him your regular key. You give him a special valet key that restricts his access to certain things like the trunk, glove box, etc. The rationale behind OAuth is the same. You give the consumer application limited access to resources in the service provider that you define.

The Beginner’s Guide to OAuth has a very detailed example of the user experience but from the consumer application that we are going to build, here is how the OAuth process will work. The user visits our consumer application and wants to view some account and contact data from our Salesforce org. The consumer application asks the user to authorize access to the data in Salesforce and directs them to the Remote Access Application page in their Salesforce org for that particular consumer application. If the user is not currently logged into their org, they must enter the credentials into the standard Salesforce login page. The user sees the details of the consumer application asking for authorization and grants access. The user is then redirected back to the consumer application and they can begin viewing the account and contacts records from Salesforce. The consumer application also stores the user's OAuth access tokens in the database for future use. This way the user only has to authorize the consumer application once and then the consumer application uses the stored OAuth access tokens for subsequent visits.

Don't confuse OAuth with OpenID. The two protocols do vaguely similar things but serve totally differently purposes. While OAuth is used for authorization, OpenID is used for authentication. To authorize a consumer with OAuth you will still need to log into your service provider. OAuth doesn't care how you authenticate against the provider, just that you do.

Why Should I Use OAuth?

For your end users the major benefit of OAuth is security. If an external website's datastore is hacked and all of their passwords are compromised, the hackers have full access to sensitive user info. Instead of a user's credentials scattered around various sites on the Internet, using OAuth the user simply issues access tokens to individual websites that need access to their data.

If a site's access token and consumer secret are compromised, then the user or provider can revoke them. In most provider cases a request authorized with an access token is not authorized to update specific aspects of the user's account, such as the password or email. With Force.com, access to data is restricted by the user's profile and its settings. Users can easily revoke a consumer application's access to their data by revoking the token from their Personal Information page under Personal Setup.

For developers, OAuth makes life much simpler. Gone are the days where you have to store your username, password and security token in your application code or create bespoke login schemes. Both of these methods are fraught with danger and cause developers to lose sleep - if your application's security is compromised or your code is decompiled, then every affected user must be notified of the security breach and have their password reset. Not only is this embarrassing but a security breach could lead to legal actions. If your credentials are coded into multiple applications and you happen to change your user's password, then you must undertake the tedious task of updating each application with the new credentials. Nothing is better on a rainy day than chasing down applications that now have wrong credentials.

Doing the OAuth Dance

One of the most important aspects to understand about OAuth is all of the tokens and secrets involved in the process and how they are used. There are basically three sets of token/secrets and each set builds upon the previous one. The three sets are consumer, request and access. The "OAuth dance" requires you to hand off these tokens and secrets as outlined in the following diagram from the Salesforce.com Help.

The consumer token and secret are generated for you by Force.com when you create a new Remote Access Application. The consumer token and secret will be hard-coded into your application, and uniquely identify it on Force.com.

Oauth dance.png

In step #1 the consumer application requests a request token from Force.com using the consumer tokens that you have embedded in your application. Force.com accepts the request and passes back a request token. In steps #2 and #3, the user is directed to the Salesforce.com login page (if not currently logged in) and then to the Remote Access Application page for your consumer application. The user sees the details of your consumer application requesting authorization and clicks "Yes" to authorize the request.

The user is then redirected back to our consumer application in steps #4 through #6 where our code requests and accepts an access token and secret. These tokens are used to create a new session with the Force.com SOAP API and we can then begin querying for data.

Since you only need to authorize the consumer application once, we store the access tokens in the database for future use. For the gruesome details of this process, see Authenticating Remote Access Application OAuth in the Salesforce.com Help and Training.

The rest of this article makes this all more concrete with a real example of a Google App Engine application that performs this dance, before accessing data on the Force.com platform.

Setting up a Remote Access Application

The first step in setting up OAuth for an external application is generating the consumer key and consumer secret that will be embedded in your GAE application. Not only does this serve as an identifier for your external application on the Force.com platform, but also identifies the URL your external site will be redirected to after a successful authentication as well as the display name and description that the user will see during the authorization process.

Setting up our new Remote Access Application is a relatively straightforward process with a few things to watch out for. Click Setup -> Develop -> Remote Access -> New to display the form below.

  • The Application name should be the same as the consumer application's name. This is the name that user will see when authorizing the consumer application so using the same name will reduce the fear, uncertainty and doubt associated with clicking the "Yes" button.
  • The Callback URL is the URL the user is sent to after they authorize the consumer application and must be a secure URL (ie. it must use HTTPS).
  • The Logo Image URL must also be a secure URL.

Oauth remoteaccess.png

Once you successfully save the form, Force.com will generate a consumer key and consumer secret for the GAE application and the screen should look like the following.

Oauth success.png

This screen displays your consumer key and consumer secret for the Remote Access Application. You will need to copy these values into the source code of your GAE application.

Configuring Data Security in Force.com

Consumer applications use the SOAP API to access data on the Force.com platform. The Force.com session is created for a named user and their access to resources on the Force.com platform is determined by their profile's settings. You can configure a user's permissions for objects and field level security by editing the profile of the user. You will need to ensure that you catch any INVALID_TYPE or INVALID_FIELD exceptions in your code for users without access to various objects and/or fields.

Building a Java Google App Engine Application for Authorizing against Force.com

Let's now look at some of the details of our application, which authorizes using OAuth. We've built it in Google App Engine, but in principle the same techniques can be applied to any application that you want to authorize against Force.com using OAuth.

If you are not experienced in building app on Google App Engine, read this good tutorial to get you up and running. However, most of this code, but certainly all of the concepts, will work on most Java app servers. I'll be outlining key parts of the code but you can check out the entire project from Force.com Code Share.

Helper Classes

Most of the OAuth flow takes place in the Servlets but there are few supporting classes that deserve a closer look. Feel free to dig into the details of the code at your convenience.

OauthSettings

This class contains the consumer key and consumer secret generated by Force.com as well as the URLs needed for the OAuth dance. You'll need to edit this class with your application's settings.

SfdcCredentials

This class isn't actually needed for OAuth but it does make life much easier. Developing and testing OAuth applications is a bit of a challenge as Force.com requires the callback URL to be secure. So to make development on localhost possible without tweaking your cache or redirecting ports, I add a small section in the ConnectionManager that uses the credentials in this class to create a session if the application running on localhost hasn't been authorized (i.e., the access token and token secret are not found in the database) and a cached session doesn't exist. This will create a new connection to Force.com and simulate a session returned by the OAuth authorization.

ConnectionManager

The ConnectionManager class is a Singleton that performs a number of tasks:

  1. Returns a PartnerConnection to use for querying Force.com for records. The getConnection() method determines if the session exists already, if a new session needs to be fetched from Force.com or if you are running locally and need to use the hard-coded testing credentials.
  2. Persists the access token and token secret to the database after they are returned from Force.com.
  3. Caches the endpoints and sessionId when returned from Force.com.
  4. Determines if the GAE application has been authorized by Force.com by querying the database for an existing access token and token secret.

OauthHelperUtils

This class is the real workhorse of the application. It contains a number low-level functions that make developing application with OAuth possible such as:

  1. Getting request and access tokens
  2. Building OAuth URLs
  3. Getting new sessions from Force.com
  4. Parsing the XML response from Force.com

Illustrative Classes

When the user first arrives at the GAE application it checks to see if the application has been authorized by Force.com. The Servlet queries the database for an existing access token and access secret, and if not found, displays a message that authorization is required before the user can continue. The user simply clicks the "login" link to start the authorization process.

LoginServlet.java

The LoginServlet is called when the user clicks on the link to log into Force.com and authorize the GAE application. A new OAuthAccessor object is created with the consumer key, token secret and the eventual callback URL. The OauthHelperUtils requests the request token from Force.com and if all is well, stores the returned request token and request's token secret in the OauthHelperUtils for later requests. Finally OauthHelperUtils constructs a URL to the confirmation page for the Remote Access Application and redirects the user to it.

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException {

  OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(
      OauthSettings.URL_CALLBACK, OauthSettings.CONSUMER_KEY,
      OauthSettings.CONSUMER_SECRET, null));

  String response = OauthHelperUtils.getRequestToken(accessor,
      OauthSettings.URL_REQUEST_TOKEN, OauthSettings.URL_CALLBACK);

  // see if the token request failed
  if (response.startsWith("<")) {
    log.warning("Failed to get request token.");
    resp.setContentType("text/html; charset=UTF-8");
    resp.getWriter().println("Request token failure!!");
    resp.getWriter().println(response);
    return;
  }

  OauthHelperUtils.REQUEST_TOKENS.put(accessor.requestToken,
      accessor.tokenSecret);

  try {
    String authUrl = OauthHelperUtils.buildAuthorizationUrl(accessor,
        OauthSettings.URL_AUTHORIZATION);
    resp.sendRedirect(authUrl);
  } catch (Exception e) {
    log.severe("Exception=" + e.toString());
  }

}

This Servlet starts the OAuth dance. Using the consumer key and consumer secret embedded in the OauthSettings class, the Servlet makes a callout to Force.com for the OAuth request token and then redirects the user to the Remote Access Application authorization page. If the user is not currently logged into the org, they will be presented with the standard Salesforce.com login screen before authorizing the GAE application.

CallbackServlet

Once the user has authorized the GAE application, they are automatically redirected back to the callback URL specified for the Remote Access Application. The following callback Servlet catches the request token and token secret and stores them for future use. The OauthHelperUtils then uses these request tokens and fires off a request to Force.com for the actual access token and secret. Once returned, these tokens are used to create a new session in Force.com. The Servlet then creates a new ConnectionManager class and persists the access token and token secret to the database for future and caches the session.

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException {

  try {

    String oauthToken = (String) req.getParameter(OAuth.OAUTH_TOKEN);
    String oauthConsumerKey = (String) req
        .getParameter(OAuth.OAUTH_CONSUMER_KEY);
    String oauthVerifier = (String) req.getParameter("oauth_verifier");

    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(
        OauthSettings.URL_CALLBACK, OauthSettings.CONSUMER_KEY,
        OauthSettings.CONSUMER_SECRET, null));
    accessor.requestToken = oauthToken;
    accessor.tokenSecret = OauthHelperUtils.REQUEST_TOKENS.get(oauthToken);

    String response = OauthHelperUtils.getAccessToken(accessor,
        oauthVerifier, OauthSettings.URL_ACCESS_TOKEN);

    String loginResponse = OauthHelperUtils.getNewSfdcSession(accessor,
        OauthSettings.URL_API_LOGIN);

    if (loginResponse.startsWith("<")) {
      OauthHelperUtils.XmlResponseHandler xmlHandler = OauthHelperUtils
          .parseResponse(loginResponse);
      String serviceEndpoint = xmlHandler.getServerUrl();
      String sessionId = xmlHandler.getSessionId();

      // get the connection
      ConnectionManager connection = ConnectionManager.getConnectionManager();
      // save the access tokens for future use
      connection.saveTokens(accessor.accessToken, accessor.tokenSecret);
      // set the connection
      connection.cacheSessionProps(OauthSettings.URL_AUTH_ENDPOINT,
          serviceEndpoint, sessionId);
    }

    RequestDispatcher rd = req.getRequestDispatcher("home");
    rd.forward(req, resp);

  } catch (Exception e) {
    log.info("Callback servlet exception=" + e.toString());
  }

}

This Servlet ends the OAuth dance. It's main purpose is to persist the tokens sent back from Force.com and to create a new session via the SOAP API.

AccountServlet

This is one of the Servlets that interacts with Force.com to display data. When the page loads, the Servlets grabs a connection from the ConnectionManager, submits the SOQL query via the Web Services API and the displays the JSP page with the results.

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException {

  PartnerConnection connection = ConnectionManager.getConnectionManager()
      .getConnection();

  try {

    QueryResult results = connection
        .query("SELECT Id, Name from Account Limit 10");
    req.setAttribute("accounts", results.getRecords());

    RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(
        "/accounts.jsp");
    dispatcher.forward(req, resp);

  } catch (ServletException e) {
    log.severe("Servlet exception=" + e.toString());
  } catch (Exception e) {
    log.severe("Query exception=" + e.toString());
  }

}

As the AccountServlet code illustrates, after authorizing an application, the rest of the application acts and feels like any other Force.com application using the Web Services API. Be sure to check out the code for the entire application on Code Share.

Best Practices

Here are some best practices when building applications that use OAuth:

  • Access tokens do not expire and are useful until they are revoked by the user. Keep the access token and access secret in a database and use it for subsequent calls to create a new Force.com session.
  • Treat your access token and token secret as if they are actual credentials. You should consider encrypting them in your database.
  • Use an existing OAuth client library instead of creating your own whenever possible. You can find a number of libraries here.
  • Name your Remote Access Application the same as your web application to avoid confusion when users authorize it.
  • When testing, use multiple browsers and ensure that you are logged out of all orgs. It is very easy to authorize the wrong user if you have multiple accounts.
  • Use the Firefox plugin Live HTTP Headers during testing to view the packets being passed back and forth to Force.com. This will make debugging your application much easier.
  • You may want to consider cloning existing users profiles and locking down access to objects not needed by the consumer application.
  • Use a test server if developing your own OAuth client. You can find a list of testing servers at the bottom of this page. I found Google's OAuth Playground and Termie's OAuth Echo Server the easiest to use.
  • Double check the package access controls or security settings for the profiles accessing Force.com via OAuth to ensure they don't have access to unauthorized data.

Summary

The OAuth protocol offers many benefits to both users and developers. Users gain the added security of not having to hand out their credentials to yet another website, while developers no longer need to store or maintain credentials in their code, database or memory. Once you understand the exchange of tokens and secrets the protocol is fairly easy to work with. In this article we walked through the steps of setting up a Remote Access Application with particular attention to some of the security requirements. We then developed a Java application on Google App Engine that uses OAuth to authenticate against Force.com, and afterwards retrieves data from the platform. You can use these same concepts in your own applications.

References

About the Author

Jeff Douglas is a Senior Technical Consultant at Appirio where he creates cutting-edge applications on the Force.com platform for some of the best companies in the world. He is a foster and adoptive parent and is trying to save the world one Apex line of code at a time. He actively blogs about cloud computing (especially Force.com) at http://blog.jeffdouglas.com.