Using OpenIdConnect with Azure AD, Angular5 and WebAPI Core: Token lifetime management

Introduction

 

By default, tokens have a lifetime of 1h, we’ll see how to manage their lifetime.

Download Azure Active Directory Powershell module

Because there is no UI for tthis, we have to go with Powershell commands to manage our tokens and Microsoft’s session.

Go to Powershell Gallery and download the module:

Connect to your Azure account

> Connect-AzureAD -Confirm

Then type your login / password in modal.

Create a policy

If you have not set a policy yet, you have to create one with the following command:

> New-AzureADPolicy -Definition @('{"TokenLifetimePolicy":{"Version":1, "AccessTokenLifetime":"03:00:00","MaxAgeSessionSingleFactor":"24:00:00"}}') -DisplayName "WebPolicyScenario" -IsOrganizationDefault $true -Type "TokenLifetimePolicy"

AccessTokenLifetime is the duration in hours of the token with a minimum value of 10 minutes and a maximum value of 24 hours.

MaxAgeSessionSingleFactor is the duration in hours and days of the Microsoft session with a minimum value of 10 minutes and a maximum value of Until-revoked (infinite)

Get your policies

If you want to manage your policies you need to display them then identify their Id:

> Get-AzureADPolicy

Then you should see something like this:

Modify your policies

After identifying Id of the policy you want to modify, type the following command to modify the token lifetime policy and / or Microsoft session:

> Set-AzureADPolicy -Definition @('{"TokenLifetimePolicy":{"Version":1, "AccessTokenLifetime":"02:00:00","MaxAgeSessionSingleFactor":"12:00:00"}}') -Id <ObjectId of Policy> -DisplayName "WebPolicyScenario" -IsOrganizationDefault $false

Remove your policies

You can also remove your policies if you wish:

> Remove-AzureADPolicy -Id <ObjectId of Policy>

 

Using OpenIdConnect with Azure AD, Angular5 and WebAPI Core: Introduction

Introduction

 

 

Azure Active Directory (Azure AD) is Microsoft’s multi-tenant, cloud based directory and identity management service. Azure AD combines core directory services, advanced identity governance, and application access management. Azure AD also offers a rich, standards-based platform that enables developers to deliver access control to their applications, based on centralized policy and rules.

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner. OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and session management, when it makes sense for them.

Five scenarios supported by Azure AD:

In this serie of articles article we will demystify Single Page Application (SPA) scenario:

 

