Sample App

To help see the different ways the JavaScript SDK can be used, we have created a sample application. This guide will walk you through the different samples and the required code to do something similar.

Prerequisites

This documentation assumes you have already read the JavaScript SDK documentation. We won't be covering the details of loading the SDK on your site. You can find that documentation here:

You should also read Authentication documentation before continuing this document.

You will also need the application credentials provided to you by Nike, and have whitelisted your redirect URIs.

Note: If you are using the auth code flow, you should never exchange a code for a token or refresh a token with a client-side request to Nike. The sample app has an example of how to pass the information to your server to allow the server to make the request.

Dependencies

The sample app was created using node.js and the express framework. In no way are these required to use the JavaScript SDK, they are just the technologies we chose to create a web application to demonstrate our examples. To run the app you will need to install node.js and download the app at the link above. Once you have unzipped the app, switch to the directory with app.js and package.json, and run:
npm install
Next you need to identify your app in app.js, enter your client id and client secret in the following code:
app.locals({
    client_id: 'your client id',
    client_secret: 'your client secret',
    redirect_uri: 'http://localhost:3000/authcode_redirect'
});

Starting the App

Once the dependencies are installed you can start the application by running the following in the same directory:
node app.js
You should now be able to load the app in your browser at http://localhost:3000. The home page has links to the different examples in the sample app.

Auth Code Flow

This example shows how you can use the auth code flow with the JavaScript SDK. The easiest way to authenticate your users with the JavaScript SDK is to use the implicit flow, but the implicit flow comes with its own disadvantages, the main one being that you can not currently refresh a users token when using the implicit flow. This example will show you how to pass an access token to the JavaScript SDK while using the auth code flow, later we will also show how you can refresh that token.

Init the SDK

As covered in the JavaScript SDK Documentation, you will want to call NIKEPLUS.init with a response_type of "code" when using the auth code flow.
NIKEPLUS.init({
    client_id: "your client id",
    response_type: "code",
    redirect_uri: "http://localhost:3000/authcode_redirect"
});
Calling init in this way will cause the Nike login page to redirect to http://localhost:3000/authcode_redirect with a code, a token type and a state (if you supplied one in init) in the querystring. This is where using the auth code flow with the JavaScript SDK differs somewhat from how most would do it when not using the JavaScript SDK.

Exchanging the Code for a Token

Usually when using the auth code flow, your server side code would read the code off of the URL and make a server to server call to exchange it for a token. That won't work when using the JavaScript SDK, we need to be able to pass the returned token to the JavaScript SDK. To achieve that, we read the information from the URL and pass it to the server via an ajax call.

The redirect page ideally will contain very little code, it does nothing except handle responses from the authentication and direct the user back to where they should be after authentication. After you init the JavaScript SDK as you have on other pages, use the following block of code (or something similar) to pass the auth data to the server.

