Secured dotnet core spa ( angular2) with token ( consuming identity server 4 token)

Note: this article is under construction, it will be updated by the time.

Introduction:

Single page applications (spa) websites is widely used now a days, the new Microsoft dotnet core provides many per-configured mvc and JavaScript frameworks templates (such as Angular2, React .. etc ), one of the most famous JavaScript frameworks is Angular2.

Single page applications (spa), the server application (.net app) has one page which responsibility to load the javascript (angular2) files to the user browser and the browser then interact with the API which handle the server side work such as database queries, files, emails ..etc.

SPA authentication scenario:

  1. when the user hit the spa website , he will be redirected to the authentication server ( such as idnetity server4 in our example).
  2. the authentication server will prompt the user to prove his identity usign username and password or other ways such as facebook or google.
  3. when the user is logged-in successfully , he will be redirected back to the SPA application and provide it with the necessary security means including Cookies and access_tokens.
  4. the SPA website will use the Cookies to authenticate the user before loading the JavaScript files ( by c#). after loading the javascript files , angular will use the ACCESS_TOKEN with every request to the API.

The problem:

  • The provided SPA by Microsoft has no built-in authentication configuration.
  • the spa mvc need to provide angular with access_token to be used when requesting the API.

in this article , i assume your architecture as follow:

  • identity server 4 is installed and configured correctly ( as explained the there documentation )
  • API Server project that secured ( as explained here)
  • website project (dotent core mvc Angular2 spa template) that use the API project for data.

what we will do in this tutorial?

in c# we will create a new api method in the SPA mvc project that return the access_token of the current logged in user.

in angular , we will create a new function that runs at the application startup that request the previous api and retrieve the access_token and store it in the sessionStorage to bes used.

change the Http request to sent tha Authorization header with each request ( using angular2-jwt plugin) .

Dotnet core MVC Side :

step 1 : securing Website ( angular )

in the startup.cs add app.UseCookieAuthentication , app.UseOpenIdConnectAuthentication as follow :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Website
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
......
}

public IConfigurationRoot Configuration { get; }

....


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{

.....


app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});


app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",

Authority = "http://localhost:5002",
RequireHttpsMetadata = false,

ClientId = "mvc",
ClientSecret = "secret",

ResponseType = "code id_token",
Scope = { "api1", "offline_access" },

GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true
});




app.UseMvc(routes =>
{
...
});
}
}
}

step 2: create rest Api to retrieve the access_token.

in the SPA mvc website , in HomeController.cs , create a new method that return current user token.

[Authorize]
public async Task<IActionResult> GetToken()
{

var accesssToken = await HttpContext.Authentication.GetAuthenticateInfoAsync("oidc");
var tokenData = accesssToken.Properties.Items;



var result = new
{
token = tokenData[".Token.access_token"]
};
return Json(result) ;
}

Angular Side:

step 1 : add configure jwt plugin to angular

we will use angular2-jwt package to send token with each http request.

npm install angular2-jwt

in the ClientApp folder create a new file and name it app/auth.module.ts , and past this code inside it

import { NgModule } from '@angular/core';
import { Http, RequestOptions } from '@angular/http';
import { AuthHttp, AuthConfig } from 'angular2-jwt';

export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
tokenName: 'token',
tokenGetter: (() => sessionStorage.getItem('token')),
globalHeaders: [{'Content-Type':'application/json'}],
}), http, options);
}

@NgModule({
providers: [
{
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [Http, RequestOptions]
}
]
})
export class AuthModule {}

in the app.share.module.ts , import the AuthModule crated before:

/**add this line to import the module**/
import{ AuthModule } from "./auth.module"
/* add AuthModule to the imports array */
imports: [ 
....
AuthModule,
....
]

step 2: get the token from the MCV Controller and store it to the session storage

in app.component.ts ( startup component); we need to call “home/gettoken” api to get the token and store it in angular .

import { Component,OnInit } from '@angular/core';
import { Http } from '@angular/http';
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
    constructor(private http: Http) { }
ngOnInit(){
/* this code will get the token as Json and tore it in sessionStorage */
          this.http.get("/home/gettoken")
          .map((res) => res.json()).subscribe((result)=>{
           sessionStorage.setItem('token',result.token);
        });
}
}

step 3 : fetching a secure data from api ( with token attached to the header) :

in your component , you can use AuthHttp Class instead of the original Http Class as follw :

import { Component , OnInit} from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
@Component({
     selector: 'home',
      templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
   constructor(public authHttp: AuthHttp) {}
   ngOnInit() {
this.authHttp.get('http://localhost:5000/api/data')
.subscribe(result => {
console.log("done",result);
         });
     }
}