Security in ASP.NET Web API


Create ASP.NET Web Application and Select Web API Project in Visual Studio.

Create Models folder in server project. Create a entity class: Account.cs as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LearnASPNETWebAPIWithRealApps.Models
{
    public class Account
    {
        public string Username
        {
            get;
            set;
        }

        public string Password
        {
            get;
            set;
        }

        public string []Roles
        {
            get;
            set;
        }

    }
}

Create a model class: AccountModel.cs in Models folder as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LearnASPNETWebAPIWithRealApps.Models
{
    public class AccountModel
    {
        private List<Account> accounts = new List<Account>();

        public AccountModel()
        {
            accounts.Add(new Account { Username = "acc1", Password = "123", Roles = new string[] { "superadmin", "admin", "employee" } });
            accounts.Add(new Account { Username = "acc2", Password = "123", Roles = new string[] { "admin", "employee" } });
            accounts.Add(new Account { Username = "acc3", Password = "123", Roles = new string[] { "employee" } });
        }

        public Account find(string username)
        {
            return accounts.SingleOrDefault(acc => acc.Username.Equals(username));
        }

        public Account login(string username, string password)
        {
            return accounts.SingleOrDefault(acc => acc.Username.Equals(username) && acc.Password.Equals(password));
        }

    }
}




Create Security folder in server project. This folder contain classes need for security in ASP.NET Web API. Create classes: Credential.cs, MyAuthorize.cs and MyPrincipal.cs as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LearnASPNETWebAPIWithRealApps.Security
{
    public class Credential
    {
        public string Username
        {
            get;
            set;
        }

        public string Password
        {
            get;
            set;
        }

    }
}
using LearnASPNETWebAPIWithRealApps.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;

namespace LearnASPNETWebAPIWithRealApps.Security
{
    public class MyAuthorize : AuthorizeAttribute
    {
        private const string BasicAuthResponseHeader = "WWW-Authenticate";
        private const string BasicAuthResponseHeaderValue = "Basic";

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            try
            {
                AuthenticationHeaderValue authValue = actionContext.Request.Headers.Authorization;
                if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter) && authValue.Scheme == BasicAuthResponseHeaderValue)
                {
                    Credential parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
                    var myPrincipal = new MyPrincipal(parsedCredentials.Username);
                    if (!myPrincipal.IsInRole(Roles))
                    {
                        actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                        actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
                        return;
                    }
                }
                else
                {
                    actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                    actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
                    return;
                }
            }
            catch (Exception ex)
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
            }
        }

        private Credential ParseAuthorizationHeader(string authHeader)
        {
            string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });
            if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
                return null;
            return new Credential() { Username = credentials[0], Password = credentials[1], };
        }

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
using LearnASPNETWebAPIWithRealApps.Models;

namespace LearnASPNETWebAPIWithRealApps.Security
{
    public class MyPrincipal : IPrincipal
    {
        private Account account;
        private AccountModel accountModel = new AccountModel();

        public MyPrincipal(string username)
        {
            this.Identity = new GenericIdentity(username);
            this.account = accountModel.find(username);
        }

        public IIdentity Identity
        {
            get;
            set;
        }

        public bool IsInRole(string role)
        {
            var roles = role.Split(new char[] { ',' });
            return roles.Any(r => this.account.Roles.Contains(r));
        }
    }
}

Create Web API Controller provides text/plain data for the client

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using LearnASPNETWebAPIWithRealApps.Security;
using System.Net.Http.Headers;

namespace LearnASPNETWebAPIWithRealApps.Controllers
{
    [RoutePrefix("api/demo")]
    public class DemoRestController : ApiController
    {

        [HttpGet]
        [Route("work1")]
        [AllowAnonymous]
        public HttpResponseMessage Work1()
        {
            try
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);
                response.Content = new StringContent("Work 1");
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
                return response;
            }
            catch
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }

        [HttpGet]
        [Route("work2")]
        [MyAuthorize(Roles = "superadmin")]
        public HttpResponseMessage Work2()
        {
            try
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);
                response.Content = new StringContent("Work 2");
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
                return response;
            }
            catch
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }

        [HttpGet]
        [Route("work3")]
        [MyAuthorize(Roles = "superadmin,admin")]
        public HttpResponseMessage Work3()
        {
            try
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);
                response.Content = new StringContent("Work 3");
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
                return response;
            }
            catch
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }

        [HttpGet]
        [Route("work4")]
        [MyAuthorize(Roles = "superadmin,admin,employee")]
        public HttpResponseMessage Work4()
        {
            try
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);
                response.Content = new StringContent("Work 4");
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
                return response;
            }
            catch
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }

    }
}




Access Web API use the following url: http://localhost:64967/api/demo/work1

