WCF Service With JWT Token

In this article, you will learn about WCF Service with JWT Token.

Introduction

WCF is a replacement for all earlier web service technologies from Microsoft. It also does a lot more than what is traditionally considered as "web services". Web Services can be accessed only over HTTP and works in a stateless environment where WCF is flexible because its services can be hosted in different types of applications. Common scenarios for hosting WCF Services are IIS, WAS, Self-hosting, and Managed Windows Service.

WCF Services are easy to create for those who know .NET Framework.  Here, I am giving an example for beginners to create a simple Web Service, using Visual Studio IDE.

Step 1

Create New-> project-> WCF Service application, as shown below.



Give some name (For my example, let’s take default name i.e., WcfService) and press OK button.

Step 2

Now, you can see that the Service.svc.cs window opens or you can see it on the right side solution panel. Open that file.
 
If you want to create a new Service, then go to Solution Explorer, right click->Add-> New item-> select WCF Service template and give name for that and press OK

Step 3

After opening Service.svc.cs file, add namespace,
  1. using System.ServiceModel.Activation;   
And, download Microsoft.ApplicationInsights.Web dll from NuGet package by right clicking the Solution, as shown below.



Add the below lines after WcfService1 namespace open brace.
  1. [ServiceContract]  
  2. [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  3. [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]  
Then, we can remove interface because in this example, I will not be using interface. So, remove interface from Service1 class.
  1. public class Service  
  2. {  
  3. }  
Step 4

Inside this class, we can start writing remaining service parts, as in the following example.
  1. private string secureToken;  
  2.         [OperationContract] //service contract  
  3.         [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)] //Post Method for gtting data according to the parameter  
  4.         public ResponseData Login(RequestData data) //Response class for retriving data  
  5.         {  
  6.             using (SqlConnection _con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["conString"].ToString()))  
  7.             {  
  8.                 SqlCommand cmd = new SqlCommand("sp_LogIn", _con);    
  9.                 cmd.CommandType = CommandType.StoredProcedure;  
  10.                 cmd.Parameters.AddWithValue("@UserName", data.usname);  
  11.                 cmd.Parameters.AddWithValue("@Password", data.pwd);  
  12.                 SqlDataAdapter da = new SqlDataAdapter(cmd);  
  13.                 DataSet dt_Login = new DataSet();  
  14.                 da.Fill(dt_Login, "table");  
  15.                 DataRow dr = dt_Login.Tables[0].Rows[0];  
  16.                 secureToken = GetJwt(data.usname, data.pwd, data.org);  
  17.                 var response = new ResponseData  
  18.                 {  
  19.                     token =secureToken ,  
  20.                     authenticated = true,  
  21.                     employeeId = dr["EmpId"].ToString(),  
  22.                     firstname = dr["emp_firstname"].ToString(),  
  23.                     timestamp = DateTime.Now,  
  24.                     userName = data.usname  
  25.                 };  
  26.                   
  27.                 return response;  
  28.             }  
  29.         }   
Step 5

Create one folder for Models and create a model as RequestData.cs for Login request.


  1. namespace WcfService.Models  
  2. {  
  3.     public class RequestData  
  4.     {  
  5.        public  string usname { get; set; }  
  6.        public  string pwd { get; set; }  
  7.     }  
  8. }  
And add namespace to Service.svc.cs using WcfService.Models.

Step 6

Create ResponseData.cs for storing response from Login Operation Contract.
  1. using System;  
  2. using System.Runtime.Serialization;  
  3.   
  4. namespace WcfService.Models  
  5. {  
  6.      
  7.         [DataContract]  
  8.         public class ResponseData  
  9.         {  
  10.             [DataMember(Order = 0)]  
  11.             public string token { get; set; }  
  12.             [DataMember(Order = 1)]  
  13.             public bool authenticated { get; set; }  
  14.             [DataMember(Order = 2)]  
  15.             public string employeeId { get; set; }  
  16.             [DataMember(Order = 3)]  
  17.             public string firstname { get; set; }  
  18.   
  19.             [DataMember(Order = 8)]  
  20.             public DateTime timestamp { get; set; }  
  21.             [DataMember(Order = 9)]  
  22.             public string userName { get; set; }  
  23.         }        
  24.       
  25.   }  
Step 7

Now, let’s create function for JWT Token as follows in Service.svc.cs. For JWT encoder, we need to download and add "Jose-jwt" reference, as shown below.

Install jose-jwt from NuGet package by right clicking the Solution.



After installing this, now add functions to the same class.
  1. private byte[] Base64UrlDecode(string arg) // This function is for decoding string to   
  2.   
  3.         {  
  4.             string s = arg;  
  5.             s = s.Replace('-''+'); // 62nd char of encoding  
  6.             s = s.Replace('_''/'); // 63rd char of encoding  
  7.             switch (s.Length % 4) // Pad with trailing '='s  
  8.             {  
  9.                 case 0: break// No pad chars in this case  
  10.                 case 2: s += "=="break// Two pad chars  
  11.                 case 3: s += "="break// One pad char  
  12.                 default:  
  13.                     throw new System.Exception(  
  14.                 "Illegal base64url string!");  
  15.             }  
  16.             return Convert.FromBase64String(s); // Standard base64 decoder  
  17.         }  
  18.         private long ToUnixTime(DateTime dateTime)  
  19.         {  
  20.             return (int)(dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1))).TotalSeconds;  
  21.         }  
  22.         public string GetJwt(string user, string pass) //function for JWT Token  
  23.         {  
  24.             byte[] secretKey = Base64UrlDecode("Hi");//pass key to secure and decode it  
  25.             DateTime issued = DateTime.Now;  
  26.             var User = new Dictionary<string, object>()  
  27.                     {  
  28.                         {"user", user},  
  29.                         {"pass", pass},  
  30.                          
  31.                          {"iat", ToUnixTime(issued).ToString()}  
  32.                     };  
  33.   
  34.             string token = JWT.Encode(User, secretKey, JwsAlgorithm.HS256);  
  35.             return token;  
  36.         }  
