In this article, I want to show you an example of how the authentication can be implemented using the
ASP.NET Web API as a back-end and Angular 2 as the front-end technology.
For this purpose, I’m going to use an already implemented application and show you just the most important pieces of this.
First, let’s take a look at the front-end. In app.module, we will register a new provider who is going to be responsible for providing information about authentication (checking whether the user is logged in or not) and also for performing the authentication. This is an example of the code.
Latest Update In
2017:
MBuild Alpha Release .Net Core Tools in Visual Studio 2017- The new release of .Net Core Tools based on MSBuild or .Net Core can be tried in Mac Visual Studio or Visual Studio version 2017 RC or at commandline. These tools can run in both .Net Core 1.1 and 1.0 versions.
- providers: [
- AuthGuard,
- AuthenticationService,
- IdentityService,
- {
- provide: AuthHttp,
- useFactory: getAuthHttp,
- deps: [Http]
- },
First let's take a look at the IdentityService, this is the code for it:
- import { Injectable } from '@angular/core';
- import { Http, Headers, RequestOptions, Response } from '@angular/http';
- import { Observable } from 'rxjs/Observable';
- import 'rxjs/add/operator/map';
- import 'rxjs/add/operator/catch';
- import 'rxjs/add/observable/throw';
- import { AuthHttp } from 'angular2-jwt';
-
-
-
-
- @Injectable()
- export class IdentityService {
- headers: Headers;
- options: RequestOptions;
- constructor(private authHttp: AuthHttp, private http: Http) {
-
- this.headers = new Headers({ 'Content-Type': 'application/json' });
- this.options = new RequestOptions({ headers: this.headers });
- }
-
-
-
- public GetAll(): Observable < any > {
-
- return this.authHttp.get("/api/identity/GetAll")
- .map((res: Response) => {
- return res.json();
- })
- .catch((error: any) => {
-
- return Observable.throw(error);
- });
- }
-
-
-
-
-
-
- public Create(model: any): Observable < any > {
- let body: string = JSON.stringify(model);
- return this.http.post("/api/identity/Create", body, this.options)
- .map((res: Response) => {
- return res.json();
- })
- .catch((error: any) => {
-
- return Observable.throw(error);
- });
- }
-
-
-
-
-
-
-
- public Delete(username: string): Observable < any > {
- let body: string = JSON.stringify(username);
-
- return this.authHttp.post("/api/identity/Delete", body, this.options)
- .map((res: Response) => {
- return res.json();
- })
- .catch((error: any) => {
-
- return Observable.throw(error);
- });
- }
-
- }
Here, we have functions that allow us to Create, Delete, and Get All users of the current application. Please note that for this purpose, we are using the http get/post methods pointing to the URI (Unique Resource Identifier) of API.
Now, let's take a look at the Authentication Service.
- import { Injectable } from '@angular/core';
- import { Http, Headers, RequestOptions, Response } from '@angular/http';
- import { Observable } from 'rxjs/Observable';
- import 'rxjs/add/operator/map';
- import 'rxjs/add/operator/catch';
- import 'rxjs/add/observable/throw';
-
- import { JwtHelper, tokenNotExpired } from 'angular2-jwt';
-
- import { Config } from '../config';
-
-
-
-
- @Injectable() export class AuthenticationService {
-
-
-
-
- public redirectUrl: string;
-
-
-
-
- private user: any = {};
-
- private headers: Headers;
- private options: RequestOptions;
-
- constructor(private http: Http) {
-
-
- this.decodeToken();
-
-
- this.headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
- this.options = new RequestOptions({ headers: this.headers });
-
- }
-
-
-
-
-
-
-
-
- public signin(username: string, password: string): Observable<any> {
-
-
- let tokenEndpoint: string = Config.TOKEN_ENDPOINT;
-
- let params: any = {
- client_id: Config.CLIENT_ID,
- grant_type: Config.GRANT_TYPE,
- username: username,
- password: password,
- scope: Config.SCOPE
- };
-
-
- let body: string = this.encodeParams(params);
-
- return this.http.post(tokenEndpoint, body, this.options)
- .map((res: Response) => {
-
- let body: any = res.json();
-
-
- if (typeof body.access_token !== 'undefined') {
-
-
- this.store(body);
-
- }
-
- }).catch((error: any) => {
-
-
- return Observable.throw(error);
-
- });
-
- }
-
-
-
-
- public getNewToken(): void {
-
- let refreshToken: string = localStorage.getItem('refresh_token');
-
- if (refreshToken != null) {
-
-
- let tokenEndpoint: string = Config.TOKEN_ENDPOINT;
-
- let params: any = {
- client_id: Config.CLIENT_ID,
- grant_type: "refresh_token",
- refresh_token: refreshToken
- };
-
-
- let body: string = this.encodeParams(params);
-
- this.http.post(tokenEndpoint, body, this.options)
- .subscribe(
- (res: Response) => {
-
- let body: any = res.json();
-
-
- if (typeof body.access_token !== 'undefined') {
-
-
- this.store(body);
-
- }
-
- });
-
- }
-
- }
-
-
-
-
- public revokeToken(): void {
-
- let token: string = localStorage.getItem('id_token');
-
- if (token != null) {
-
-
- let revocationEndpoint: string = Config.REVOCATION_ENDPOINT;
-
- let params: any = {
- client_id: Config.CLIENT_ID,
- token_type_hint: "access_token",
- token: token
- };
-
-
- let body: string = this.encodeParams(params);
-
- this.http.post(revocationEndpoint, body, this.options)
- .subscribe(
- () => {
-
- localStorage.removeItem('id_token');
-
- });
-
- }
-
- }
-
-
-
-
- public revokeRefreshToken(): void {
-
- let refreshToken: string = localStorage.getItem('refresh_token');
-
- if (refreshToken != null) {
-
-
- let revocationEndpoint: string = Config.REVOCATION_ENDPOINT;
-
- let params: any = {
- client_id: Config.CLIENT_ID,
- token_type_hint: "refresh_token",
- token: refreshToken
- };
-
-
- let body: string = this.encodeParams(params);
-
- this.http.post(revocationEndpoint, body, this.options)
- .subscribe(
- () => {
-
- localStorage.removeItem('refresh_token');
-
- });
-
- }
-
- }
-
-
-
-
- public signout(): void {
-
- this.redirectUrl = null;
-
- this.user = {};
-
-
- this.revokeToken();
-
-
- this.revokeRefreshToken();
-
- }
-
-
-
-
-
-
- public getUser(): any {
-
- return this.user;
-
- }
-
-
-
-
- private decodeToken(): void {
-
- if (tokenNotExpired()) {
-
- let token: string = localStorage.getItem('id_token');
-
- let jwtHelper: JwtHelper = new JwtHelper();
- this.user = jwtHelper.decodeToken(token);
-
- }
-
- }
-
-
-
-
-
-
-
- private encodeParams(params: any): string {
-
- let body: string = "";
- for (let key in params) {
- if (body.length) {
- body += "&";
- }
- body += key + "=";
- body += encodeURIComponent(params[key]);
- }
-
- return body;
- }
-
-
-
-
-
-
- private store(body: any): void {
-
-
- localStorage.setItem('id_token', body.access_token);
-
- localStorage.setItem('refresh_token', body.refresh_token);
-
-
- this.decodeToken();
-
- }
-
- }
Here, we can note the usage of JWT (JavaScript Web Token); this service is responsible for performing the login/logout functionalities, and also for managing the life cycle of the logged-in user object.
The method getUser() retrieves a user which is currently logged in, in order to be able to access the information of that user.
Now, let’s take a look at the AuthGuard.
- import { Injectable } from '@angular/core';
- import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
-
- import { tokenNotExpired } from 'angular2-jwt';
-
- import { AuthenticationService } from './authentication.service';
-
-
-
-
- @Injectable() export class AuthGuard implements CanActivate {
-
- constructor(public authenticationService: AuthenticationService, private router: Router) { }
-
- public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
-
- if (tokenNotExpired()) {
-
- return true;
- }
-
- let url: string = state.url;
- this.authenticationService.redirectUrl = url;
-
- this.router.navigate(['/signin']);
- return false;
- }
-
- }
This guard performs validation, to see whether the user is logged in or not. Guards are normally used and associated with routes of the Angular2 application, to check whether the user can access the route or not, and in our case, the condition to pass the guard is that the user should be logged in.
Also, this guard redirects a user to the login component if the user is currently not logged in.
Here is an example of how we can apply the guard to some specific routes.
- Routes = [
- { path: '', redirectTo: 'home', pathMatch: 'full' },
- { path: 'home', component: HomeComponent },
- { path: 'resources', component: ResourcesComponent, canActivate: [AuthGuard] },
- { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
- { path: 'signin', component: SigninComponent },
- { path: 'signup', component: SignupComponent }
- ];
- # So the main idea here is to separate public routes from ones that require the user to be logged in.
-
- Now let’s take a look at the asp.net web api example:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Authorization;
- using ******.DataCore.Models;
- using Microsoft.AspNetCore.Identity;
- using ******.ApiCore.Services;
- using ******.DataCore.Access;
- using Microsoft.Extensions.Logging;
- using System.Security.Claims;
- using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
- using IdentityModel;
- using ******.ApiCore.Models.AccountViewModels;
- using IdentityServer4.Extensions;
- using Microsoft.EntityFrameworkCore;
-
- namespace ******.ApiCore.Controllers
- {
-
-
-
- [Route("api/[controller]")]
-
- public class IdentityController : ControllerBase
- {
- private readonly UserManager<ApplicationUser> _userManager;
- private readonly SignInManager<ApplicationUser> _signInManager;
- private readonly IEmailSender _emailSender;
- private readonly ISmsSender _smsSender;
- private readonly ILogger _logger;
- private readonly ApplicationDbContext _context;
-
- public IdentityController(
- UserManager<ApplicationUser> userManager,
- SignInManager<ApplicationUser> signInManager,
- IEmailSender emailSender,
- ISmsSender smsSender,
- ILoggerFactory loggerFactory,
- ApplicationDbContext context)
- {
- _userManager = userManager;
- _signInManager = signInManager;
- _emailSender = emailSender;
- _smsSender = smsSender;
- _logger = loggerFactory.CreateLogger<IdentityController>();
- _context = context;
- }
-
- [HttpGet]
- [AllowAnonymous]
- public void Get()
- {
- var sports = _context.Sports.Include(x => x.Associations);
-
- var sport = _context.Associations;
- }
-
-
-
-
-
-
- [HttpGet("GetAll")]
- public async Task<IActionResult> GetAll()
- {
- var claim = new Claim("role", "user");
- var users = await _userManager.GetUsersForClaimAsync(claim);
-
- return new JsonResult(users);
- }
-
-
-
-
-
-
- [HttpPost]
- [AllowAnonymous]
- public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
- {
- var user = new ApplicationUser
- {
- UserName = model.Username,
- Email = model.Email
- };
-
- var claims = new IdentityUserClaim<string>[]
- {
- new IdentityUserClaim<string> { ClaimType = JwtClaimTypes.PreferredUserName, ClaimValue = model.Username },
- new IdentityUserClaim<string> { ClaimType = JwtClaimTypes.Email, ClaimValue = model.Email },
- new IdentityUserClaim<string> { ClaimType = JwtClaimTypes.Role, ClaimValue = "user" }
- };
-
- foreach (var claim in claims)
- {
- user.Claims.Add(claim);
- }
-
- var result = await _userManager.CreateAsync(user, model.Password);
-
-
-
- return new JsonResult(result);
- }
-
-
-
-
-
-
- [HttpPost("delete")]
- public async Task<IActionResult> Delete([FromBody]string username)
- {
- var user = await _userManager.FindByNameAsync(username);
-
- var result = await _userManager.DeleteAsync(user);
-
- return new JsonResult(result);
- }
-
-
-
- }
- }
Here, you have the mapping of paths and methods from identityService. I’m using the standard authentication from .NET, based on .NET identity.
The very same could be done for the sign in and sign out. We just need to generate and store JWT in our front-end Angular 2 application, and then use the stored token to make sure if the user is logged in or not.
You must try this guide. If you face any issue while implementing authentication with Angular 2 apps and ASP.NET Web API, ask me in the comments. Your feedback is valuable, so share your thoughts regarding this post.