Tuesday, August 8, 2017

Consuming a secured JWT webapi using ionic, angular 2

I had a requirement to create an app which consumes a secured JWT webapi using ionic and angular 2. This is how I did it. Hope you like.

Reference : http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/

1) Open Startup.cs and paste the following code

using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Http;

[assembly: OwinStartup(typeof(SecuredWebAPI.Startup))]
namespace SecuredWebAPI
{
    public class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            ConfigureOAuth(app);
            ConfigureOAuthTokenConsumption(app);

          
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        private void ConfigureOAuthTokenConsumption(IAppBuilder app)
        {
            //Hosting url
            var issuer = "http://localhost:53726";
            string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
            byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);

            // Api controllers with an [Authorize] attribute will be validated with JWT
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { audienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                    }
                });
        }
        public void ConfigureOAuth(IAppBuilder app)
        {
                   

            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                //For Dev enviroment only (on production should be AllowInsecureHttp = false)
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new SimpleAuthorizationServerProvider(),
                AccessTokenFormat = new CustomJwtFormat("http://localhost:53726") //Hosing Url
            };

            // OAuth 2.0 Bearer Access Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);

        }
    }
}


2) Create CustomJwtFormat.cs and paste the following code

using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Web;
using Thinktecture.IdentityModel.Tokens;

namespace SecuredWebAPI
{
    public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {

        private readonly string _issuer = string.Empty;
        public CustomJwtFormat(string issuer)
        {
            _issuer = issuer;
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
            string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
            var signingKey = new HmacSigningCredentials(keyByteArray);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.WriteToken(token);
            return jwt;
        }

        public AuthenticationTicket Unprotect(string protectedText)
        {
            throw new NotImplementedException();
        }
    }
}

3) Open web.config and add the following app settings

<!-- You can modify these if you want -->
 <add key="as:AudienceId" value="8866" />
  <add key="as:AudienceSecret" value="U2gxMjM0NTY3ODkwMjEyMTIxMjFyZXJlcmVycmVxdGg=" />


Now we will look at the client to consume this web api, I am not going to include all the angular 2 code here. I will just add the service and login codes. Remember you need 
angular2-jwt to consume this web api through generated access token.

1) Security.service.ts 

import { Injectable } from '@angular/core';
import {Http,Headers} from '@angular/http';
import { AppSettings } from '../app.settings';
import { LoginData,CurrentUserInfo } from '../app.common';
import {Observable} from 'rxjs/Observable';
import { AuthHttp } from 'angular2-jwt';

@Injectable()
export class SecurityService {
public currentUserInfo:CurrentUserInfo;
constructor(private http: Http,private authHttp:AuthHttp) {
this.currentUserInfo = new CurrentUserInfo();
}

login(loginData:LoginData) : any {
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');

var credentials = "grant_type=password"
+ "&userName=" + loginData.userName
+ "&password=" + loginData.password;


return this.http.post(AppSettings.API_ENDPOINT +'/token', credentials, { headers: headers });



}

}

2) Login.ts 

import { Component, ViewChild } from '@angular/core';
import { Nav, Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SecurityService } from '../../app/services/security.service'
import { LoginData, CurrentUserInfo } from '../../app/app.common'
import { AuthHttp,JwtHelper } from 'angular2-jwt';
import { AppSettings } from '../../app/app.settings';


@Component({
selector:'login',
templateUrl:'login.html',
providers: [SecurityService]
})

export class LoginPage {

private message:string;
private loginData:LoginData;


constructor(private secService:SecurityService,private authHttp:AuthHttp){
this.loginData = new LoginData();
}

login(loginData)
{
this.secService.login(loginData).subscribe((res) => {
var resObj = res.json();
if (typeof resObj.access_token != "undefined")
{
this.secService.currentUserInfo.isLoggedIn = true;
localStorage.setItem('access_token',resObj.access_token);

var jwtHelper = new JwtHelper();



localStorage.setItem('loginKey',jwtHelper.decodeToken(resObj.access_token).loginKey);

this.message = "Successfull Login";
this.authHttp.get(AppSettings.API_ENDPOINT +'/api/User/GetFunctions?loginKey='+localStorage.getItem('loginKey'))
.subscribe((res)=> {console.log(res);});
}
else
{
this.secService.currentUserInfo.isLoggedIn = false;
this.message = "login Failed!!! "+
JSON.parse(resObj._body).error_description;
}
},(error)=>{
this.secService.currentUserInfo.isLoggedIn = false;
this.message = "login Failed!!! "+ error;
});
}
}