Step 8 Configure Web.Config file
  1.     <?xml version="1.0" encoding="utf-8"?>  
  2. <configuration>  
  3.   <connectionStrings>  
  4.     <add name="conString" connectionString="server= ;database=db_Name;User ID=sa;Password=123" providerName="System.Data.SqlClient"/>  
  5.   </connectionStrings>  
  6.   <system.web>  
  7.     <compilation debug="true" targetFramework="4.0" />  
  8.   </system.web>  
  9.   <system.serviceModel>  
  10.     <services>  
  11.       <service name="WcfService.Service" behaviorConfiguration="serviceBehavior">  
  12.         <endpoint binding="webHttpBinding" contract="WcfService.Service" behaviorConfiguration="httpBehavior"></endpoint>  
  13.           
  14.         <endpoint name="mexHttpBinding" address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />  
  15.       </service>  
  16.     </services>  
  17.     <behaviors>  
  18.       <serviceBehaviors>  
  19.         <behavior name="serviceBehavior">  
  20.           <!-- To avoid disclosing metadata information, set the value below to false before deployment -->  
  21.           <serviceMetadata httpGetEnabled="true" />  
  22.         <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->  
  23.           <serviceDebug includeExceptionDetailInFaults="true" />  
  24.           
  25.         </behavior>  
  26.       </serviceBehaviors>  
  27.       <endpointBehaviors>  
  28.         <behavior name="httpBehavior">  
  29.           <webHttp />  
  30.         </behavior>  
  31.       </endpointBehaviors>  
  32.        
  33.     </behaviors>  
  34.     <serviceHostingEnvironment multipleSiteBindingsEnabled="false" />  
  35.   </system.serviceModel>  
  36.  <system.webServer>  
  37.     <modules runAllManagedModulesForAllRequests="true">  
  38.        <remove name="ApplicationInsightsWebTracking" />  
  39.        <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />  
  40.     </modules>  
  41.     <directoryBrowse enabled="true" />  
  42.     <validation validateIntegratedModeConfiguration="false" />  
  43.   </system.webServer>  
  44.   
  45.   <runtime>  
  46.   
  47.     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
  48.   
  49.       <dependentAssembly>  
  50.   
  51.         <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />  
  52.   
  53.         <bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />  
  54.   
  55.       </dependentAssembly>  
  56.   
  57.       <dependentAssembly>  
  58.   
  59.         <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />  
  60.   
  61.         <bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />  
  62.   
  63.       </dependentAssembly>  
  64.   
  65.       <dependentAssembly>  
  66.   
  67.         <assemblyIdentity name="Microsoft.Diagnostics.Tracing.EventSource" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />  
  68.   
  69.         <bindingRedirect oldVersion="0.0.0.0-1.1.28.0" newVersion="1.1.28.0" />  
  70.   
  71.       </dependentAssembly>  
  72.   
  73.     </assemblyBinding>  
  74.   
  75.   </runtime>  
  76. </configuration>  
Step 9

For security, create one class as below and add it into Web.Config file. It is used to provide security using custom username and password. For that, create DistributorValidator.cs class.

DistributorValidator.cs is shown below.
  1. using System;  
  2. using System.Net;  
  3. using System.ServiceModel;  
  4. using System.ServiceModel.Web;  
  5.   
  6. namespace WcfService  
  7. {  
  8.     public class DistributorValidator : ServiceAuthorizationManager  
  9.     {  
  10.         protected override bool CheckAccessCore(OperationContext operationContext)  
  11.         {  
  12.             //Extract the Authorization header, and parse out the credentials converting the Base64 string:  
  13.             var authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];  
  14.             if ((authHeader != null) && (authHeader != string.Empty))  
  15.             {  
  16.                 var svcCredentials = System.Text.ASCIIEncoding.ASCII  
  17.                         .GetString(Convert.FromBase64String(authHeader.Substring(6)))  
  18.                         .Split(':');  
  19.                 var user = new { Name = svcCredentials[0], Password = svcCredentials[1] };  
  20.                 if ((user.Name == "smart" && user.Password == "andr0id"))  
  21.                 {  
  22.                     //User is authrized and originating call will proceed  
  23.                     return true;  
  24.                 }  
  25.                 else  
  26.                 {  
  27.                     //not authorized  
  28.                     return false;  
  29.                 }  
  30.             }  
  31.             else  
  32.             {  
  33.                 //No authorization header was provided, so challenge the client to provide before proceeding:  
  34.                 WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"MyWCFService\"");  
  35.                 //Throw an exception with the associated HTTP status code equivalent to HTTP status 401  
  36.                 throw new WebFaultException(HttpStatusCode.Unauthorized);  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41. }  
Then add,
  1. <serviceAuthorization serviceAuthorizationManagerType="WcfService1.DistributorValidator, WcfService"/>  
Into Web.config file. That’s it.

Step 10

Now, run the service by right clicking Service.svc View in browser. If there is no error messages, then you will get the following window.



Here you will get a service link as in figure above: localhost:8080/Service.svc

To check this web service download Advanced REST client for chrome web browser as below,



Now, open the Advanced REST client from Chrome, give inputs to that, and send . You will get notification 200; i.e., OK. If you get an error, fix those errors explained in the message and try sending again.



When you send it  the first time, you will get one popup asking for username and password. Enter those from DistributorValidator.cs class. In my example, I gave username - "smart" and password - "andr0id".