Output

Work 1

Access restful web services use the following url: http://localhost:64967/api/demo/work2

Output

Access restful web services use the following url: http://localhost:64967/api/demo/work3

Output

Access restful web services use the following url: http://localhost:64967/api/demo/work4

Output

Create Console Application Project in Visual Studio.

DemoRestClientModel class contain methods call Web API

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;

namespace LearnASPNETWebAPIWithRealApps_Client
{
    public class DemoRestClientModel
    {
        private string BASE_URL = "http://localhost:64967/api/demo/";
        private string username = "acc2";
        private string password = "123";

        public Task<HttpResponseMessage> work1()
        {
            try
            {
                HttpClient client = new HttpClient();
                client.BaseAddress = new Uri(BASE_URL);
                return client.GetAsync("work1");
            }
            catch
            {
                return null;
            }
        }

        public Task<HttpResponseMessage> work2_without_account()
        {
            try
            {
                HttpClient client = new HttpClient();
                client.BaseAddress = new Uri(BASE_URL);
                return client.GetAsync("work2");
            }
            catch
            {
                return null;
            }
        }

        public Task<HttpResponseMessage> work2()
        {
            try
            {
                HttpClient client = new HttpClient();
                var authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(this.username + ":" + this.password));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);
                client.BaseAddress = new Uri(BASE_URL);
                return client.GetAsync("work2");
            }
            catch
            {
                return null;
            }
        }

        public Task<HttpResponseMessage> work3()
        {
            try
            {
                HttpClient client = new HttpClient();
                var authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(this.username + ":" + this.password));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);
                client.BaseAddress = new Uri(BASE_URL);
                return client.GetAsync("work3");
            }
            catch
            {
                return null;
            }
        }

        public Task<HttpResponseMessage> work4()
        {
            try
            {
                HttpClient client = new HttpClient();
                var authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(this.username + ":" + this.password));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);
                client.BaseAddress = new Uri(BASE_URL);
                return client.GetAsync("work4");
            }
            catch
            {
                return null;
            }
        }

    }
}




using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace LearnASPNETWebAPIWithRealApps_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            DemoRestClientModel demoRestClientModel = new DemoRestClientModel();

            Console.WriteLine("Test with work1 web method");
            HttpResponseMessage httpResponseMessage1 = demoRestClientModel.work1().Result;
            HttpStatusCode httpStatusCode1 = httpResponseMessage1.StatusCode;
            Console.WriteLine("\tStatus Code: " + httpStatusCode1);
            String result1 = httpResponseMessage1.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\tResult: " + result1);

            Console.WriteLine("Test with work2 web method without account");
            HttpResponseMessage httpResponseMessage2 = demoRestClientModel.work2_without_account().Result;
            HttpStatusCode httpStatusCode2 = httpResponseMessage2.StatusCode;
            Console.WriteLine("\tStatus Code: " + httpStatusCode2);
            String result2 = httpResponseMessage2.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\tResult: " + result2);

            Console.WriteLine("Test with acc2 have roles: admin and employee access work2 web method");
            HttpResponseMessage httpResponseMessage3 = demoRestClientModel.work2().Result;
            HttpStatusCode httpStatusCode3 = httpResponseMessage3.StatusCode;
            Console.WriteLine("\tStatus Code: " + httpStatusCode3);
            String result3 = httpResponseMessage3.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\tResult: " + result3);

            Console.WriteLine("Test with acc2 have roles: admin and employee access work3 web method");
            HttpResponseMessage httpResponseMessage4 = demoRestClientModel.work3().Result;
            HttpStatusCode httpStatusCode4 = httpResponseMessage4.StatusCode;
            Console.WriteLine("\tStatus Code: " + httpStatusCode4);
            String result4 = httpResponseMessage4.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\tResult: " + result4);

            Console.WriteLine("Test with acc2 have roles: admin and employee access work4 web method");
            HttpResponseMessage httpResponseMessage5 = demoRestClientModel.work4().Result;
            HttpStatusCode httpStatusCode5 = httpResponseMessage5.StatusCode;
            Console.WriteLine("\tStatus Code: " + httpStatusCode5);
            String result5 = httpResponseMessage5.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\tResult: " + result5);

            Console.ReadLine();
        }
    }
}
Test with work1 web method
        Status Code: OK
        Result: Work 1
Test with work2 web method without account
        Status Code: Unauthorized
        Result:
Test with acc2 have roles: admin and employee access work2 web method
        Status Code: Unauthorized
        Result:
Test with acc2 have roles: admin and employee access work3 web method
        Status Code: OK
        Result: Work 3
Test with acc2 have roles: admin and employee access work4 web method
        Status Code: OK
        Result: Work 4