How Connect IQ Brings OAuth to Wearables


OAUTH provides a standard way for developers to authenticate an application in order to gain access to web APIs. Before an app can access the web API, the developer must register the app with the service. During registration, you must provide a redirect URL that will be used to retrieve the credentials. Once registered, the app will provide a client id and secret, which are necessary for the login process.

Connect IQ 2 added new OAUTH APIs into the Communications module that allow widgets and device apps to make authenticated calls. These features were also ported into Connect IQ 1 compatible products so you can use these APIs in your 1.x apps as well. These easy to use APIs open up Connect IQ apps to many of the APIs available on the web.

To get started with these powerful features, download the free Connect IQ SDK.

To access the web service, the client must authenticate the user against the web service. With web applications, the app redirects the user’s web browser to the web service authentication page, providing the secret, the client id and the redirect URL. After the service has authenticated the user, it redirects their browser back to the client providing an access token. The access token can then be used in subsequent calls to the web service.

While the OAUTH standard is based on and works well for the world of web browsers and mobile applications, people generally don’t want to enter their usernames and passwords on their watches. Connect IQ has added some new APIs to allow you to bridge that gap using Garmin Connect Mobile.

    //! Request an OAuth sign-in on Garmin Connect Mobile.
    //! A notification will trigger on the phone, that when clicked,
    //! provides a webview that shows initialUrl. If the user grants
    //! permission to the app the function given to
    //! registerForOAuthMessages() will be called with a Dictionary of
    //! keys from the OAuth process.
    //! @param [String] requestUrl The URL to load in the web view
    //!        to begin authentication.
    //! @param [Dictionary] requestParams A dictionary of non-URL encoded
    //!        parameters for the initial url.
    //! @param [String] resultUrl The URL of the final page of authentication
    //!        that contains the resultKeys.
    //! @param [Number] resultType What format the result will be in.
    //!        Should be a OAUTH_RESULT_TYPE_XXX value.
    //! @param [Dictionary] resultKeys A dictionary of the keys Garmin
    //!        Connect Mobile will need to pull out of the OAuth response
    //!        and given to the registered callback in
    //!        registerForOAuthMessages().
    //! @since 1.3.0
    function makeOAuthRequest(requestUrl, requestParams, resultUrl, resultType, resultKeys);

    //! Register a callback for receiving OAuth messages. The callback will
    //! be called once for each received OAuth message. If there are messages
    //! waiting for the app when this function is called, the callback
    //! will immediately be called once for each waiting message.
    //! @param method [Method] The callback with the signature callback(data).
    //!               data will be of type OAuthMessage
    //! @since 1.3.0
    function registerForOAuthMessages(method);

The makeOAuthRequest call implements the credential entry step of the OAUTH 1 and 2 standard. When called, the user will receive on their wrist a notification that your app wants to log into a web service. Clicking on this notification on their phone will take the user to a web view within the Garmin Connect™ Mobile app, where they can enter their login information.

During this time, the Connect IQ app should display a page directing the user to open Garmin Connect Mobile. Once the user has completed credential entry, Garmin Connect Mobile will send back the tokens specified in the resultKeys option, and Garmin Connect Mobile will direct them back to the wearable device.

Your app should call registerForOAuthMessages to receive the result of the login process. Logging in can take a long time, and a widget may time out before the user completes the login step. If your app closes before the login process completes, the result will be cached on the device until the next time your app calls registerForOAuthMessages, at which point the result will be passed to your callback immediately. Once you have the access token, you can use it as an argument to makeWebRequest.

Activity Summary

Strava is a popular fitness app and has an active online community.  Strava’s cloud system can collect workout information through its own fitness application as well as other fitness communities, including Garmin Connect™. Strava offers a developer API that allows access to an athlete’s workout history and other information.

Wouldn’t it be great if we could see a summary of our past four weeks of workouts on a simple display on your wrist? We can do that by building a Connect IQ widget.

