3
votes

I have a web application with a asp.net Web Api back-end a angular front-end. The application uses a form based authentication that check the identity of the user against its database.

I have been tasked to add Saml authentication to the application with Azure AD as my Identity Provider.

I already tried using SustainSys: Add SAML Authentication to .net WebAPI

The problem what I have with this approach is that the Angular SPA is not triggered by the final redirect of the Saml Authentication thus not performing the log-in operations.

Another option that I'm evaluating is Adal: https://www.npmjs.com/package/microsoft-adal-angular6

In this case I don't know what should I implement in the Web API to perform the authentication.

What's the correct way to add this type of authentication ?

1

1 Answers

1
votes

Here is something you can do to make your angular SPA and asp.net core web api secure.

Technical Solution

1) Implement the Web application to web API authentication scenario.

2) Use https://portal.azure.com to configure authentication process.

3) Use adal-angular4 (version 3.0.1+) angular package to implement authentication into the angular application.

4) Use Microsoft.AspNetCore.Authentication.AzureAD.UI NuGet package to implement authentication into the Asp.Net core Web API service.

Prerequisite

Register Angular application

Register the application within Azure Active Directory from https://portal.azure.com Go to “Azure Active Directory” -> “App registrations” and select to add a “New application registration”.

enter image description here

Register Web API

Register the Web API service within Azure Active Directory from https://portal.azure.com. Registration process is similar to registering the Angular application. (see above)

Configure access to the Web API resource

A client application gains access to a resource server by declaring permission requests. Go to Azure Active Directory -> App registrations and select the Settings of the Angular application.

Select the Web API that is accessed by the Angular application:

enter image description here

Enable access with delegated permission

enter image description here

The required resource (Web API) access is added in the registered Angular application manifest:

enter image description here

Grant Access to Web API resource

enter image description here

Install adal package

nstall adal package adal-angular4 (version 3.0.1+). The package adal-angular4 was updated to be compatible with angular6. Spotting the right package is confusing because the package name refers to angular 4 and also because there is another package adal-angular5 that is not compatible with angular 6.

Technical solution

Authentication process

First Step:

The first time when a user accesses the application through the browser the angular application detects that it is not authenticated and redirects to the Microsoft login for credentials introduction.

enter image description here

Step 2:

After the login step the authentication service redirects the process to the configured angular application callback where the authentication process is finalized.

Step 3:

The angular application calls the required Web API. The authentication token is added in the HTTP headers before calling the Web API.

Step 4:

The Web API validates the authentication token and, in case of success, it returns the requested resource.

Implementation

Step 1:

a) Configure the ADAL service

import { Component } from '@angular/core';
import { AdalService } from 'adal-angular4';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  private adalConfig = {
    tenant: '[TENANT_GUID]',
    clientId: '[CLIENTID_GUID]',
    redirectUri: "[LOGIN_REDIRECT_URL]",
    postLogoutRedirectUri: "[POST_LOGOUT_REDIRECT_URL]",
    endpoints: {
      "[HOME_URL_WEB_API]": "[HOME_WEB_API_GUID]"
    }
  }

  constructor(private adal: AdalService) {
    this.adal.init(this.adalConfig);
  }

  signOut(): void {
    this.adal.logOut();
  }
}

[TENANT_GUID] is the Azure AD Directory ID.

enter image description here

b) Implement a guard that redirects to login in case of an unauthenticated request

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { AdalService } from 'adal-angular4';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(private adal: AdalService) { }

  canActivate(): boolean {

    if (this.adal.userInfo.authenticated) {
      return true;
    }

    this.adal.login();

    return false;
  }

}

c) Guard the application paths in the router class:

const routes: Routes = [
  { path: '', component: MyComponent, canActivate: [AuthGuard] },
  { path: 'auth-callback', component: AuthCallbackComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Step 2:- Finalize the login process.

Implement a callback component that will be called as part of the login process. The registration of the callback URL is done in Step 1 when configuring adal with redirectUri information.

import { Component, OnInit, NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { AdalService } from 'adal-angular4';

@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 adal: AdalService, private _zone: NgZone) { }

  ngOnInit() {
    this.adal.handleWindowCallback();

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

}

handleWindowCallback() add the authentication token to the session.

Step 3:

Set authentication token for the HTTP request that is sent to the web API Adal makes it an easy step. The only step to be done is to register the out of the box AdalInterceptor in the app.module.ts file:

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './/app-routing.module';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
import { AdalService, AdalInterceptor } from 'adal-angular4';

@NgModule({
  declarations: [
    AppComponent,
    AuthCallbackComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule,
    FormsModule
  ],
  providers: [AdalService, { provide: HTTP_INTERCEPTORS, useClass: AdalInterceptor, multi: true }],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 4:

Validate authentication token in the web API service Configure authentication service as AzureADBear in the web API Startup.cs file:

public void ConfigureServices(IServiceCollection services)
{

    services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
    .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddCors(options = 
    {
        options.AddPolicy("AllowAllOrigins",
         builder =
         {
             builder.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin();
         });
    });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseCors("AllowAllOrigins");
    app.UseAuthentication();

    app.UseStaticFiles();
    app.UseMvc(routes =
    {
        routes.MapRoute(name: "default", template: "api/{controller}/{id}");
    });
}

Please Make sure that you have installed the Microsoft.AspNetCore.Authentication.AzureAD.UI NuGet package mentioned in the prerequisites section. Add the AzureAD section in the appsettings.json in order to provide authentication details for the validation process:

"AzureAd": {
  "Instance": "https://login.microsoftonline.com",
  "Domain": "[AD_DOMAIN]",
  "TenantId": "[TENANTID_GUID]",
  "ClientId": "[CLIENTID_GUID]"
}

Replace the above placeholders with the following information:

[AD_DOMAIN] is the Azure AD domain.

[TENANT_GUID] is the Azure AD Directory ID. (see above)

[CLIENTID_GUID] is the Application ID of the Web API.

Finally decorate your controller with filter attribute like below:

[Authorize]
[ApiController]
public class MyController : ControllerBase

Hope it helps.