15 Dec 2019
cat: Angular 2+
0 Comments

Angular 8 OAuth 2 Keep-alive with Refresh Token

Introduction

When authenticating to OAuth, the access token is usually short lived and expires after a few hours or minutes. Some apps will want to use the refresh_token grant to refresh the access token to keep the user logged in. In this tutorial we will create an Angular app and with keep-alive using @ng-core, @ng-keepalive, and angular-2-storage for storing tokens.

Setup

Assuming you already have an app that authenticates using OAuth we can run this command to install the necessary packages. I have covered how to authenticate Angular to OAuth 2 server here and how to make OAuth server using express here where you can also download the sample codes.

npm i @ng-core @ng-keepalive angular-2-storage

Next is to import these packages’s modules NgIdleKeepaliveModule and LocalStorageModule.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { NgIdleKeepaliveModule } from '@ng-idle/keepalive';
import { LocalStorageModule } from 'angular-2-local-storage';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';

@NgModule({
    declarations: [
        AppComponent,
        LoginComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        HttpClientModule,
        NgIdleKeepaliveModule.forRoot(),
        LocalStorageModule.forRoot({
            prefix: 'angular-oauth2-keepalive-example',
            storageType: 'localStorage'
        })
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

Now we can initialize the actions to make when the user is idle or timeout. When the user had idled but started doing something again like moving the mouse cursor, we can refresh the token. If the user did not use the app again, we can delete any values stored in the storage and logout.

import { HttpClient, HttpParams } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { DEFAULT_INTERRUPTSOURCES, Idle } from '@ng-idle/core';
import { LocalStorageService } from 'angular-2-local-storage';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {

    constructor(
        private idle: Idle,
        private storage: LocalStorageService,
        private http: HttpClient
    ) {
    }

    ngOnInit() {

        this.idle.onIdleStart.subscribe(e => {
            console.log('onIdleStart! Maybe warn user');
        });

        this.idle.onIdleEnd.subscribe(e => {

            console.log('onIdleEnd! Refresh token');

            const payload = new HttpParams()
                .append('grant_type', 'refresh_token')
                .append('refresh_token', this.storage.get('refresh_token'))
                .append('client_id', 'api');

            this.http.post('http://192.168.10.10:3000/oauth/access_token', payload, {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }).subscribe((response: any) => {
                this.storage.set('access_token', response.access_token);
                this.storage.set('token_type', response.token_type);
                this.storage.set('expires_in', response.expires_in);
                this.storage.set('refresh_token', response.refresh_token);
                this.storage.set('scope', response.scope);
            });
        });

        this.idle.onTimeout.subscribe(() => {
            console.log('onTimeout! Logout');
            this.storage.clearAll();
            this.idle.stop();
        });
        this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
    }
}

The final step would be to update the login callback. We will store the access_token, refresh_token, and expiry time and start watching the user for activity. We use the expiry time as the time the user can be idle.

Expiry time will be the time the user can be idle and 10 seconds before timeout. If the user interrupts the idling before 10 seconds then we can refresh the token, if not then logout.

import { HttpClient, HttpParams } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Idle } from '@ng-idle/core';
import { LocalStorageService } from 'angular-2-local-storage';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
})
export class LoginComponent implements OnInit {

    // removed for brevity

    constructor(
        private activatedRoute: ActivatedRoute,
        private storage: LocalStorageService,
        private idle: Idle,
        private http: HttpClient
    ) {
    }

    // removed for brevity
    getAccessToken(code: string) {

        const payload = new HttpParams()
            .append('grant_type', 'authorization_code')
            .append('code', code)
            .append('redirect_uri', 'http://localhost:4200/oauth/callback')
            .append('client_id', 'api');

        this.http.post('http://192.168.10.10:3000/oauth/access_token', payload, {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
        }).subscribe((response: any) => {
            this.oauthResponse = response;
            this.storage.set('access_token', response.access_token);
            this.storage.set('token_type', response.token_type);
            this.storage.set('expires_in', response.expires_in);
            this.storage.set('refresh_token', response.refresh_token);
            this.storage.set('scope', response.scope);

            this.idle.setIdle(response.expires_in);
            this.idle.setTimeout(10); // Timeout 10 seconds after being idle

            // Start
            this.idle.watch();
        });
    }

    // removed for brevity
}

Testing it out

We can test it by setting the response.expires_in to a few seconds. The example OAuth server have access token expire in 1 hour and refresh token lasts 14 days.

Angular 8 OAuth 2 Keep-alive with Refresh Token

Angular 8 OAuth 2 Keep-alive with Refresh Token

Angular 8 OAuth 2 Keep-alive with Refresh Token

That is it for this tutorial. Thanks for reading!

References

Be the first to write a comment.

Post A Comment