This scenario is named Implicit flow in opposition to basic flow in a full back end web application

  1. The user navigates to the web application.
  2. The application returns the JavaScript front end (presentation layer) to the browser.
  3. The user initiates sign in, for example by clicking a sign in link. The browser sends a GET to the Azure AD authorization endpoint to request an ID token. This request includes the application ID and reply URL in the query parameters.
  4. Azure AD validates the Reply URL against the registered Reply URL that was configured in the Azure Portal.
  5. The user signs in on the sign-in page.
  6. If authentication is successful, Azure AD creates an ID token and returns it as a URL fragment (#) to the application’s Reply URL. For a production application, this Reply URL should be HTTPS. The returned token includes claims about the user and Azure AD that are required by the application to validate the token.
  7. The JavaScript client code running in the browser extracts the token from the response to use in securing calls to the application’s web API back end.
  8. The browser calls the application’s web API back end with the access token in the authorization header.

Using OpenIdConnect with Azure AD, Angular5 and WebAPI Core: Angular5 configuration

 

Install required packages and setup Angular 5 application

Firstly install the latest version of Angular with Angular-CLI

npm install -g @angular/cli

Then install an angular version of ADAL.JS:  adal-angular5

npm install --save adal-angular5

Create a AuthService

We will create a AuthService.ts, this service will manage signin, signout and users data :

auth.service.ts:

import { Adal5HTTPService, Adal5Service } from 'adal-angular5';
import { HttpClient, HttpHandler } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthService {

private_user=null;
private_config = {
   tenant:'136544d9-038e-4646-afff-10accb370679', <-- tenantId mentionned in the previous article
   clientId:'257b6c36-1168-4aac-be93-6f2cd81cec43', <-- clientId mentionned in the previous article
   redirectUri:"http://localhost:4200/auth-callback", <-- callback URI mentionned in the previous article
   postLogoutRedirectUri:"http://localhost:4200" <-- same URI as homepage URI mentionned in the previous article
}

constructor(private_adal:Adal5Service) {
   this._adal.init(this._config);
}

public isLoggedIn():boolean {
   return this._adal.userInfo.authenticated;
}

public signout():void {
   this._adal.logOut();
}

public startAuthentication():any {
   this._adal.login();
}

public getName():string {
   return this._user.profile.name;
}

public completeAuthentication():void {
   this._adal.handleWindowCallback();
   this._adal.getUser().subscribe(user=> {
   this._user=user;
   console.log(this._adal.userInfo);
   var expireIn=user.profile.exp-newDate().getTime();
});

}

}

this service is the most important thing, it will be used everywhere in the application.

Create a AuthGuardService

This AuthGuard service is usefull for protecting your page with authentication

You need to implement CanActivate method using Interface with the same name:

auth-guard.service.ts:

import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';

@Injectable()
export class AuthGuardService implements CanActivate {

constructor(private_authService:AuthService) { }

   canActivate():boolean {

   if(this._authService.isLoggedIn()) {
      return true;
   }
   this._authService.startAuthentication();
    return false;
   }

}

CanActivate is fired when you try to access to a route that is protected.

To setup route protection, create a component served by a protected route you want to protected with authentication, and setup route protection in app.module.ts:

Example of a protected component:

protected.component.ts:

import { AuthService } from './../services/auth.service';
import { Component, OnInit } from '@angular/core';

@Component({
   selector:'app-protected',
   templateUrl:'./protected.component.html',
   styleUrls: ['./protected.component.css']
})

export class ProtectedComponent implements OnInit {

   name:String="";
   constructor(private_authService:AuthService) { }

   ngOnInit() {
      this.name=this._authService.getName();
   }

   public signout():void {
      this._authService.signout();
   }
}

protected.component.html:

<p>

Welcome {{name}}

</p>

<a href="javascript:void(0);" (click)="signout();">signout</a>

Protect your routes like this in app.module.ts:

const routes: Routes = [
{
   path:'',
   children: []
},
{
   path:'protected',
   component:ProtectedComponent,
   canActivate: [AuthGuardService]
},
{
   path:'auth-callback',
   component:AuthCallbackComponent
}
];

Create a AuthCallback component

This component will be used as endpoint in your app to manage user creation after receiving the token from Azure Authorization endpoint.

Azure AD will call your page and send the token. If you remember well, the URI has to be declaqred in Azure to make it work.

auth-callback.component.ts:

import { AuthService } from './../services/auth.service';
import { Router, ActivatedRoute } from '@angular/router';
import { Component, OnInit, NgZone } from '@angular/core';

@Component({
   selector:'app-auth-callback',
   templateUrl:'./auth-callback.component.html',
   styleUrls: ['./auth-callback.component.css']
})

export class AuthCallbackComponent implements OnInit {

   constructor(private_router:Router, private_authService:AuthService, private_zone:NgZone, private_activatedRoute:ActivatedRoute) { }

   ngOnInit() {
      this._authService.completeAuthentication();

      setTimeout(() => {this._zone.run(
      () => this._router.navigate(['/protected'])
      );},200);
   }
}

auth-callback.component.html:

<p>Please wait while we redirect you back</p>

The method completeAuthentication is in fact internally a Promise but I execute the redirection after authentication synchronously by using a setTimeout statement.

I used also NgZone object.

Why this?

Passing a callback to promise after authentication in completeAuthentication did not work on Safari IOS, so the work around was using setTimeout inside NgZone… a bit dirty but it works! 🙂

Modify you app.component.html to test a protected route:

<h3><a routerLink="/">Home</a> | <a routerLink="/protected">Protected</a></h3>
<h1>
{{title}}
</h1>
<router-outlet></router-outlet>

Check your app.module.ts

Your app.module.ts should be well setup like this:

import { AuthService } from './services/auth.service';
import { AuthGuardService } from './services/auth-guard.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { ProtectedComponent } from './protected/protected.component';
import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
import { Adal5Service, Adal5HTTPService } from 'adal-angular5';
import { HttpClient } from '@angular/common/http';
const routes: Routes = [
{
   path:'',
   children: []
},
{
   path:'protected',
   component:ProtectedComponent,
   canActivate: [AuthGuardService]
},
{
   path:'auth-callback',
   component:AuthCallbackComponent
}
];

@NgModule({
   declarations: [
      AppComponent,
      ProtectedComponent,
      AuthCallbackComponent
   ],
   imports: [
      BrowserModule,
      RouterModule.forRoot(routes)
   ],
   exports: [RouterModule],
   providers: [AuthGuardService, AuthService, Adal5Service,{ provide:Adal5HTTPService, useFactory:Adal5HTTPService.factory, deps: [HttpClient, Adal5Service] } ],
   bootstrap: [AppComponent]
})

export class AppModule { }

Demo time !!!

If everything is well setup in Azure and in your Angular 5 application, if you try to go to navigate to a protected route you should have a logging page to Microsoft and be redirected to your web application:

 

 

 

 

 

If log in your browser console the authenticated user object you should have something like this :

Awesome right ? 😉

Now let’s see how we protect a Web Api: Using OpenIdConnect with Azure AD, Angular5 and WebAPI Core: WebAPI configuration