Saturday, July 15, 2017

Secured Token Based WebAPI with custom authentication

Hi Guys,

I had a requirement to create Secured  Token Based WebAPI which can be consumed using mobile applications. And it had to check a custom authentication stored in database. Upon successful login an auth token would be returned subsequent requests must pass this auth token to get data.

Reference : http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/



1) Create a WebAPI Project as following




















2) Modify the Startup.cs as following

using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;

[assembly: OwinStartup(typeof(SecuredWebAPI.Startup))]
namespace SecuredWebAPI
{
    public class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            ConfigureOAuth(app);
           
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new SimpleAuthorizationServerProvider()

               
            };

         

            // Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

         

     

  }

    }
}


3) Modify the SimpleAuthorizationServerProvider.cs with the following


using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Security.Claims;
using System.Threading.Tasks;

namespace SecuredWebAPI
{
    public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            //Your authentication logic here
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

         

            int userId = -1;
            BE.ReturnDataMsg<Guid> objReturnLoginDataMsg = null;

            bool mustChangePwd;
            string name;
            objReturnLoginDataMsg =
                BusinessLayer.Services.UserService.Login(context.UserName, context.Password,
                1, true, out mustChangePwd, out name, out userId, true);

            if (objReturnLoginDataMsg.Status == BE.ProcessStatus.Successful ||
                    objReturnLoginDataMsg.Status == BE.ProcessStatus.SuccessButExpired)
            {

                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("sub", context.UserName));
                identity.AddClaim(new Claim("loginKey", objReturnLoginDataMsg.Data.ToString()));
                context.Validated(identity);


            }

            else
            {
                context.SetError("invalid_grant", objReturnLoginDataMsg.Message);
                return;

               

            }              

        }    

    }
}





Sunday, February 26, 2017

Uploading images using WebApi and JSON

Hi All,

I recently had a requirement to create a Web API to work with a mobile phone. I was required to create a method to upload images. So I did it the following way hope you like it.

1) Class Definitions
 public class AuthData
    {
        public string auth_token { get; set; }

    }
  
    public class ImageSetData : AuthData
    {
        public string Name { get; set; }
        public List<Image> Images { get; set; }
    }

    public class Image
    {
        public string FileName { get; set; }
        public string Extension { get; set; }
        public byte[] ImageData { get; set; }
    }

2) WebApi Method

public ReturnMsg SaveFile([FromBody] ImageSetData imageData)
        {
            ReturnMsg objReturnMsg = new ReturnMsg();

            try
            {
                if (APIHelper.AuthenticateToken(imageData.auth_token))
                {

                    foreach (var image in imageData.Images)
                    {
                        File.WriteAllBytes(string.Format("{0}\\{1}.{2}", HttpContext.Current.Server.MapPath("~/App_Data") , image.FileName, image.Extension), image.ImageData);
                    }  
                  




                }
                else
                {
                    objReturnMsg.success = false;
                    objReturnMsg.msg = "Invalid Token. Please Login to continue";
                }
            }
            catch (Exception ex)
            {

                objReturnMsg.success = false;
                objReturnMsg.msg = "Internal Server Error. Contact Admin.";
                APIHelper.Log(ex.Message, ex.StackTrace);
            }


            return objReturnMsg;
        }

3) Post Man Screen shot and Image




Wednesday, January 25, 2017

WCF Service and TLS 1.2


Hi All,

I worked on a WCF Service (.NET 3.5) which consumed an external web service and was working fine. Until recently it started giving the following error.

An error occurred while making the HTTP request to
{url}.This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case.This could also be caused by a mismatch of the security binding between the client and the server.

This made a big issue as the WCF was already being consumed by number of other systems. Later we got to know the external web service was upgraded to use TLS 1.2 and the .NET 3.5 doesn't support TLS 1.2 (.NET 4.5 or greater supports TLS 1.2). So I had to change the version of the .NET version to 4.5 and add the following code to enforce it to use TLS 1.2. And everything started to work the way they used to.


1) .NET 4.6 and above. You don’t need to do any additional work to support TLS 1.2, it’s supported by default.
2) .NET 4.5. TLS 1.2 is supported, but it’s not a default protocol. You need to opt-in to use it. The following code will make TLS 1.2 default, make sure to execute it before making a connection to secured resource:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