jQuery(document).ready(function(){
    jQuery.ajax({
        url: "/authcode_exchange" + window.location.search,
        dataType: "json",
        success: function(response){
            // will show what to do here shortly
        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log(textStatus, errorThrown)
        }
    });
});
You can see all the code for the redirect page in views/authcode_redirect.ejs. In this code we are passing the auth data to the server by appending it to the URL with window.location.search. The important work here is being done by the authcode_exchange route which you can find in the routes/index.js file and is explained below.
exports.authcode_exchange = function(req, res){
    res.setHeader('Content-Type', 'application/json');
    if (req.query && req.query.code) {
        var https = require('https');
        var post_data = JSON.stringify({
            client_id: req.app.locals.client_id,
            client_secret: req.app.locals.client_secret,
            redirect_uri: req.app.locals.redirect_uri,
            code: req.query.code,
            state: req.query.state,
            grant_type: 'authorization_code'
        });
        var post_options = {
            hostname: 'api.nike.com',
            port: '443',
            path: '/oauth/2.0/token',
            method: 'POST',
            headers: {
              'Accept': 'application/json',
              'Content-Type': 'application/json',
              'Content-Length': post_data.length
            }
        };
In the above section of the code we are setting up the post of the code to https://api.nike.com/oauth/2.0/token. When exchanging a code for a token you need to send the client ID, redirect URI (must be the same URI that was used when the code was acquired), the auth code and a grant type of "authorization_code".
        var post_req = https.request(post_options, function(post_res) {
            post_res.setEncoding('utf8');
            post_res.on('data', function (chunk) {
                var jsonresp = JSON.parse(chunk),
                    responseObj = {},
                    redis = require("redis"),
                    client = redis.createClient();

                client.on("error", function (err) {
                    console.log("Error " + err);
                });

                if (jsonresp.access_token) {
                    client.set(jsonresp.user_id, JSON.stringify(jsonresp), redis.print);
                    ifExistsAdd("access_token", jsonresp, responseObj); 
                    ifExistsAdd("expires_in", jsonresp, responseObj); 
                    ifExistsAdd("token_type", jsonresp, responseObj); 
                    ifExistsAdd("user_id", jsonresp, responseObj); 
                    ifExistsAdd("profile_img_url", jsonresp, responseObj); 
                    res.end(JSON.stringify(responseObj)); 
                } else {
                    res.send(post_res.statusCode, chunk);
                    res.end();
                } 
            });
        });
        post_req.on("error", function(err){console.log("error", err)});
        post_req.write(post_data);
        post_req.end();
    } else {
        res.send(400, JSON.stringify({ error: "an auth code is required" }, null, 3));
        res.end();
    }
};
In the rest of the authcode_exchange route we are handling the response from the token endpoint. If there is an access token in the response we are building the JSON that we want to return to the browser. You do not want to return the refresh token to the browser, don't just pass the response from the token endpoint back to the browser. In this code we are building a new response object (responseObj) that will be returned as JSON to the browser. If you plan to use the refresh token you should store it for later use. We will explain further in the authorization code flow with Refresh example, but we are storing the authorization data with this line of code:
client.set(jsonresp.user_id, JSON.stringify(jsonresp), redis.print);
By returning the auth info in the authcode_exchange response, the browser now has access to an access token.

Passing the Access Token to the JS SDK

So now that the browser has access to an access token we need to pass that information to the JavaScript SDK. To show that lets look at the success method on the original Ajax call to authcode_exchange.
jQuery(document).ready(function(){
    jQuery.ajax({
        url: "/authcode_exchange" + window.location.search,
        dataType: "json",
        success: function(response){
            if (response && response.access_token) {
                NIKEPLUS.setAuthData(response, function(){
                    var qs = deserialize(window.location.search.substring(1));
                    if (qs.state === "refresh") {
                        window.location.replace("/authcode_with_refresh");
                    } else {
                        window.location.replace("/authcode");
                    }
                });
            }
        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log(textStatus, errorThrown)
        }
    });
});
In the success function we are validating that the Ajax response has an access token and then passing it directly to 'setAuthData'. At a minimum the 'setAuthData' requires an object with the access_token and expires_in (seconds until token expires). Once you have passed this data to 'setAuthData' you can make calls with the JavaScript SDK. The rest of the success function is parsing the querystring to read the state parameter which we are using to determine where we should redirect the user after authentication is complete.

Token Refresh

Access tokens returned from Nike are only valid for 1 hour. Your users would have to authenticate with Nike every time they used your site if it had been more than a couple hours since they last used your site. This example shows how you can seamlessly refresh your users tokens for the entire life of the refresh token. Store the refresh token when the user first authenticates with Nike and use it to keep refreshing the token behind the scenes. Using the auth code flow while refreshing tokens is very similar to using the auth code flow as explained above. Read the auth code flow section before reading this section where we will highlight the differences.

Storing the Auth Data

The first difference when you are planning to refresh tokens for your users is that you need to store the refresh token after users authenticate. In this example we are storing it using Redis, but it can be stored in any data store you may be using. By storing the refresh token you can retrieve a Nike access token in the future without the user having to authenticate again with Nike.
post_res.on('data', function (chunk) {
    var jsonresp = JSON.parse(chunk),
        responseObj = {},
        redis = require("redis"),
        client = redis.createClient();

    client.on("error", function (err) {
        console.log("Error " + err);
    });

    if (jsonresp.access_token) {
        client.set(jsonresp.user_id, JSON.stringify(jsonresp), redis.print);
        ifExistsAdd("access_token", jsonresp, responseObj); 
        ifExistsAdd("expires_in", jsonresp, responseObj); 
        ifExistsAdd("token_type", jsonresp, responseObj); 
        ifExistsAdd("user_id", jsonresp, responseObj); 
        ifExistsAdd("profile_img_url", jsonresp, responseObj); 
        res.end(JSON.stringify(responseObj)); 
    } else {
        res.send(post_res.statusCode, chunk);
        res.end();
    } 
});
In this code above from the authcode_exchange route we are storing the JSON response from the token endpoint with the user_id as the key.

