Automatic for the People, Redux – Connecting your Car to the Salesforce1 Platform

The Automatic adapter contains GPS, mobile data and an accelerometer, accessible via an API that leverages OAuth and REST. Discover how to connect your car to the Salesforce1 Platform.

Wasn’t the Dreamforce 2014 IoT Zone AWESOME? A major factor contributing to its awesomeness was the 27 partners that supported us, among them Automatic, with their car adapter and connected apps.

Briefly, the Automatic adapter contains GPS, mobile data and an accelerometer, and plugs into the OBD-II port on all 1996 or newer cars sold in the US.

The device monitors your driving – reporting events such as ‘ignition on’, ‘trip start’, ‘hard acceleration’ and ‘hard braking’. The app collates the information to give you your trip history, car’s current location, and stats on your fuel consumption with tips for improving it (hint – minimize hard acceleration and hard braking!). Interestingly for us developers, the device also has an API (currently in ‘alpha’ release!), so you can register your own app and retrieve trip data and events.

Automatic kindly gave me an adapter to experiment with, and I used Matt Welch’s excellent blog post and code as a starter. Matt’s app includes a Visualforce page to manage authorization of the app’s access to your Automatic data via OAuth, but I wanted to try out a feature quietly introduced in Spring ’14, Authentication Provider ‘OAuth-only flow’.

You might be familiar with Authentication Providers as the mechanism for linking accounts at providers such as Facebook and Google with Force.com, allowing single sign-on. It’s a great feature, but sometimes, you just want access to APIs on behalf of a user, without linking their account in a login context. For instance, you might write an app that needs to retrieve a user’s Google contacts. You could configure the OpenID Connect provider to allow the user to login to your org via Google, but then you need to decide how you’re going to map identities from Google to Salesforce, write a Registration Handler, and direct users through the login process, even though you don’t really need any of that.

Spring ’14 introduced the ability to run the OAuth flow outside the login context, so an app can have the platform redirect the user to authorize API access at a provider then return control. This is ideal for integration with API providers such as Automatic – they’re not set up for OpenID Connect, but they do OAuth just fine. My users can log in to Salesforce as normal and, in my app, hit a button to make the connection with Automatic. It’s easier to show the flow that explain it – here’s a video:

To achieve this, I forked Matt’s code (I’ll send a pull request as soon as I update the README, Matt!) and modified it in a few key areas:

  • Use the OAuth-only flow on the built-in Authentication Provider rather than hand-rolled OAuth via Visualforce pages (less code, more functionality, yeah!)
  • Add an ‘Automatic ID’ field to the User object, rather than keeping user information in a custom setting, We can now manage drivers by just setting the Automatic ID on their User record.
  • Retrieve past trips via the Automatic API as well as gathering new trips via the webhook.

Here’s the code to kick that flow off:

public PageReference doRedirect() {
    return new PageReference('/services/auth/oauth/'+UserInfo.getOrganizationId()+'/'+authProviderName+'?'+
        'startURL='+EncodingUtil.urlEncode(ApexPages.currentPage().getUrl(), 'UTF-8'));
 }

Let’s break it down… The OAuth-only URL is available on the Authentication Provider detail page, but, given a few bits of metadata, we can construct it, since it has a well known format: /services/auth/oauth/ORG_ID/AUTH_PROVIDER_NAME. Notice we also supply a startURL query parameter that tells the Authentication Provider to redirect the user back to the current Visualforce page once the flow is complete.

After a successful flow, we can get the OAuth access token via the Auth.AuthToken.getAccessToken() method:

accessToken = Auth.AuthToken.getAccessToken(authProviderId, authProviderType);

One benefit of using the Authentication Provider for OAuth is that the platform manages tokens – it stores the Automatic access token against the user record. You can see the evidence in the ‘Third-Party Account Links’ section on the user detail page:

Note – at present you can’t easily read Authentication Provider metadata from Apex, so we get the Authentication Provider metadata from a custom setting:

private static AutomaticAPI__c settings = AutomaticAPI__c.getInstance();
private static String authProviderId = settings.authProviderId__c;     // e.g. '0SOE00000001234'
private static String authProviderName = settings.authProviderName__c; // e.g. 'Automatic'
private static String authProviderType = settings.authProviderType__c; // e.g. 'Open ID Connect'

Now it’s a snap to call the API and retrieve trips:

public static String getResource(String accessToken, String path) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint(baseUrl+path);
    req.setMethod('GET');
    req.setTimeout(60*1000);

    // Automatic uses 'token' rather than 'Bearer' here
    req.setHeader('Authorization', 'token '+accessToken);

    Http h = new Http();
    HttpResponse res = h.send(req);

    System.debug('getResource - HTTP: '+res);

    String resp = res.getBody();
    Integer statusCode = res.getStatusCode();
    if (statusCode == 401 || statusCode == 403) {
        System.debug('Refreshing access token');
        Map<String, String> refreshMap = Auth.AuthToken.refreshAccessToken(authProviderId,
            authProviderName,
            accessToken);
        System.debug(refreshMap);
        accessToken = refreshMap.get('AccessToken');
        if (accessToken != null) {
            return getResource(accessToken, path);
        } else {
            throw new AutomaticException('Error getting access token: '+refreshMap.get('RefreshError'));
        }
    }

    System.debug('getResource - JSON: '+resp);

    return resp;
}

Note the code to handle an expired access token – this is a common pattern with OAuth. Route all calls through a single handler, check the API return code, and, if the access token has expired, refresh it. Note also that the platform will do this for us.

There’s one more piece of code to consider. The user may authorize access to an API, giving our app an access token, but later revoke that access at the API provider. This is different from token expiry – refresh won’t work. Our app needs a way to check that a token is valid before using it. The simplest way? Just try an API call:

public Boolean getAccessTokenIsValid() {
    if (accessToken != null) {
        // We have a token - see if it works
        try {
            AutomaticUtils.getResource(accessToken, '/user');
            return true;
        } catch (AutomaticException e) {
            return false;
        }
    }

    return false;
}

Since this is a sample, we break out this functionality and display the state of the token, but a real app would hide the gory details from the user and just direct them through the OAuth flow as necessary.

Now you can connect vehicles on the move to the Salesforce1 Platform, the possibilities are endless: you could write an app to find the closest service vehicle to a customer, manage vehicle service scheduling, or perhaps analyze driving styles and send your lead-footed drivers for re-education. The AppExchange beckons!

Leave your comments...

Automatic for the People, Redux – Connecting your Car to the Salesforce1 Platform