Skip to content
Developerhome
X3

Refresh access token with refresh token

  Less than to read

Before starting this step, please read the Authorization and Token Management topic, especially about the Token Management.

First, you add the case of access token renewal in the handlebars template for token management accessToken.handlebars in views folder:

  <h2>Generate access token</h2>

  <div id="alertMessage" class="alert alert-warning alert-dismissible fade show " style="display:none" role="alert">
      <span id="alertMessageContent"></span>
      <button type="button" class="close" onclick="$('#alertMessage').hide()">
          <span aria-hidden="true">&times;</span>
      </button>
  </div>
  <div id="successMessage" class="alert alert-success alert-dismissible fade show " style="display:none" role="alert">
      <span id="successMessageContent"></span>
      <button type="button" class="close" onclick="$('#successMessage').hide()">
          <span aria-hidden="true">&times;</span>
      </button>
  </div>

  <div class="input-group mb-3">
      <div class="input-group-prepend w-12-c">
          <span class="input-group-text w-100">{{inputTitle}}</span>
      </div>
      <input type="text" class="form-control" id="access_code_or_refresh_token" value="{{code}}">
  </div>

  <div id="tokenDetails" class="alert details-box alert-dismissible fade show " style="display:none" role="alert">
      <span id="tokenDetailsContent"></span>
  </div>

  <button type="button" class="btn btn-secondary" onclick="generateAccessToken({{refresh}});">Fetch token</button>
  <button type="button" class="btn btn-secondary"  {{#unless allowDelete}} style="display:none"  {{/unless}} onclick="deleteRefreshToken({{refresh}});">Delete refresh token</button>

  <script type="text/javascript" src="./js/tokenHelper.js"></script>
  <script>
  loadToken({{refresh}});

  </script>

Then add loadToken function into tokenHelper.js in js folder to load the refresh token in order to generate access token:

  function generateAccessToken(refresh)
  {
      $('#alertMessage').hide();
      $('#successMessage').hide();
      var request = new XMLHttpRequest();

      var path;

      if (refresh) {
          path = '/api/refreshAccessToken';
      } else {
          path = '/api/initiateAccessToken';
      }
      console.log(path);
      request.open('POST', path, true);
      let sessionId = localStorage.getItem('sessionId');
      var data = {
          "access_code_or_refresh_token" : document.getElementById('access_code_or_refresh_token').value,
          "sessionId": sessionId
      };

      request.setRequestHeader("content-type","application/json");
      request.send(JSON.stringify(data));
      request.onreadystatechange = function() {

          if (request.readyState == 4 && (request.status == 200 || request.status == 0)) {
              var result = JSON.parse(request.responseText);
              $('#successMessageContent').html("access_token : " + result.access_token
                  + "<br />scope : " + result.scope
                  + "<br />token_type : " + result.token_type
                  + "<br />expires_in : " + result.expires_in
                  + "<br />refresh_token : " + result.refresh_token
                  + "<br />refresh_token_expires_in : " + result.refresh_token_expires_in
                  + "<br />customer_id : " + result.customerId)

              localStorage.setItem('token', JSON.stringify({ refresh_token: result.refresh_token }));
              if (refresh) {
                  document.getElementById('access_code_or_refresh_token').value = result.refresh_token;
              }
              $('#successMessage').show();
              loadCurlCommands();
          } else if (request.readyState == 4 ) {
              var result = JSON.parse(request.responseText);
              $('#alertMessageContent').text(result.message);
              $('#alertMessage').show();
              loadCurlCommands();
          }
      };
  }



  function deleteRefreshToken()
  {
      $('#alertMessage').hide();
      $('#successMessage').hide();
      var request = new XMLHttpRequest();

      var path;

      path = '/api/deleteRefreshToken';

      console.log(path);
      request.open('POST', path, true);
      let sessionId = localStorage.getItem('sessionId');
      var data = {
          "access_code_or_refresh_token" : document.getElementById('access_code_or_refresh_token').value,
          "sessionId": sessionId
      };

      request.setRequestHeader("content-type","application/json");
      request.send(JSON.stringify(data));
      request.onreadystatechange = function() {

          if (request.readyState == 4 && (request.status == 200 || request.status == 0)) {
              var result = JSON.parse(request.responseText);

              $('#successMessageContent').html("Refresh token successfully deleted");
              localStorage.setItem('token', "");
              if (refresh) {
                  document.getElementById('access_code_or_refresh_token').value = "";
              }
              $('#successMessage').show();
              loadCurlCommands();
          } else if (request.readyState == 4 ) {
              var result = JSON.parse(request.responseText);
              $('#alertMessageContent').text(result.message);
              $('#alertMessage').show();
              loadCurlCommands();
          }
      };
  }

  function loadToken(refresh) {

      if (!refresh) {
          return;
      }
      let data = JSON.parse(localStorage.getItem('token'));

      if (data && data.refresh_token) {

          document.getElementById('access_code_or_refresh_token').value = data.refresh_token;
          let request = new XMLHttpRequest();

          request.open('POST', '/api/decodeToken', true);
          let content = {
              "token" : data.refresh_token
          };

          request.setRequestHeader("content-type","application/json");
          request.send(JSON.stringify(content));
          request.onreadystatechange = function() {

              if (request.readyState == 4 && (request.status == 200 || request.status == 0)) {
                  let result = JSON.parse(request.responseText);
                  console.log(request.responseText);
                  let content = "<b>Current Refresh token details</b> <br />Configuration : " + result.url + "<br /> Scope : " + result.scopes;

                  $('#tokenDetailsContent').html(content);

                  $('#tokenDetails').show();
              }
          };
      }
  }

The API call need to be intercepted for renewal and deletion cases , therefore you implement the server side interceptor: you add new code to the tokenHandler.js in lib folder:

  let request = require('./requestHelper');
  let jwt = require('jsonwebtoken');

  let token = {
      access_token: "",
      refresh_token: ""
  };

  exports.decodeCurrentToken = function (req, res) {
      res.send(jwt.decode(req.body.token));
  };

  exports.accessToken = function() {
      return token.access_token;
  };
  exports.refreshToken = function() {
      return token.refresh_token;
  };
  exports.refreshTokenExpiresIn = function() {
      return token.refresh_token_expires_in;
  };
  exports.initiate = async function(req, res, bgColor, buttonColor) {
      let redirectUrl = process.env.REDIRECT_URL ? process.env.REDIRECT_URL : process.env.ENV_URL + "/callback";
      let data = {
              code: req.body.access_code_or_refresh_token,
              client_id: process.env.CLIENT_ID,
              client_secret: process.env.CLIENT_SECRET,
              grant_type: "authorization_code",
              redirect_uri: redirectUrl
          };

      console.log(process.env.ENDPOINT_URL + "/api/token" + data);


      let response = await  request.post(req, {headers: {'content-type' : 'application/json'}, url:process.env.ENDPOINT_URL + "/token", body: JSON.stringify(data)});

          if (response && response.statusCode) {
              console.log(response.statusCode);
              if (response.statusCode === 201) {
                  let result = JSON.parse(response.body);
                  token = result;
                  res.send(result);
              } else {
                  let message = "An error occurred";
                  let result = JSON.parse(response.body);
                  if (result && result.message) {
                      message = result.message;
                  }
                  res.status(400).send('{"message": "' + message + '"}');
              }
          }

  };

  exports.refresh = async function(req, res, refreshToken) {
      console.log(refreshToken ? refreshToken : req.body.access_code_or_refresh_token);

      let data = {
          refresh_token: refreshToken ? refreshToken : req.body.access_code_or_refresh_token,
          client_id: process.env.CLIENT_ID,
          client_secret: process.env.CLIENT_SECRET,
          grant_type: "refresh_token"
      };

      console.log(process.env.ENDPOINT_URL + "/api/token" + data);

      console.log("refresh call");
      let response = await  request.post(req,{headers: {'content-type' : 'application/json'}, url:process.env.ENDPOINT_URL + "/token", body: JSON.stringify(data) }, true);

          if (response && response.statusCode) {
              if (response.statusCode === 201) {
                  let result = JSON.parse(response.body);
                  console.log(response.body);
                  token = result;
                  res.send(result);
              } else {
                  let message = "An error occured";
                  let result = JSON.parse(response.body);
                  if (result && result.message) {
                      message = result.message;
                  }
                  if (response.statusCode === 404) {
                      message = "refresh token does not exists";
                  }
                  res.status(400).send('{"message": "' + message + '"}');
              }
          }

  };


  exports.delete = async function(req, res, refreshToken) {
      console.log(refreshToken ? refreshToken : req.body.access_code_or_refresh_token);
      let data =
          "?refresh_token="  + (refreshToken ? refreshToken : req.body.access_code_or_refresh_token) +
          "&client_id=" + process.env.CLIENT_ID +
          "&client_secret=" + process.env.CLIENT_SECRET;


      console.log(process.env.ENDPOINT_URL + "/api/token" + data);

      console.log("delete call");
      let response = await  request.delete(req,{headers: {'content-type' : 'application/json'}, url:process.env.ENDPOINT_URL + "/token" + data});

          console.log("Response status: " +response.statusCode);
          if (response && response.statusCode) {
              if ((response.statusCode === 200) || (response.statusCode === 201)) {
                  console.log(" ============== DELETE DONE ================");
                  res.status(200).send("{}");
              } else {
                  let message = "An error occured";
                  let result = JSON.parse(body);
                  if (result && result.message) {
                      message = result.message;
                  }
                  res.status(400).send('{"message": "' + message + '"}');
              }
          }

  };

  exports.getNewAccessTokenFromRefreshToken = async function(req) {
      if (!token.refresh_token) {
          throw new Error("Refresk token needed");
      }
      let data = {
          refresh_token: token.refresh_token,
          client_id: process.env.CLIENT_ID,
          client_secret: process.env.CLIENT_SECRET,
          grant_type: "refresh_token"
      };

      console.log(process.env.ENDPOINT_URL + "/api/token" + data);
      let result = await request.post(req, {headers: {'content-type' : 'application/json'}, url:process.env.ENDPOINT_URL + "/token" , body: JSON.stringify(data)});

      if (result && result.statusCode) {
          if (result.statusCode === 201) {
              let body = JSON.parse(result.body);
              console.log(body);
              token = body;
              return body;
          } else {
              let message = "An error occured while refreshing access token";
              let body = JSON.parse(result.body);
              if (body && body.message) {
                  message += ": " + body.message;
              }
              throw new Error(message);
          }
      }
  };

Then add new path of your application to main.js :

const express = require('express');
const exphbs = require("express-handlebars");
const fileUpload = require("express-fileupload");
const app = express();
const bodyParser = require('body-parser');
const fs = require('fs')
const https = require('https')

const config = require('./lib/config.js');
const callbackHandler = require('./lib/callbackHandler');
const tokenHandler = require('./lib/tokenHandler');
const requestLib = require('./lib/requestLib');
const port = 5050;

let envColor = process.env.COLOR;
let bgColor;
let buttonColor;
switch (envColor) {
    case "red":
        bgColor = "danger";
        buttonColor = "warning";
        break;
    case "grey":
        bgColor = "dark";
        buttonColor = "secondary";
        break;
    default:
        bgColor = "primary";
        buttonColor = "info";
        break;
}

app.use('/', express.static('app'));
app.use(bodyParser.json());       // to support JSON-encoded bodies
app.use(fileUpload());


app.get('/config', function (req, res) {
    config.get(req, res);
});

app.get('/settings', function (req, res) {
    res.render("settings", {bgColor: bgColor, btColor: buttonColor});
});

let redirectUrl = process.env.REDIRECT_URL ? process.env.REDIRECT_URL : process.env.ENV_URL + "/callback";
let urlParams = process.env.ENDPOINT_URL + "/token/authorise?client_id=" + process.env.CLIENT_ID + "&state=astate&redirect_uri=" + redirectUrl;
app.get('/pairingRequest', function (req, res) {
    res.render("pairingRequest", {bgColor: bgColor, btColor: buttonColor, url: urlParams, config_url: process.env.CONFIG_URL});
});

app.get('/callback', function (req, res) {
    callbackHandler.handleCallback(req, res, bgColor, buttonColor);
});

app.get('/api/curlCommands', function (req, res) {
    requestLib.getCurlCommands(req, res);
});

app.post('/api/initiateAccessToken', async function (req, res) {
    tokenHandler.initiate(req, res);
});

app.post('/api/refreshAccessToken', async function (req, res) {
    tokenHandler.refresh(req, res);
});

app.post('/api/deleteRefreshToken', async function (req, res) {
    tokenHandler.delete(req, res);
});

app.get('/generateAccessToken', function (req, res) {
    res.render("accessToken", {
        bgColor: bgColor,
        btColor: buttonColor,
        code: req.query.code,
        refresh: false,
        inputTitle: "Access code"
    });
})

app.get('/refreshAccessToken', function (req, res) {
    res.render("accessToken", {bgColor: bgColor, btColor: buttonColor, code: tokenHandler.refreshToken(), refresh: true, inputTitle: "Refresh token", allowDelete: true});
});

app.engine('handlebars', exphbs({defaultLayout: 'main', layoutsDir: 'views/layouts'}));
app.set('view engine', 'handlebars');
app.set('views', 'views');

https.createServer({
    key: fs.readFileSync('server.key'),
    cert: fs.readFileSync('server.cert')
  }, app).listen(port, function () {
});
  • In the end of this step we have this file structure:

    Step 3

In VSCode, launch the debug to see the result at https://localhost:5050/refreshAccessToken.

Step 3

Then click on “Fetch token” to get new access token from the current refresh token:

Step 3

Congratulation, you obtain your new access token and new refresh token from your old refresh token.

You can also download the VSCode project at the end of this step here. Please note, after downloading the zip file, you have to run npm i to install all package dependencies before debugging with VSCode.