.NET Core API - Authentication with API


Download ASP.NET Core - Authentication with Example API

In this tutorial we will learn how to create ASP.NET Core Web API authentication using JWT Token. Here, we will take a example first register after that validate login if login user is valid then create a token and refresh token. To check token based authentication we will use the following tools and softwares:

  1. SQL Server - To create tables to save and fetch user credentials and JWT Token.
  2. Create an ASP.NET Core Web API in Visual Studio 2019 - To create API for Authentication using JWT Token.
  3. Postman Tool -  To test ASP.NET API authentication.

So now we will start with the SQL Server firstly.

1. SQL Server

In SQL Server database we create a database named as EmployeeDB. Inside created database we create two tables which have names Accounts and RefreshToken. The following Script you can run directly through SQL Query window. It will create both tables.

/* To create database */
Create Database EmployeeDB

USE [EmployeeDB]
GO

/****** Object:  Table [dbo].[RefreshToken]    Script Date: 27-12-2022 22:02:51 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[RefreshToken](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[AccountId] [int] NOT NULL,
	[Token] [nvarchar](max) NULL,
	[Expires] [datetime2](7) NOT NULL,
	[Created] [datetime2](7) NOT NULL,
	[CreatedByIp] [nvarchar](max) NULL,
	[Revoked] [datetime2](7) NULL,
	[RevokedByIp] [nvarchar](max) NULL,
	[ReplacedByToken] [nvarchar](max) NULL,
CONSTRAINT [PK_RefreshToken] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[RefreshToken]  WITH CHECK ADD  CONSTRAINT [FK_RefreshToken_Accounts_AccountId] FOREIGN KEY([AccountId])
REFERENCES [dbo].[Accounts] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[RefreshToken] CHECK CONSTRAINT [FK_RefreshToken_Accounts_AccountId]
GO

CREATE TABLE [dbo].[Accounts](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Title] [nvarchar](max) NULL,
	[FirstName] [nvarchar](max) NULL,
	[LastName] [nvarchar](max) NULL,
	[Email] [nvarchar](max) NULL,
	[PasswordHash] [nvarchar](max) NULL,
	[AcceptTerms] [bit] NOT NULL,
	[Role] [int] NOT NULL,
	[VerificationToken] [nvarchar](max) NULL,
	[Verified] [datetime2](7) NULL,
	[ResetToken] [nvarchar](max) NULL,
	[ResetTokenExpires] [datetime2](7) NULL,
	[PasswordReset] [datetime2](7) NULL,
	[Created] [datetime2](7) NOT NULL,
	[Updated] [datetime2](7) NULL,
 CONSTRAINT [PK_Accounts] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

The following image show the created tables inside EmployeeDB.

ASP.NET Core Authentication Tutorial with Example API

The above table Account have the user information to validate. Table refreshtoken table has the information regarding token. We can also see how we can create these table automatically using API Migrations below tutorials.

2. Creating an ASP.NET Core Web API in Visual Studio 2019

We will create Web API using Visual Studio 2019. You can download the free community version from the Microsoft official site. https://visualstudio.microsoft.com/

Follow the below steps to create your Web API project, Open Visual Studio 2019 and create a new project.

ASP.NET Core Authentication Tutorial with Example API

Select Web Application template and click Next button.

ASP.NET Core Authentication Tutorial with Example API

Provide Project name and location.

ASP.NET Core Authentication Tutorial with Example API

Now click on the next button and select Target Framework and click on Create button.

ASP.NET Core Authentication Tutorial with Example API

Now we have added the following folders inside the API project. It looks like the below:

ASP.NET Core Authentication Tutorial with Example API

  1. Controller
    • AccountsController.cs
    • BaseController.cs
  2. Entity Folder
    • Account.cs
    • RefreshToken.cs
    • Role.cs
  3. Middleware Folder
    • ErrorHandlerMiddleware.cs
    • JwtMiddleware.cs
  4. Migrations Folder
  5. Models
    • AuthenticateRequest.cs
    • AuthenticateResponse.cs
  6. Service
    • AccountService.cs
  7. AppSettings.json File
  8. Program.cs File
  9. Startup.cs File

1. Controller Folder

It contains two Controllers as name below:

  1. AccountsController.cs
  2. BaseController.cs

AccountsController.cs

The .NET accounts controller defines and handles all routes for the API that relate to accounts, So we will add the following code with it. Each method of the controller calls the Account service to perform the action required.

using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using WebApi.Entities;
using WebApi.Models.Accounts;
using WebApi.Services;

namespace WebApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class AccountsController : BaseController
    {
        private readonly IAccountService _accountService;
        private readonly IMapper _mapper;

        public AccountsController(
            IAccountService accountService,
            IMapper mapper)
        {
            _accountService = accountService;
            _mapper = mapper;
        }

        [HttpPost("register")]
        public IActionResult Register(RegisterRequest model)
        {
            _accountService.Register(model, Request.Headers["origin"]);
            return Ok(new { message = "Registration successful, please check your email for verification instructions" });
        }

        [HttpPost("authenticate")]
        public ActionResult Authenticate(AuthenticateRequest model)
        {
            var response = _accountService.Authenticate(model, ipAddress());
            setTokenCookie(response.RefreshToken);
            return Ok(response);
        }

        [HttpPost("refresh-token")]
        public ActionResult RefreshToken()
        {
            var refreshToken = Request.Cookies["refreshToken"];
            var response = _accountService.RefreshToken(refreshToken, ipAddress());
            setTokenCookie(response.RefreshToken);
            return Ok(response);
        }

        [Authorize]
        [HttpPost("revoke-token")]
        public IActionResult RevokeToken(RevokeTokenRequest model)
        {
            // accept token from request body or cookie
            var token = model.Token ?? Request.Cookies["refreshToken"];

            if (string.IsNullOrEmpty(token))
                return BadRequest(new { message = "Token is required" });

            // users can revoke their own tokens and admins can revoke any tokens
            if (!Account.OwnsToken(token) && Account.Role != Role.Admin)
                return Unauthorized(new { message = "Unauthorized" });

            _accountService.RevokeToken(token, ipAddress());
            return Ok(new { message = "Token revoked" });
        }

        [Authorize]
        [HttpGet]
        public ActionResult> GetAll()
        {
            var accounts = _accountService.GetAll();
            return Ok(accounts);
        }

        [Authorize]
        [HttpGet("{id:int}")]
        public ActionResult GetById(int id)
        {
            // users can get their own account and admins can get any account
            if (id != Account.Id && Account.Role != Role.Admin)
                return Unauthorized(new { message = "Unauthorized" });

            var account = _accountService.GetById(id);
            return Ok(account);
        }

        // helper methods

        private void setTokenCookie(string token)
        {
            var cookieOptions = new CookieOptions
            {
                HttpOnly = true,
                Expires = DateTime.UtcNow.AddDays(7)
            };
            Response.Cookies.Append("refreshToken", token, cookieOptions);
        }

        private string ipAddress()
        {
            if (Request.Headers.ContainsKey("X-Forwarded-For"))
                return Request.Headers["X-Forwarded-For"];
            else
                return HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
        }
    }
}

BaseController.cs

This controller returns the current authenticated account if user not logged it will return NULL.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WebApi.Entities;

namespace WebApi.Controllers
{
    [Controller]
    public abstract class BaseController : ControllerBase
    {
        public Account Account => (Account)HttpContext.Items["Account"];
    }
}

2. Entity Folder

This folder contain entity for account, refreshtoken and role. It contain 3 files.

  1. Account.cs
  2. RefreshToken.cs
  3. Role.cs

Account.cs

The account entity use to get and set value of Account.

using System;
using System.Collections.Generic;

namespace WebApi.Entities
{
    public class Account
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string PasswordHash { get; set; }
        public bool AcceptTerms { get; set; }
        public Role Role { get; set; }
        public string VerificationToken { get; set; }
        public DateTime? Verified { get; set; }
        public bool IsVerified => Verified.HasValue || PasswordReset.HasValue;
        public string ResetToken { get; set; }
        public DateTime? ResetTokenExpires { get; set; }
        public DateTime? PasswordReset { get; set; }
        public DateTime Created { get; set; }
        public DateTime? Updated { get; set; }
        public List RefreshTokens { get; set; }

        public bool OwnsToken(string token) 
        {
            return this.RefreshTokens?.Find(x => x.Token == token) != null;
        }
    }
}

RefreshToken.cs

It will use to get and set value of refresh token.

using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;

namespace WebApi.Entities
{
    [Owned]
    public class RefreshToken
    {
        [Key]
        public int Id { get; set; }
        public Account Account { get; set; }
        public string Token { get; set; }
        public DateTime Expires { get; set; }
        public bool IsExpired => DateTime.UtcNow >= Expires;
        public DateTime Created { get; set; }
        public string CreatedByIp { get; set; }
        public DateTime? Revoked { get; set; }
        public string RevokedByIp { get; set; }
        public string ReplacedByToken { get; set; }
        public bool IsActive => Revoked == null && !IsExpired;
    }
}

Role.cs

The role enum defines all the available roles in the this api. But we are not using here role based api but we have the option to use in future.

namespace WebApi.Entities
{
    public enum Role
    {
        Admin,
        User
    }
}

3. Middleware Folder

Middleware in ASP.NET Core controls how our application responds to HTTP requests. It can also control how our application looks when there is an error, and it is a key piece in how we authenticate and authorize a user to perform specific actions. It contain two .cs file to controll error and authentication.

  1. ErrorHandlerMiddleware.cs
  2. JwtMiddleware.cs

ErrorHandlerMiddleware.cs

The global error handler is used catch all errors and error handling code throughout the .NET API.

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi.Helpers;

namespace WebApi.Middleware
{
    public class ErrorHandlerMiddleware
    {
        private readonly RequestDelegate _next;

        public ErrorHandlerMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception error)
            {
                var response = context.Response;
                response.ContentType = "application/json";

                switch(error)
                {
                    case AppException e:
                        // custom application error
                        response.StatusCode = (int)HttpStatusCode.BadRequest;
                        break;
                    case KeyNotFoundException e:
                        // not found error
                        response.StatusCode = (int)HttpStatusCode.NotFound;
                        break;
                    default:
                        // unhandled error
                        response.StatusCode = (int)HttpStatusCode.InternalServerError;
                        break;
                }

                var result = JsonSerializer.Serialize(new { message = error?.Message });
                await response.WriteAsync(result);
            }
        }
    }
}

JwtMiddleware.cs

The JWT middleware fetch the JWT token from the request Authorization header and validates it with the ValidateToken() method. If validation is successful the user id from the token is returned, and the authenticated user object is attached to the HttpContext.Items collection to make it accessible within the scope of the current request.

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApi.Helpers;

namespace WebApi.Middleware
{
    public class JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly AppSettings _appSettings;

        public JwtMiddleware(RequestDelegate next, IOptions appSettings)
        {
            _next = next;
            _appSettings = appSettings.Value;
        }

        public async Task Invoke(HttpContext context, DataContext dataContext)
        {
            var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

            if (token != null)
               await attachAccountToContext(context, dataContext, token);
            await _next(context);
        }

        private async Task attachAccountToContext(HttpContext context, DataContext dataContext, string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                    ClockSkew = TimeSpan.Zero
                }, out SecurityToken validatedToken);

                var jwtToken = (JwtSecurityToken)validatedToken;
                var accountId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);

             
                context.Items["Account"] = await dataContext.Accounts.FindAsync(accountId);
            }
            catch 
            {
               
            }
        }
    }
}

4. Migrations Folder

It provides a way to update the database schema. We have created above SQL Tables manually you can also create through Migrations. In Visual Studio,

Open the Package Manager Console from the menu Tools -> NuGet Package Manager -> Package Manager Console

ASP.NET Core Authentication Tutorial with Example API

PM>add-migration  MigrationProject

Creating or Updating the Database

Use the following command to create or update the database schema.

PM> Update-Database

When you run the above commant you will see migration folder two files automatically generated.

ASP.NET Core Authentication Tutorial with Example API

Now if you check database tables created automatically. So there is no need to add manually.

5. Models

Inside the model we have created a folder Account which have mainly three files.

  1. AuthenticateRequest.cs
  2. AuthenticateResponse.cs

We will explains only AuthenticateRequest and Response.

AuthenticateRequest.cs

The authenticate request model defines the parameters for incoming POST requests to the /accounts/authenticate route.

using System.ComponentModel.DataAnnotations;

namespace WebApi.Models.Accounts
{
    public class AuthenticateRequest
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        public string Password { get; set; }
    }
}

AuthenticateResponse.cs

The authenticate response model defines the data returned by the Authenticate method of the account controller after successful authentication. It include following code:

using System;
using System.Text.Json.Serialization;

namespace WebApi.Models.Accounts
{
    public class AuthenticateResponse
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Role { get; set; }
        public DateTime Created { get; set; }
        public DateTime? Updated { get; set; }
        public bool IsVerified { get; set; }
        public string JwtToken { get; set; }

        [JsonIgnore] // refresh token is returned in http only cookie
        public string RefreshToken { get; set; }
    }
}

 Note - For other models you can download code link given in at the end of tutorials.

6. Service

It contain one file called AccountService.cs.

AccountService.cs

The account service contains the core logic for authentication, generating JWT tokens and fetching user data. The Authenticate() method is used to verifies username and password in the database. The GetAll() method is used to returns a list of all users in the system.

using AutoMapper;
using BC = BCrypt.Net.BCrypt;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WebApi.Entities;
using WebApi.Helpers;
using WebApi.Models.Accounts;

namespace WebApi.Services
{
    public interface IAccountService
    {
        AuthenticateResponse Authenticate(AuthenticateRequest model, string ipAddress);
        AuthenticateResponse RefreshToken(string token, string ipAddress);
        void RevokeToken(string token, string ipAddress);
        void Register(RegisterRequest model, string origin);
        IEnumerable GetAll();
        AccountResponse GetById(int id);
    }

    public class AccountService : IAccountService
    {
        private readonly DataContext _context;
        private readonly IMapper _mapper;
        private readonly AppSettings _appSettings;

        public AccountService(
            DataContext context,
            IMapper mapper,
            IOptions appSettings)
        {
            _context = context;
            _mapper = mapper;
            _appSettings = appSettings.Value;
        }

        public void Register(RegisterRequest model, string origin)
        {
            // map model to new account object
            var account = _mapper.Map(model);

            // first registered account is an admin
            var isFirstAccount = _context.Accounts.Count() == 0;
            account.Role = isFirstAccount ? Role.Admin : Role.User;
            account.Created = DateTime.UtcNow;
            account.VerificationToken = randomTokenString();

            // hash password
            account.PasswordHash = BC.HashPassword(model.Password);

            // save account
            _context.Accounts.Add(account);
            _context.SaveChanges();
        }
        public AuthenticateResponse Authenticate(AuthenticateRequest model, string ipAddress)
        {
            var account = _context.Accounts.SingleOrDefault(x => x.Email == model.Email);

            if (account == null || !BC.Verify(model.Password, account.PasswordHash))
                throw new AppException("Email or password is incorrect");

            // authentication successful so generate jwt and refresh tokens
            var jwtToken = generateJwtToken(account);
            var refreshToken = generateRefreshToken(ipAddress);
            account.RefreshTokens.Add(refreshToken);

            // remove old refresh tokens from account
            removeOldRefreshTokens(account);

            // save changes to db
            _context.Update(account);
            _context.SaveChanges();

            var response = _mapper.Map(account);
            response.JwtToken = jwtToken;
            response.RefreshToken = refreshToken.Token;
            return response;
        }

        public AuthenticateResponse RefreshToken(string token, string ipAddress)
        {
            var (refreshToken, account) = getRefreshToken(token);

            // replace old refresh token with a new one and save
            var newRefreshToken = generateRefreshToken(ipAddress);
            refreshToken.Revoked = DateTime.UtcNow;
            refreshToken.RevokedByIp = ipAddress;
            refreshToken.ReplacedByToken = newRefreshToken.Token;
            account.RefreshTokens.Add(newRefreshToken);

            removeOldRefreshTokens(account);

            _context.Update(account);
            _context.SaveChanges();

            // generate new jwt
            var jwtToken = generateJwtToken(account);

            var response = _mapper.Map(account);
            response.JwtToken = jwtToken;
            response.RefreshToken = newRefreshToken.Token;
            return response;
        }

        public void RevokeToken(string token, string ipAddress)
        {
            var (refreshToken, account) = getRefreshToken(token);

            // revoke token and save
            refreshToken.Revoked = DateTime.UtcNow;
            refreshToken.RevokedByIp = ipAddress;
            _context.Update(account);
            _context.SaveChanges();
        }

        public IEnumerable GetAll()
        {
            var accounts = _context.Accounts;
            return _mapper.Map>(accounts);
        }

        public AccountResponse GetById(int id)
        {
            var account = getAccount(id);
            return _mapper.Map(account);
        }

        // helper methods

        private (RefreshToken, Account) getRefreshToken(string token)
        {
            var account = _context.Accounts.SingleOrDefault(u => u.RefreshTokens.Any(t => t.Token == token));
             if (account == null) throw new AppException("Invalid token");
            var refreshToken = account.RefreshTokens.Single(x => x.Token == token);
            if (!refreshToken.IsActive) throw new AppException("Invalid token");
            return (refreshToken, account);
        }

        private string generateJwtToken(Account account)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[] { new Claim("id", account.Id.ToString()) }),
                Expires = DateTime.UtcNow.AddMinutes(15),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        private RefreshToken generateRefreshToken(string ipAddress)
        {
            return new RefreshToken
            {
                Token = randomTokenString(),
                Expires = DateTime.UtcNow.AddDays(7),
                Created = DateTime.UtcNow,
                CreatedByIp = ipAddress
            };
        }

        private void removeOldRefreshTokens(Account account)
        {
            account.RefreshTokens.RemoveAll(x => 
                !x.IsActive && 
                x.Created.AddDays(_appSettings.RefreshTokenTTL) <= DateTime.UtcNow);
        }

        private string randomTokenString()
        {
            using var rngCryptoServiceProvider = new RNGCryptoServiceProvider();
            var randomBytes = new byte[40];
            rngCryptoServiceProvider.GetBytes(randomBytes);
            // convert random bytes to hex string
            return BitConverter.ToString(randomBytes).Replace("-", "");
        }

        private Account getAccount(int id)
        {
            var account = _context.Accounts.Find(id);
            if (account == null) throw new KeyNotFoundException("Account not found");
            return account;
        }
    }
}

7. AppSettings.json File

The appsettings. json file is an application configuration file used to store configuration settings such as database connections strings, any application scope global variables etc. The below we have defined the database connection string.

{
    "AppSettings": {
        "Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING",
        "RefreshTokenTTL": 2,
        "EmailFrom": "info@aspnet-core-signup-verification-api.com",
        "SmtpHost": "[ENTER YOUR OWN SMTP OPTIONS OR CREATE FREE TEST ACCOUNT IN ONE CLICK AT https://ethereal.email/]",
        "SmtpPort": 587,
        "SmtpUser": "",
        "SmtpPass": ""
    },
    "ConnectionStrings": {        
        "WebApiDatabase": "Server=DESKTOP-G82VNVU;Initial Catalog=EmployeeDB; Persist Security Info=True; Integrated Security=True"

    },
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*"
}

Note - The Secret property is used to sign and verify JWT tokens for authentication, change it to a random string to ensure nobody else can generate a JWT with the same secret and gain unauthorized access to your API.

8. Program.cs File

This file is used to as a entry point of application that means it start execution from it.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace WebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup()
                        .UseUrls("http://localhost:4000");
                });
    }
}

9. Startup.cs File

The startup class contains two methods:

  1. ConfigureServices
  2. Configure

Add the following code on startup file.

using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using WebApi.Helpers;
using WebApi.Middleware;
using WebApi.Services;

namespace WebApi
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // add services to the DI container
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext();
            services.AddCors();
            services.AddControllers().AddJsonOptions(x => x.JsonSerializerOptions.IgnoreNullValues = true);
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
            services.AddSwaggerGen();

            // configure strongly typed settings object
            services.Configure(Configuration.GetSection("AppSettings"));

            // configure DI for application services
            services.AddScoped(); 
        }

        // configure the HTTP request pipeline
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext context)
        {
            // migrate database changes on startup (includes initial db creation)
            //context.Database.Migrate();

            // generated swagger json and swagger ui middleware
            app.UseSwagger();
            app.UseSwaggerUI(x => x.SwaggerEndpoint("/swagger/v1/swagger.json", "ASP.NET Core Sign-up and Verification API"));

            app.UseRouting();

            // global cors policy
            app.UseCors(x => x
                .SetIsOriginAllowed(origin => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());

            // global error handler
            app.UseMiddleware();

            // custom jwt auth middleware
            app.UseMiddleware();

            app.UseEndpoints(x => x.MapControllers());
        }
    }
}

ConfigureServices Method

This method is used to configure services that are used by the application.

services.AddScoped<IAccountService, AccountService>(); 

Configure Method

This method is used to define how the application will respond on each HTTP request that means we can control the ASP.net pipeline. 

// generated swagger json and swagger ui middleware
            app.UseSwagger();
            app.UseSwaggerUI(x => x.SwaggerEndpoint("/swagger/v1/swagger.json", "ASP.NET Core Sign-up and Verification API"));

            app.UseRouting();

            // global cors policy
            app.UseCors(x => x
                .SetIsOriginAllowed(origin => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());

            // global error handler
            app.UseMiddleware();

            // custom jwt auth middleware
            app.UseMiddleware();

            app.UseEndpoints(x => x.MapControllers());

You can also check when we run the application. Swagger will also show all the controller methods.

ASP.NET Core Authentication Tutorial with Example API

3. Postman Tool -  To test ASP.NET API authentication.

The following are the testing tools for Web API.

  1. Katalon Studio
  2. Postman
  3. Apigee
  4. JMeter
  5. Rest-assured

Here , we will use Postman tool to test Webapi authentication.

You can use Postman is a tool for testing APIs, you can download it at https://www.postman.com/downloads.

Open the Postman and click New and select HTTP request.

ASP.NET Core Authentication Tutorial with Example API

Change the HTTP method to POST with the dropdown selector on the left of the URL input field. In the URL field enter the address to the authenticate route of your local API : 

http://localhost:53287/Accounts/register

Select the Body tab below the URL field, change the body type radio button to raw, and change the format dropdown selector to JSON.

{
  "title": "Mr",
  "firstName": "Rohatash",
  "lastName": "Kumar",
  "email": "rohatash@gmail.com",
  "password": "rohatash@123",
  "confirmPassword": "rohatash@123",
  "acceptTerms": true
}

ASP.NET Core Authentication Tutorial with Example API

Now check also on DB Table.

ASP.NET Core Authentication Tutorial with Example API

Authenticate Above registered user

 In the URL field enter the address to the authenticate route of your local API - http://localhost:53287/Accounts/Authenticate

Select the Body tab below the URL field, change the body type radio button to raw, and change the format dropdown selector to JSON.

Enter a JSON object containing the username and password in the Body textarea. Email and password will be the same as you registered above.

{
  "email": "rohatash@gmail.com",
  "password": "rohatash@123"
}

ASP.NET Core Authentication Tutorial with Example API

Get All users from Ablove Generated Token

  1. Open a new request tab by clicking the plus sign.
  2. Select the HTTP GET method from the dropdown list.
  3. In the URL field enter the address to the users route of your local API - http://localhost:53287/Accounts
  4. Select the Authorization tab below the URL field, change the type to Bearer Token in the type dropdown selector, and paste the JWT token from the previous step into the Token field.
  5. After that click on the send button to execute http request.

ASP.NET Core Authentication Tutorial with Example API

The above screenshot show all the authenticated request users.

Download ASP.NET Core - Authentication with Example API


Prev Next