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”.
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:
Enable access with delegated permission
The required resource (Web API) access is added in the registered Angular application manifest:
Grant Access to Web API resource
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.
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.
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.