Angular 2+ Token Renewal

Sample Project

Download a sample project specific to this tutorial configured with your Auth0 API Keys.

System Requirements
  • Angular 2+
Show requirements

For security, keep the expiry time of a user's access token short.

When you create an API in the Auth0 dashboard, the default expiry time for browser flows is 7200 seconds (2 hours).

This short expiry time is good for security, but can affect user experience. To improve user experience, provide a way for your users to automatically get a new access token and keep their client-side session alive. You can do this with Silent Authentication.

You can control the expiry time of an access token from the APIs section. You can control the expiry time of an ID token from the Clients section. These settings are independent.

Server Setup

To renew the user's access token, you need to serve a static HTML file. You can choose any server setup to do this.

The example below uses Node.js and Express.

Create a simple server with Express and add a file called silent.html.

The silent.html file receives and parses the result of a token renewal. An instance of the WebAuth object from auth0.js is created. The parseHash method returns an object from the hash with the authentication result. The object is then posted back to the parent window and the client-side session starts again.

// server.js

const express = require('express');
const app = express();
const cors = require('cors');
const staticFile = require('connect-static-file');

app.use(cors());
app.use('/silent', staticFile(`${__dirname}/silent.html`));

app.listen(3001);
console.log('Listening on http://localhost:3001');
<!-- silent.html -->

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://cdn.auth0.com/js/auth0/8.10.1/auth0.min.js"></script>
  <script>
    var webAuth = new auth0.WebAuth({
      domain: 'YOUR_AUTH0_DOMAIN',
      clientID: 'YOUR_CLIENT_ID',
      scope: 'openid profile',
      responseType: 'token id_token',
      redirectUri: 'http://localhost:4200'
    });
  </script>
  <script>
    webAuth.parseHash(window.location.hash, function (err, response) {
      parent.postMessage(err || response, 'http://localhost:4200');
    });
  </script>
</head>
<body></body>
</html>

Add http://localhost:3001/silent to the Allowed Callback URLs section in your application's Client Settings.

Add Token Renewal

To the AuthService service, Add a method which calls the renewAuth method from auth0.js. If the renewal is successful, use the existing setSession method to set the new tokens in local storage.

The method loads your silent callback page in an invisible iframe. Then, the method makes a call to Auth0 and gives back the result.

// src/app/auth/auth.service.ts

public renewToken() {
  this.auth0.renewAuth({
    audience: '{YOUR_API_IDENTIFIER}',
    redirectUri: 'http://localhost:3001/silent',
    usePostMessage: true
  }, (err, result) => {
    if (err) {
      console.log(err);
    } else {
      this.setSession(result);
    }
  });
}

Add a method called scheduleRenewal to set up the time when authentication is silently renewed.

Define the refreshSubscription class property, which will hold a reference to the subscription that refreshes your token.

// src/app/auth/auth.service.ts

// ...
public scheduleRenewal() {
  if(!this.isAuthenticated()) return;
  this.unscheduleRenewal();

  const expiresAt = JSON.parse(window.localStorage.getItem('expires_at'));

  const source = Observable.of(expiresAt).flatMap(
    expiresAt => {

      const now = Date.now();

      // Use the delay in a timer to
      // run the refresh at the proper time
      return Observable.timer(Math.max(1, expiresAt - now));
    });

  // Once the delay time from above is
  // reached, get a new JWT and schedule
  // additional refreshes
  this.refreshSubscription = source.subscribe(() => {
    this.renewToken();
    this.scheduleRenewal();
  });
}

public unscheduleRenewal() {
  if(!this.refreshSubscription) return;
  this.refreshSubscription.unsubscribe();
}

This lets you schedule token renewal any time. For example, you can schedule a renewal after the user logs in and then again, if the page is refreshed.

In the setSession method, add the function right after setting the access_token and id_token into local storage.

// src/app/auth/auth.service.ts

private setSession(authResult): void {
  // Set the time that the access token will expire at
  const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + Date.now());

  localStorage.setItem('access_token', authResult.accessToken);
  localStorage.setItem('id_token', authResult.idToken);
  localStorage.setItem('expires_at', expiresAt);

  this.scheduleRenewal();
}

Add a call to the scheduleRenewal method in the root app component to schedule a renewal when the page is refreshed.

// src/app/app.component.ts

export class AppComponent {

  constructor(public auth: AuthService) {
    auth.handleAuthentication();
    auth.scheduleRenewal();
  }

}

Since client-side sessions should not be renewed after the user logs out, call the unscheduleRenewal method in the logout method to cancel the renewal.

// src/app/auth/auth.service.ts

public logout(): void {
  // Remove tokens and expiry time from localStorage
  localStorage.removeItem('access_token');
  localStorage.removeItem('id_token');
  localStorage.removeItem('expires_at');
  this.unscheduleRenewal();
  // Go back to the home route
  this.router.navigate(['/']);
}

Troubleshooting

If you're having problems with token renewal (for example, you get the login_required error), make sure you're not using Auth0 dev keys for social login. You must use your own social authentication keys.

Previous Tutorial
4. Authorization
Use Auth0 for FREECreate free Account