Checking for Token Expiration

In order to refresh the token in the SDK before it expires you will need to monitor the token, you can see how to do that in views/authcode_with_refresh.ejs.
function pollToken() {
    NIKEPLUS.getAuthData(function(authData){
        if (authData.status && authData.status === "validToken") {
            if (authData.authData && authData.authData.expires_in && authData.authData.expires_in < 180) {
                jQuery(document).ready(function(){
                    jQuery.ajax({
                        url: "/authcode_refresh?user_id=" + authData.authData.user_id + "&access_token=" + authData.authData.access_token,
                        dataType: "json",
                        success: function(response){
                            if (response && response.access_token) {
                                NIKEPLUS.setAuthData(response, function(){
                                    setTimeout(pollToken, 1000);
                                });
                            }
                        },
                        error: function(jqXHR, textStatus, errorThrown){
                            console.log(textStatus, errorThrown)
                        }
                    });
                });
            } else {
                setTimeout(pollToken, 1000);
            }
        }
    });
}
The above function should be called after you have called init for the SDK. It will call NIKEPLUS.getAuthData every second to see how much time remains before the token expires. In this example we are going to refresh the token when there is less than 180 seconds (3 mins) until the token expires. Adjust any of the times in this example to fit your needs.

Refreshing the Token

When the token only has 3 minutes left, we make another ajax call, very similar to the authcode_exchange call. This time we pass authcode_refresh our current access token and the user_id; coveniently both of these are available in the response from getAuthData. The user id will be used to retrieve the refresh token from Redis, and in our use case the access token will be used to validate our data in Redis.

NOTE: Using the previous access token to validate the data would not work in a scenario where you are getting a token for somebody who does not currently have a token, e.g. somebody who came back to your site a week after first authenticating with Nike. In that use case you would want some other security measure to make sure the correct user is the one making a request for a new token.

The authcode_refresh route can be seen in routes/index.js.

exports.authcode_refresh = function(req, res){
    res.setHeader('Content-Type', 'application/json');
    if (req.query && req.query.access_token && req.query.user_id) {
        var redis = require("redis"),
            data,
            client = redis.createClient();

        client.on("error", function (err) {
            console.log("Error " + err);
        });

        client.get(req.query.user_id, function(err, response){
            data = JSON.parse(response);
            if (data.access_token && data.access_token === req.query.access_token) {
                var https = require('https');
                var post_data = JSON.stringify({
                    client_id: req.app.locals.client_id,
                    client_secret: req.app.locals.client_secret,
                    refresh_token: data.refresh_token,
                    grant_type: 'refresh_token'
                });
                var post_options = {
                    hostname: 'api.nike.com',
                    port: '443',
                    path: '/oauth/2.0/token',
                    method: 'POST',
                    headers: {
                      'Accept': 'application/json',
                      'Content-Type': 'application/json',
                      'Content-Length': post_data.length
                    }
                };
Just like the authcode_exchange route, we will need to post to the token endpoint. This time we will pass the client id, the refresh token retrieved from your data store, and a grant type of "refresh_token".
                var post_req = https.request(post_options, function(post_res) {
                    post_res.setEncoding('utf8');
                    post_res.on('data', function (chunk) {
                        var jsonresp = JSON.parse(chunk),
                            responseObj = {};

                        if (jsonresp.access_token) {
                            client.set(jsonresp.user_id, JSON.stringify(jsonresp), redis.print);
                            ifExistsAdd("access_token", jsonresp, responseObj);
                            ifExistsAdd("expires_in", jsonresp, responseObj);
                            ifExistsAdd("token_type", jsonresp, responseObj);
                            ifExistsAdd("user_id", jsonresp, responseObj);
                            ifExistsAdd("profile_img_url", jsonresp, responseObj);
                            res.end(JSON.stringify(responseObj));
                        } else {
                            res.send(post_res.statusCode, chunk);
                            res.end();
                        }
                    });
                });
                post_req.on("error", function(err){console.log(err)});
                post_req.write(post_data);
                post_req.end();
            } else {
                res.send(400, JSON.stringify({ error: "invalid request" }, null, 3));
                res.end();
            }
        });
    } else {
        res.send(400, JSON.stringify({ error: "invalid request" }, null, 3));
        res.end();
    }
};
The response from refreshing a token is the same as when exchanging a code for a token, so again we sanitize the data that will be returned to the browser.