3) .NET 4.0. TLS 1.2 is not supported, but if you have .NET 4.5 (or above) installed on the system then you still can opt in for TLS 1.2 even if your application framework doesn’t support it. The only problem is that SecurityProtocolType in .NET 4.0 doesn’t have an entry for TLS1.2, so we’d have to use a numerical representation of this enum value:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

4) .NET 3.5 or below. TLS 1.2 is not supported (*) and there is no workaround. Upgrade your application to more recent version of the framework.


Sources : http://blogs.perficient.com/microsoft/2016/04/tsl-1-2-and-net-support/

Tuesday, August 16, 2016

Bilingual Dynamic Survey

Hi Guys,

I created a bilingual Dynamic Survey application using ASP.NET. Hope you like it.























Sunday, May 15, 2016

Request String Encryption in MVC 5

Hi Guys,

This time I had a requirement to Encrypt the request string parameters before passing them to the Controller's method. After referring this solution, I made this solution I hope you like it.

1) SecurityHelper 

public class SecurityHelper
    {
        public static string Encrypt(string plainText)
        {
            string key = "jdsg432387#";
            byte[] EncryptKey = { };
            byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
            EncryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByte = Encoding.UTF8.GetBytes(plainText);
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, des.CreateEncryptor(EncryptKey, IV), CryptoStreamMode.Write);
            cStream.Write(inputByte, 0, inputByte.Length);
            cStream.FlushFinalBlock();
            return Convert.ToBase64String(mStream.ToArray());
        }

        public static string Decrypt(string encryptedText)
        {
            string key = "jdsg432387#";
            byte[] DecryptKey = { };
            byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
            byte[] inputByte = new byte[encryptedText.Length];

            DecryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            inputByte = Convert.FromBase64String(encryptedText);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(DecryptKey, IV), CryptoStreamMode.Write);
            cs.Write(inputByte, 0, inputByte.Length);
            cs.FlushFinalBlock();
            System.Text.Encoding encoding = System.Text.Encoding.UTF8;
            return encoding.GetString(ms.ToArray());
        }

        public static string MakeQueryString(string queryString)
        {          

            return "p=" + Encrypt(queryString);

        }
    }

2) EncryptedActionParameterAttribute 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class EncryptedActionParameterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {

            Dictionary<string, object> decryptedParameters = new Dictionary<string, object>();
            if (filterContext.HttpContext.Request.QueryString.HasValue)
            {
                string encryptedQueryString = filterContext.HttpContext.Request.QueryString.Value.Substring(filterContext.HttpContext.Request.QueryString.Value.IndexOf("=")+1);
                string decrptedString = SecurityHelper.Decrypt(encryptedQueryString.ToString());
                string[] paramsArrs = decrptedString.Split('&');

                for (int i = 0; i < paramsArrs.Length; i++)
                {
                    string[] paramArr = paramsArrs[i].Split('=');
                    decryptedParameters.Add(paramArr[0], paramArr[1]);
                }
            }
            for (int i = 0; i < decryptedParameters.Count; i++)
            {
                filterContext.ActionArguments[decryptedParameters.Keys.ElementAt(i)] = decryptedParameters.Values.ElementAt(i);
            }
            base.OnActionExecuting(filterContext);

        }

   
    }

3) Index.cshtml

@{
    ViewData["Title"] = "Home Page";
}



<div class="row">
    <div class="col-md-12">
        <ul>
            @foreach (var person in PeopleHelper.GetPeople())
            {
                <li> @person.Name
                    <a href="/Home/Person?@SecurityHelper.MakeQueryString("id="+person.ID+"&name="+@person.Name)">
                        Click Here
                    </a>
                </li>
            }
        </ul>
     
    </div>
 
</div>

4) HomeController

 public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpGet]
        [EncryptedActionParameter]
        public IActionResult Person(string id, string name)
        {
            return View(new Person() { ID = int.Parse(id),Name = name } );
        }



        public IActionResult Error()
        {
            return View("~/Views/Shared/Error.cshtml");
        }
    }





Wednesday, March 30, 2016

Current Weather App using Google Maps and forecast.io

Hi Guys,

This time I made a real time whether application which uses the phone's GPS coordinates and Google Map's GPS coordinates to fetch whether information. I hope you will like it.