To try one of the Connect IQ sample apps, import it into your Eclipse workspace:

  1. Clone the connectiq-apps repository from GitHub [https://github.com/garmin/connectiq-apps]
  2. Click the File menu
  3. Select Import…
  4. Choose Existing Projects into Workspace under the General section of the window, and click Next
  5. Click the Browse button, and select the connectiq-apps/apps/strava-api sample
  6. If you prefer to work with a copy of the project, check Copy projects into workspace
  7. Click Finish to complete the import

To access the Strava API, we need to first create an app definition. We need to go to Strava’s developer site and create an application. This app puts the app identifiers in globals in Keys.mc

const ClientId = "FROM STRAVA API ADMIN"
const ClientSecret = "FROM STRAVA API ADMIN";
const ServerToken = "FROM STRAVA API ADMIN";
const ApiUrl = "https://www.strava.com/api/v3/";
const RedirectUri = "https://localhost";

The app is split into two halves — login and display. If we haven’t received an access token, we need to authenticate the user. Most of the work is done in the LoginTransaction class. This class goes through the OAUTH login flow. The transaction is kicked off in the go method, which calls makeOAuthRequest.

    // Method to kick off transaction
    function go() {
       // Kick off a request for the user's credentials. This will
       // cause a notification from Connect Mobile to file
       Comm.makeOAuthRequest(
           // URL for the authorization URL
           "https://www.strava.com/oauth/authorize",
           // POST parameters
           {
               "client_id"=>$.ClientId,
               "response_type"=>"code",
               "scope"=>"public",
               "redirect_uri"=>$.RedirectUri
           },
           // Redirect URL
           $.RedirectUri,
           // Response type
           Comm.OAUTH_RESULT_TYPE_URL,
           // Value to look for
           {"code"=>"value"}
           );
    }

The makeOAuthRequest call will cause the login prompt to appear in Garmin Connect Mobile. Once the user has completed credential entry, the accessCodeResult callback will be invoked with the response from the authenticating server, including the temporary access code. At this point, we no longer need the browser, and we can use the makeWebRequest call to request the access token with an HTTP POST request. If this request is successful, we will call our delegate’s handleResponse method with the access token.

    // Convert the authorization code to the access token
    function getAccessToken(accessCode) {
       // Make HTTPS POST request to request the access token
       Comm.makeWebRequest(
           // URL
           "https://www.strava.com/oauth/token",
           // Post parameters
           {
               "client_secret"=>$.ClientSecret,
               "client_id"=>$.ClientId,
               "code"=>accessCode
           },
           // Options to the request
           {
               :method => Comm.HTTP_REQUEST_METHOD_POST
           },
           // Callback to handle response
           method(:handleAccessResponse)
       );
    }

    // Callback to handle receiving the access code
    function handleAccessResponse(responseCode, data) {
       // If we got data back then we were successful. Otherwise
       // pass the error onto the delegate
       if( data != null) {
           _delegate.handleResponse(data);
       } else {
           Sys.println("Error in handleAccessResponse");
           Sys.println("data = " + data);
           _delegate.handleError(responseCode);
       }
    }

Connect IQ offers nonvolatile storage of app properties. For app store apps, the persisted data is encrypted using a randomly generated asymmetric key, and access to those properties is validated with digital signatures. We can use the app properties to store away the access token we receive from the server.

    // Handle a successful response from the server
    function handleResponse(data) {
       // Store the access and refresh tokens in properties
       App.getApp().setProperty("refresh_token", data["refresh_token"]);
       App.getApp().setProperty("access_token", data["access_token"]);
       // Store away the athlete id
       App.getApp().setProperty("athlete_id", data["athlete"]["id"]);
       // Switch to the data view
       Ui.switchToView(new StravaView(), null, Ui.SLIDE_IMMEDIATE);
    }

Once we have the access token, we can use it to make authenticated requests to the server by passing the access token in the HTTP headers.

       Comm.makeWebRequest(
           url,
           _parameters,
           {
               :method=>Comm.HTTP_REQUEST_METHOD_GET,
              :headers=>{ "Authorization"=>"Bearer " + accessToken }
           },
           method(:onResponse)
       );

Keep It Brief

The Connect IQ Communication API brings the wearable web to Garmin devices. However, there are some subtleties to how to expose a web service to a Garmin device. Since all of the communication takes place over a Bluetooth® Smart (you may know this as Bluetooth LE or BLE) connection, the device is bandwidth limited. Data transfers through the Connect IQ SDK will have a transfer speed less than 1 Kb/s, generally between 400 and 800 bytes/s. A single tweet being pulled from Twitter’s API can be upwards of 2.5 Kb. Connect IQ does some magic under the hood to minimize the amount of data that’s transferred from the phone to the watch, but you can quickly see how pulling a user’s last few tweets could be somewhat time consuming.

Be sure to read the next OAuth article: How Green Button Ingeniously Extended The OAuth Standard Without Forking It

 

Comments (0)