Passing the New Token to the SDK

Just like when exchanging a code for a token, we pass the response from authcode_refresh directly to setAuthData.
jQuery(document).ready(function(){
    jQuery.ajax({
        url: "/authcode_refresh?user_id=" + authData.authData.user_id + "&access_token=" + authData.authData.access_token,
        dataType: "json",
        success: function(response){
            if (response && response.access_token) {
                NIKEPLUS.setAuthData(response, function(){
                    setTimeout(pollToken, 1000);
                });
            }
        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log(textStatus, errorThrown)
        }
    });
});
Once the token has been refreshed we also need to call pollToken again so that when the new token expires it can be refreshed again.

Implicit Flow

The implicit flow is the easiest one to use with the JavaScript SDK. It was designed for clients implemented in a browser and returns a token directly to the client rather than an authorization code. The downside to the implicit flow is that there is no way to refresh the token. When the token expires your user will have to reauthenticate with Nike to receive a new token.

Init the SDK

As covered in the JavaScript SDK Documentation, you will want to call NIKEPLUS.init with a response_type of "code" when using the auth code flow.
NIKEPLUS.init({
    client_id: "your client id",
    response_type: "token",
    redirect_uri: "http://localhost:3000/implicit_redirect"
});
Calling init in this way will cause the Nike login page to redirect to http://localhost:3000/implicit_redirect with access_token, expires_in, token_type, user_id and profile_img_url in the URL hash.

Getting the Access Token

When using the implicit flow the JavaScript SDK will parse the authentication data from the redirect URL. The redirect page should only contain the JavaScript SDK. Call the init function the same way you do on other pages, except this time include a success callback in the init call.
NIKEPLUS.init({
    client_id: "your client id",
    response_type: "token",
    authSuccess: function(authData){
        window.location.replace("/implicit");
    },
    redirect_uri: "http://localhost:3000/implicit_redirect"
});
The 'authSuccess' callback is called when the SDK has parsed the authorization data and stored it in local storage. The 'authSuccess' callback is also passed the 'authData' that was just parsed. In the 'authSuccess' callback you will want to redirect the user to the correct location in the application. A primary reason for the redirect, besides the obvious, is to get rid of the authorization data from the URL hash. At any time after authentication, you can retrieve the access token and when it expires by calling 'NIKEPLUS.getAuthData'.

Re-Authenticating

Any time you try and make an API call with the JavaScript SDK and the access token has expired since the last time you tried to use it, the auth.expiredToken event will fire. This is a good event to subscribe a callback that will trigger your authentication flow for a new token.

Implicit Flow with Popup Window

One of the options available in the NIKEPLUS.init call is oauthPopup. By default this is false, but if you set it to true the Nike login screen will open in a popup window rather than redirecting the entire page. The rest of the flow is the same as described above, but the redirect after authentication will happen in the popup window. The authSuccess callback will need to handle the closing of that window.
NIKEPLUS.init({
    client_id: "your client id",
    response_type: "token",
    authSuccess: function(authData){
        opener.location.reload();
        window.close();
    },
    redirect_uri: "http://localhost:3000/implicit_redirect_popup"
});
In this example we are reloading the page that opened the login page, and then closing the popup. The reloading of the opener is just to force the sample app to see the logged in state, you can do this however you need to in your application. By calling window.close() in authSucces you can be sure that the SDK has had time to parse the auth data out of the URL and store it before the window closes.

Kitchen Sink

This page in the app just demonstrates almost everything the JavaScript SDK can do. It logs the result of calling NIKE.getAuthData; it subscribes to all the events, and logs everytime an event fires and the data it sends to the callback, and makes a call to all the api endpoints and logs the response. The only methods it does not call are NIKEPLUS.login, NIKEPLUS.logout and NIKEPLUS.setAuthData. You can call these methods in the browser console and the resulting triggered events will be logged on the page.