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

 

Installing required packages

There is only one required package to achieve our Web Api protection with a JWT.

Install https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer/

PM> Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 2.0.1

Configure your Web API in Startup.cs:

using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebApiJwtBearer
{
   public class Startup
   {
      public Startup(IConfiguration configuration)
      {
         Configuration = configuration;
      }

      public IConfiguration Configuration { get; }

     //This method gets called by the runtime. Use this method to add services to the container.
     public void ConfigureServices(IServiceCollection services)
     {
        services.AddAuthentication(options =>
        {
           options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
           options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
           options.Authority = "https://login.microsoftonline.com/136544d9-038e-4646-afff-10accb370679"; <- tenantId
           options.Audience = "257b6c36-1168-4aac-be93-6f2cd81cec43"; <- clientId
           options.TokenValidationParameters.ValidateLifetime = true;
           options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;
       });

       services.AddAuthorization();

       services.AddMvc();
    }

    //This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
       if (env.IsDevelopment())
       {
          app.UseDeveloperExceptionPage();
       }

      app.UseAuthentication();

      app.UseCors(builder => builder
      .AllowAnyOrigin()
      .AllowAnyMethod()
      .AllowCredentials()
      .AllowAnyHeader());
      app.UseMvc();
   }
 }
}

Now you should be done 🙂

Let’s see what happen if we test it :

 

Nice isn’t it? 🙂

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

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

Introduction

 

 

We’ll see how to setup Azure for being consumed by a SPA, how to setup and include in claims, roles and groups and see where are defined clientId and the tenantId required by ADAL for the SPA.

Create an application

First step is to create an application in the Azure portal, to do this go to “Azure Active directory” then “App registrations”:

Then click on “New application registration”:

Add a name, application type and the login pahe URL:

Then the application will apear in the list:

The Application Id is the clientId required for your SPA, remember where to find it 😉

Finally, find your tenantId, it could be your tenantName like this: “yourtenantname@onmicrosoft.com” but it doesn’t work for all, because if your account email is not a Microsoft one, such as mine “anthony.giretti@gmail.com” it will be formatted as “anthonygirettigmail.onmicrosoft.com” won’t work at all, so I suggest you to use your tenantGuid  that you can find there:

“Azure Active Directory -> Enterprise applications -> All Applications -> Properties”

The Object ID is your tenantId required for ADAL

Enabling users request access to the application

To do this go to: “Azure Active Directory -> Enterprise applications -> All Applications -> Self Service”

Then “Allow” users by selecting “Yes” and select a group for users asignations:

 

Enabling Implicit flow

To use it in a SPA you have to enable Implicit flow mode, here:

To do this, click on your application in the list, then click on “Manifest”:

Find oauthAllowImplicitFlow property and set its value to true:

Assign a group to an user and add it in claims

Let’s go there:

“Azure Active Directory -> Users -> All Users -> Select users in the list to edit them -> Groups”

Then go back to the “Application Manifest” and activate roles in claims by setting the property groupMembershipClaims to All:

 

Assign a role to an user and add it in claims

There is no UI to create a role, so you have to create them within the “Application Manifest”, like this:

You have to set isEnabled to true, displayName, id, description, value are up to you.

Now you have to assign it to users, and there is a UI for this here:

“Azure Active Directory -> Enterprise applications -> Users and groups”

Select a user to edit then assign to him a role:

Set your SPA post login page (callback page)

There is a UI to do this, but you can go to “Manifest” to add callback pages like this:

You can set several URI, for example one callback URI for each environment of your SPA:

 

We just configured Azure AD 🙂

Let’s go to setup our Angular 5 app: Using OpenIdConnect with Azure AD, Angular5 and WebAPI Core: Angular5 configuration