When you create a new project and use a Class Library project type, the default configuration is to put the dll's in a folder named bin/release or bin/debug depending on your current build. Then if you add a web service (either asmx or wcf), the web service will not be able to find the dll's because they are nested. You must change the Project Properties, Compile tab, Build Output Path to just "/bin".
If you fail to do this, when you right click on the web service, and click View in Browser, you will get a parser error saying: Could not create type.
This is a rare problem, because most of the time, you are creating a web project type that makes this configuration for you.
Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts
Wednesday, March 30, 2016
Friday, February 26, 2016
WCF Timeouts
Timeouts can be configured on either the client side or the server side. They are set on the binding element in the web.config file. See here for the specific details.
Client side timeouts are pretty self explanatory. If you don't receive the response in the specified time period, the connection is closed and your code moves on. Server side timeouts work a bit differently than I expected. First of all, when you set a server side timeout, you are saying "After the connection is opened up, if I don't receive the full request in the specified amount of time, I'm going to time out." What it is not saying is "If I receive the request and it takes me longer than the specified time to process the request, I time out." You'll just keep churning away probably until your database connection times out. I believe client side timeouts are set to a minute by default, but can be lengthened.
Client side timeouts are pretty self explanatory. If you don't receive the response in the specified time period, the connection is closed and your code moves on. Server side timeouts work a bit differently than I expected. First of all, when you set a server side timeout, you are saying "After the connection is opened up, if I don't receive the full request in the specified amount of time, I'm going to time out." What it is not saying is "If I receive the request and it takes me longer than the specified time to process the request, I time out." You'll just keep churning away probably until your database connection times out. I believe client side timeouts are set to a minute by default, but can be lengthened.
Thursday, July 23, 2015
Security Auditing in WCF
It is possible to log all security successes and/or failures to the event log by just modifying your configuration file. This can be a quick and easy way to see if any funny business is going on with your web service. However, a better solution is to log these types of events to a database that is easier to check and query on if you're doing this on a regular basis.
I'm a fan of the Service Configuration Editor tool (In Visual Studio, right click the web.config and select Edit WCF Configuration) rather than changing the XML directly, but it's helpful to see both.
First add a Service Behavior Configuration. It doesn't necessarily have to be named. Then add the serviceSecurityAudit behavior to the configuration:
Now, expand the behavior configuration, and select the newly added serviceSecurityAudit:
I recommend choosing the "Application" log as the location. Here, I have chosen to log both successes and failures at the message level. Once this is set up, simply go to the Event Viewer and you'll see information entries for each authentication or rejection. To turn it off, just set it to None and leave it in the web.config in case you want to turn it on again.
Here is the settings as they exist in the XML:
I'm a fan of the Service Configuration Editor tool (In Visual Studio, right click the web.config and select Edit WCF Configuration) rather than changing the XML directly, but it's helpful to see both.
First add a Service Behavior Configuration. It doesn't necessarily have to be named. Then add the serviceSecurityAudit behavior to the configuration:
Now, expand the behavior configuration, and select the newly added serviceSecurityAudit:
I recommend choosing the "Application" log as the location. Here, I have chosen to log both successes and failures at the message level. Once this is set up, simply go to the Event Viewer and you'll see information entries for each authentication or rejection. To turn it off, just set it to None and leave it in the web.config in case you want to turn it on again.
Here is the settings as they exist in the XML:
<behaviors>
<servicebehaviors>
<behavior name="">
...
<servicesecurityaudit auditloglocation="Application"
messageauthenticationauditlevel="SuccessOrFailure"
serviceauthorizationauditlevel="None">
</servicesecurityaudit>
</behavior>
</servicebehaviors>
</behaviors>
Wednesday, September 10, 2014
Create a secure ASP.NET Web Api web service from scratch.
In a previous post, I described how to create a secure WCF web service. Here, I will describe how to set up the same type of service using ASP.NET Web Api technology.
Initial Setup
First, create a new project using the ASP.NET Web Application template. Choose the Web Api project template with No Authentication.
In the Web tab of the Project Properties, change it to Local IIS, and create a virtual directory. Then change the application pool of this virtual directory to a pool that uses your own more powerful account. While, you're in there, require SSL.
At this point, make sure you're everything is working by browsing to the api using this uri: NameOfSite/api/values/5. If you get json in return, you're good so far.
Authentication
Authentication and authorization can be configured automatically for you if you choose Local Accounts when you add the project. However, if you want to implement your own custom authentication and authorization code, you need to jump through some hoops.
Authentication of a Web Api is done through what is called an Http Module. This is a bit of code that you write that actually runs on IIS before the request hits your web service. It's pretty heavy stuff, but is the only way I know how to do this at the time of this writing. Add an Infrastructure folder to your project and add a class that implements the IHttpModule interface. Like this:
To enable IIS to use this HTTP Module, add the following to your web.config:
In IIS, you also need to disable all authentication including anonymous on the site where the web api is hosted.
Do a quick test in the browser. You should find that a login window comes up (in IE anyway) with which to provide your credentials.
Next, you'll want to create a little client application to test the web service. A console application works great for this. However, you will need to install a NuGet package called Microsoft ASP.NET Web API Client Libraries. The id is Microsoft.AspNet.WebApi.Client. Here is the code to call the Get method from your console:
Authorization
To create your own custom authorization, you just need to create your own Authorization Filter. Just add a class called CustomAuthorizeAttribute to your Infrastructure folder like this:
There are a few things to note here.
To call a POST method from your client, do the following:
There are some key differences here between WCF and Web API. With WCF, you can just call a function and specify parameters just like you would if the method was local. With Web API, in cases of GETs the parameters are either provided via the URL with query parameters or the route. With POSTs you pass a JSON object as data. This JSON object has a "property" for each parameter that you need.
Initial Setup
First, create a new project using the ASP.NET Web Application template. Choose the Web Api project template with No Authentication.
In the Web tab of the Project Properties, change it to Local IIS, and create a virtual directory. Then change the application pool of this virtual directory to a pool that uses your own more powerful account. While, you're in there, require SSL.
At this point, make sure you're everything is working by browsing to the api using this uri: NameOfSite/api/values/5. If you get json in return, you're good so far.
Authentication
Authentication and authorization can be configured automatically for you if you choose Local Accounts when you add the project. However, if you want to implement your own custom authentication and authorization code, you need to jump through some hoops.
Authentication of a Web Api is done through what is called an Http Module. This is a bit of code that you write that actually runs on IIS before the request hits your web service. It's pretty heavy stuff, but is the only way I know how to do this at the time of this writing. Add an Infrastructure folder to your project and add a class that implements the IHttpModule interface. Like this:
Imports System.Net.Http.Headers
Imports System.Security.Principal
Public Class BasicAuthHttpModule
Implements IHttpModule
Public Sub Dispose() Implements IHttpModule.Dispose
End Sub
Public Sub Init(context As HttpApplication) Implements IHttpModule.Init
AddHandler context.AuthenticateRequest, AddressOf OnAuthenticateRequest
AddHandler context.EndRequest, AddressOf OnEndRequest
End Sub
Private Sub SetPrincipal(principal As IPrincipal)
Threading.Thread.CurrentPrincipal = principal
If HttpContext.Current IsNot Nothing Then
HttpContext.Current.User = principal
End If
End Sub
Private Function CheckUserNameAndPassword(userName As String, password As String) As Boolean
If userName = "Steve" And password = "topsecret" Then
Return True
Else
Return False
End If
End Function
Private Function AuthenticateUser(userNamePasswordCombo As String) As Boolean
Dim Validated As Boolean = False
Try
Dim MyEncoding = Encoding.GetEncoding("iso-8859-1")
userNamePasswordCombo = MyEncoding.GetString(Convert.FromBase64String(userNamePasswordCombo))
Dim Separator As Integer = userNamePasswordCombo.IndexOf(":")
Dim UserName As String = userNamePasswordCombo.Substring(0, Separator)
Dim Password As String = userNamePasswordCombo.Substring(Separator + 1)
Validated = CheckUserNameAndPassword(UserName, Password)
If Validated Then
Dim MyIdentity = New GenericIdentity(UserName)
SetPrincipal(New GenericPrincipal(MyIdentity, Nothing))
End If
Catch ex As Exception
Validated = False
End Try
Return Validated
End Function
Private Sub OnAuthenticateRequest(sender As Object, e As EventArgs)
Dim MyRequest As HttpRequest = HttpContext.Current.Request
Dim AuthHeader As String = MyRequest.Headers("Authorization")
If AuthHeader IsNot Nothing Then
Dim AuthHeaderValue = AuthenticationHeaderValue.Parse(AuthHeader)
If AuthHeaderValue.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) And AuthHeaderValue.Parameter IsNot Nothing Then
AuthenticateUser(AuthHeaderValue.Parameter)
End If
End If
End Sub
Private Sub OnEndRequest(sender As Object, e As EventArgs)
Dim MyResponse As HttpResponse = HttpContext.Current.Response
If MyResponse.StatusCode = 401 Then
MyResponse.Headers.Add("WWW-Authenticate", "Basic realm=""My Realm""")
End If
End Sub
End Class
To enable IIS to use this HTTP Module, add the following to your web.config:
<system.webServer>
<modules>
<add name="BasicAuthHttpModule"
type="NameOfWebApplication.BasicAuthHttpModule, NameOfWebApplication"/>
</modules>
</system.webServer>
In IIS, you also need to disable all authentication including anonymous on the site where the web api is hosted.
Do a quick test in the browser. You should find that a login window comes up (in IE anyway) with which to provide your credentials.
Next, you'll want to create a little client application to test the web service. A console application works great for this. However, you will need to install a NuGet package called Microsoft ASP.NET Web API Client Libraries. The id is Microsoft.AspNet.WebApi.Client. Here is the code to call the Get method from your console:
Async Function WebApiGetAsync() As Task
Using Client As New HttpClient()
Client.BaseAddress = New Uri("https://NameOfServer.Domain.com/NameOfSite/")
Client.DefaultRequestHeaders.Accept.Clear()
Client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
Dim ByteArray = Encoding.ASCII.GetBytes("Steve:topsecret")
Client.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Basic", Convert.ToBase64String(ByteArray))
Dim Response As HttpResponseMessage = Await Client.GetAsync("api/values/5")
If Response.IsSuccessStatusCode Then
Dim Results As String = Await Response.Content.ReadAsStringAsync()
Console.WriteLine(Results)
Else
Console.WriteLine(Response.StatusCode.ToString)
Dim Results As String = Await Response.Content.ReadAsStringAsync()
Console.WriteLine(Results)
End If
End Using
End Function
Authorization
To create your own custom authorization, you just need to create your own Authorization Filter. Just add a class called CustomAuthorizeAttribute to your Infrastructure folder like this:
Public Class CustomAuthorizeAttribute
Inherits System.Web.Http.AuthorizeAttribute
Protected Overrides Function IsAuthorized(actionContext As Http.Controllers.HttpActionContext) As Boolean
Dim Authorized As Boolean = False
For Each ThisRole As String In Roles.Split(",").ToList
If SomeCustomLibrary.IsInRole(Threading.Thread.CurrentPrincipal.Identity.Name, ThisRole) Then
Authorized = True
Exit For
End If
Next
Return Authorized
End Function
End Class
There are a few things to note here.
- You must inherit System.Web.Http.AuthorizeAttribute and not System.Web.Mvc.AuthorizeAttribute.
- This is invoked by using <CustomAuthorize(Roles:="RoleName1, RoleName2")>
To call a POST method from your client, do the following:
Async Function WebApiPostAsync() As Task
Using Client As New HttpClient()
Client.BaseAddress = New Uri("https://NameOfServer.YourDomain.com/NameOfSite/")
Dim MyModel As New ParameterModel With {.Parameter1 = "From the Web Service.", .Parameter2 = 484507, .Parameter3 = Now.Date}
Dim ByteArray = Encoding.ASCII.GetBytes("Steve:topsecret")
Client.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Basic", Convert.ToBase64String(ByteArray))
Dim Response As HttpResponseMessage = Await Client.PostAsJsonAsync("api/NameOfController", MyModel)
If Response.IsSuccessStatusCode Then
Dim Results As String = Await Response.Content.ReadAsStringAsync()
Console.WriteLine(Results)
Else
Console.WriteLine(Response.StatusCode.ToString)
Dim Results As String = Await Response.Content.ReadAsStringAsync()
Console.WriteLine(Results)
End If
End Using
End Function
There are some key differences here between WCF and Web API. With WCF, you can just call a function and specify parameters just like you would if the method was local. With Web API, in cases of GETs the parameters are either provided via the URL with query parameters or the route. With POSTs you pass a JSON object as data. This JSON object has a "property" for each parameter that you need.
Thursday, September 4, 2014
Use SoapUI to test your web services
Recently, I created a web service that external organizations would use as an API to access our data. I decided to use WCF and the SOAP architecture to achieve this. I also created a little .NET console app to test the web service. Everything worked great. However, I was a little concerned that I might have unintentionally wrote some code that was only supported by the Microsoft stack. How could I be sure that my web service could be used by someone using a different platform?
My first idea was to try to access the web service using jQuery. I felt that if I could successfully invoke the web service using jQuery or JavaScript, this would demonstrate that Microsoft technology wasn't necessary to use my service. However, I found that calling a SOAP based web service from client side scripting is not an easy task. In fact, I never got this to work, and there is not a lot of information out there about how to do this. One problem is that browsers have security measures to stop cross site scripting. So the web service has to be part of the same site. If you can get past that, you also have to manually build the SOAP envelope in your javascript which is tedious. On top of that you have to figure out how to specify your logon credentials within the request. I concluded that it is just not practical to call a SOAP service from the client. I think this is where the advantages of using a REST style web service really pay off.
I discovered a widely used application called SoapUI. It is a free download, and you can use it to invoke the methods of your web service for testing purposes. This is a way to independently test your web service operations without being bound to Microsoft.
Installation is straight forward and instructions can be found on the site. Note that you do not need to install Hermes if you're not testing a Java service.
Here is how to test a web service operation.
My first idea was to try to access the web service using jQuery. I felt that if I could successfully invoke the web service using jQuery or JavaScript, this would demonstrate that Microsoft technology wasn't necessary to use my service. However, I found that calling a SOAP based web service from client side scripting is not an easy task. In fact, I never got this to work, and there is not a lot of information out there about how to do this. One problem is that browsers have security measures to stop cross site scripting. So the web service has to be part of the same site. If you can get past that, you also have to manually build the SOAP envelope in your javascript which is tedious. On top of that you have to figure out how to specify your logon credentials within the request. I concluded that it is just not practical to call a SOAP service from the client. I think this is where the advantages of using a REST style web service really pay off.
I discovered a widely used application called SoapUI. It is a free download, and you can use it to invoke the methods of your web service for testing purposes. This is a way to independently test your web service operations without being bound to Microsoft.
Installation is straight forward and instructions can be found on the site. Note that you do not need to install Hermes if you're not testing a Java service.
Here is how to test a web service operation.
- Open SoapUI and right click on Projects, New SOAP Project.
- Name the project and specify the address of the WSDL of your service.
- Check the box that says Create Sample Requests.
- In the navigator, you'll see each operation of the service. If you click the plus sign next to each operation you will see the sample request that was created for you.
- Click on the request and find the request properties.
- If the web service security is set up for TransportWithMessageCredential, then set the username and password properties to something that works. Also set the WSS-Password Type to PasswordText.
- Double-click the request.
- In the request window, you'll see the SOAP envelope. You can see where the parameters go, and can set these here.
- Then, click the green arrow which sends the request to the web service.
- The results will be shown in the right-hand pane.
Friday, August 1, 2014
Setting up a secure WCF web service from "scratch" with ASP.NET Identity
For this example, I'm using Visual Studio 2013 with Update 2 installed. Update 2 will ensure that the templates use the latest and greatest versions of all technologies involved.
Add a project using the WCF Service Application template. You could also use the Asp.Net Web Application template, but I think you'll just end up with a bunch of extra references you don't need. Now take a look at the web.config file. You'll notice that using this template automatically adds the protocolMapping element which allows the service to be accessed using https. If you did not use this template, you would have had to add this for https support. You'll also notice that there are no endpoints or bindings configured. That's because WCF has default bindings and IIS can determine the endpoint by examining the .svc file.
I delete the sample service created for you.
Next, I find it convenient to host the web services I'm developing on my local IIS. Click on My Project, Web tab, and type http://{name of your computer}.{domain}.{com}/{something meaningful}. It is important to use the fully qualified name of your computer because the certificate you will soon use is named the same and WCF won't like it if they are not the same. Click Create Virtual Directory. Open up IIS Manager, and change the app pool of the site to one with an associated identity of your (hopefully powerful) account. This becomes important when you connect to a database for authentication.
Add your web service by using the WCF Service template. Build the solution, and right click the service. Choose View in Browser to make sure all is well so far.
Now it's time to enable SSL (https). Read here to do this. At this point, you'll want to change the Project URL to include https on the Web tab when you go to My Project. I think it's a good idea to go ahead and setup a binding configuration and an endpoint. As I said earlier, WCF can figure all this out because of default values. However, Visual Studio's Add Service Reference automatic proxy generator can get confused when you are setting up a client. You can either use the Microsoft Service Configuration Editor (Right click web.config and choose Edit WCF Configuration) tool or edit the web.config by hand. If you use the tool, just click the Service folder, and choose Create a New Service. This will walk you through a wizard to create the service node. After that there is a link to create the binding configuration. You end up with this inside the <system.serviceModel> element:
You also need to set the multipleSiteBindingEnabled to False when you are using https:
At this point, I would create a little test app in a completely different solution and make sure you can call your new web service. A Windows Console app will work great. Just Add, Service Reference and specify the endpoint of the service. By adding the reference, Visual Studio will add the following to your app.config file:
There are a couple of things to note here. Why does the client configuration set up for basicHttpBinding when the server configuration is set up for basicHttpsBinding? Essentially basicHttpBinding and basicHttpsBinding are exactly the same. The only difference is that basicHttpsBinding includes the <security mode="Transport" /> by default.
Now that the web service is up and running with SSL we want to configure it to require credentials in order to access it. You can use the ASP.NET Membership, but this has been superceded by ASP.NET Identity and WCF doesn't directly interface with ASP.NET Identity yet. So, we will create a custom user name and password validator which we will eventually use to authenticate with ASP.NET Identity. First, we need to create the validator class itself. Add references to the following:
The only configuration change in your client console app is changing the security mode to "TransportWithMessageCredential". When you run your console, you will notice that you'll get an exception if you fail to provide credentials. To specify the client's credentials, do the following:
If the client provides accurate credentials, then the service will return results; otherwise, an exception will be thrown.
With this infrastructure in place, you can change the code in the CustomUserNameValidator class to connect to any database you want to authenticate. I mentioned earlier my desire to access ASP.NET Identity to authenticate users for the web service. In a separate solution, I have set up an ASP.NET MVC Web Application and configured it with ASP.NET Identity authentication. I am going to use this web application to administer user accounts for my web service. I simply add this class to my MVC project:
Now, if I can call this function from my CustomUserNameValidator in my WCF project, I will be authenticating using ASP.NET Identity. First add a reference to the dll produced by building the MVC project. Then, simply add this code to your CustomUserNameValidator:
Of course, it's not quite that easy. You need to do two more things:
So there we go. We now have a secure web service. Only accessible via https and secured by credentials stored in an ASP.NET Identity database.
Add a project using the WCF Service Application template. You could also use the Asp.Net Web Application template, but I think you'll just end up with a bunch of extra references you don't need. Now take a look at the web.config file. You'll notice that using this template automatically adds the protocolMapping element which allows the service to be accessed using https. If you did not use this template, you would have had to add this for https support. You'll also notice that there are no endpoints or bindings configured. That's because WCF has default bindings and IIS can determine the endpoint by examining the .svc file.
I delete the sample service created for you.
Next, I find it convenient to host the web services I'm developing on my local IIS. Click on My Project, Web tab, and type http://{name of your computer}.{domain}.{com}/{something meaningful}. It is important to use the fully qualified name of your computer because the certificate you will soon use is named the same and WCF won't like it if they are not the same. Click Create Virtual Directory. Open up IIS Manager, and change the app pool of the site to one with an associated identity of your (hopefully powerful) account. This becomes important when you connect to a database for authentication.
Add your web service by using the WCF Service template. Build the solution, and right click the service. Choose View in Browser to make sure all is well so far.
Now it's time to enable SSL (https). Read here to do this. At this point, you'll want to change the Project URL to include https on the Web tab when you go to My Project. I think it's a good idea to go ahead and setup a binding configuration and an endpoint. As I said earlier, WCF can figure all this out because of default values. However, Visual Studio's Add Service Reference automatic proxy generator can get confused when you are setting up a client. You can either use the Microsoft Service Configuration Editor (Right click web.config and choose Edit WCF Configuration) tool or edit the web.config by hand. If you use the tool, just click the Service folder, and choose Create a New Service. This will walk you through a wizard to create the service node. After that there is a link to create the binding configuration. You end up with this inside the <system.serviceModel> element:
<bindings>
<basicHttpsBinding>
<binding name="BasicHttpsBindingConfig" />
</basicHttpsBinding>
</bindings>
<services>
<service name="AssemblyName.YourService">
<endpoint address="https://ComputerName.domain.whatever/IISSite/YourService.svc"
binding="basicHttpsBinding" bindingConfiguration="BasicHttpsBindingConfig"
contract="AssemblyName.IYourService" />
</service>
</services>
You also need to set the multipleSiteBindingEnabled to False when you are using https:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="false" />
At this point, I would create a little test app in a completely different solution and make sure you can call your new web service. A Windows Console app will work great. Just Add, Service Reference and specify the endpoint of the service. By adding the reference, Visual Studio will add the following to your app.config file:
<bindings>
<basicHttpBinding>
<binding name="BasicHttpsBinding_IYourService">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://ComputerName.domain.whatever/IISSite/YourService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpsBinding_IYourService"
contract="BenefitsServiceReference.IYourService" name="BasicHttpsBinding_IYourService" />
</client>
There are a couple of things to note here. Why does the client configuration set up for basicHttpBinding when the server configuration is set up for basicHttpsBinding? Essentially basicHttpBinding and basicHttpsBinding are exactly the same. The only difference is that basicHttpsBinding includes the <security mode="Transport" /> by default.
Now that the web service is up and running with SSL we want to configure it to require credentials in order to access it. You can use the ASP.NET Membership, but this has been superceded by ASP.NET Identity and WCF doesn't directly interface with ASP.NET Identity yet. So, we will create a custom user name and password validator which we will eventually use to authenticate with ASP.NET Identity. First, we need to create the validator class itself. Add references to the following:
- System.IdentityModel
- System.IdentityModel.Selectors
Imports System.IdentityModel.Selectors
Imports System.ServiceModel
Public Class CustomUserNameValidator
Inherits UserNamePasswordValidator
Public Overrides Sub Validate(userName As String, password As String)
If Not (userName = "Steve" AndAlso password = "topsecret") Then
Throw New FaultException("Unknown Username or Incorrect Password")
End If
End Sub
End Class
It's pretty obvious what is happening here. If your User Name and Password is not right, an exception gets thrown back to the client. Now we have to configure the web service to use this. First of all we need to edit our binding configuration. Remember that by default basicHttpsBinding uses Transport as the Security mode. We need to change this to TransportWithMessageCredential. We are protecting (SSL) the service at the transport (IIS) level and authenticating at the message (web service) level. Here is the binding configuration after we do that:
<bindings>
<basicHttpsBinding>
<binding name="BasicHttpsBindingConfig">
<security mode="TransportWithMessageCredential" />
</binding>
</basicHttpsBinding>
</bindings>
You also need to add a serviceCredentials behavior to your existing behavior element: <behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="AssemblyName.CustomUserNameValidator, AssemblyName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
As with most configuration changes, here you're definitely better off using the Service Configuration Editor tool. This is where we specify our custom class for validating the user name and password. Note the specific way that the class is specified along with the assembly that contains the class.The only configuration change in your client console app is changing the security mode to "TransportWithMessageCredential". When you run your console, you will notice that you'll get an exception if you fail to provide credentials. To specify the client's credentials, do the following:
YourServiceProxy.ClientCredentials.UserName.UserName = "Steve"
YourServiceProxy.ClientCredentials.UserName.Password = "topsecret"
If the client provides accurate credentials, then the service will return results; otherwise, an exception will be thrown.
With this infrastructure in place, you can change the code in the CustomUserNameValidator class to connect to any database you want to authenticate. I mentioned earlier my desire to access ASP.NET Identity to authenticate users for the web service. In a separate solution, I have set up an ASP.NET MVC Web Application and configured it with ASP.NET Identity authentication. I am going to use this web application to administer user accounts for my web service. I simply add this class to my MVC project:
Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.EntityFramework
Public Module SecurityManager
Public Function IsAuthenticated(userName As String, password As String) As Boolean
Dim MyUserStore As UserStore(Of IdentityUser) = New UserStore(Of IdentityUser)
Dim MyUserManager As UserManager(Of IdentityUser) = New UserManager(Of IdentityUser)(MyUserStore)
Dim MyUser As IdentityUser = MyUserManager.Find(userName, password)
If MyUser Is Nothing Then
Return False
Else
Return True
End If
End Function
End Module
Now, if I can call this function from my CustomUserNameValidator in my WCF project, I will be authenticating using ASP.NET Identity. First add a reference to the dll produced by building the MVC project. Then, simply add this code to your CustomUserNameValidator:
If Not SecurityManager.IsAuthenticated(userName, password) Then
Throw New FaultException("Unknown Username or Incorrect Password")
End If
Of course, it's not quite that easy. You need to do two more things:
- Use NuGet to install the same version of Entity Framework that your MVC project uses. This will setup your web.config with some special Entity Framework stuff that you will need.
- Copy the connectionStrings section out of the MVC web.config file and put it into the WCF web.config file. This connection string contains the database where the identity data is.
To get the name of the user who is accessing your service use this line:
System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity.Name
So there we go. We now have a secure web service. Only accessible via https and secured by credentials stored in an ASP.NET Identity database.
Wednesday, July 30, 2014
Protect a WCF service using SSL - Part 2
In a prior post, I described how to protect a WCF service using SSL. In the service .config file, you must do the following in order to expose the https endpoint:
The client configuration also needs some specific settings:
<configuration>
<system.serviceModel>
<protocolMapping>
<add scheme="https" binding="basicHttpsBinding" />
</protocolMapping>
</system.serviceModel>
</configuration>
The client configuration also needs some specific settings:
<system.serviceModel>
<bindings>
<basicHttpsBinding>
<binding name="BasicHttpsBinding_IDayOfTheWeekService"></binding>
</basicHttpsBinding>
</bindings>
<client>
<endpoint address="https://computername.yourdomain.net/WcfSecureServer/DayOfTheWeekService.svc"
binding="basicHttpsBinding" bindingConfiguration="BasicHttpsBinding_IDayOfTheWeekService"
contract="DayOfTheWeekReference.IDayOfTheWeekService" name="BasicHttpsBinding_IDayOfTheWeekService" />
</client>
</system.serviceModel>
Note that I am using basicHttpsBinding. This just came out with .NET 4.5. It's exactly the same as basicHttpBinding except the <security mode="Transport" /> is default so you don't have to specify this. Also, if you generate your own certificate, you will get an error message that says something like:Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'This is because WCF doesn't trust your self generated cert. However, if you generate a certificate with the name of the computer that it is issued to, and specify the fully qualified name of the computer in the URL like I did above, WCF will allow it.
Friday, July 18, 2014
WCF and ASP.NET Web API
When writing web services using .NET technology, you have a few options.
WCF: Tried and true, this is the successor to the original ASMX style web services. WCF has capabilities to create any type of service that can be accessed over a network, not just HTTP. WCF tends to be a bit configuration heavy requiring the use of a special tool in Visual Studio to set the myriad of options up. WCF generally uses the SOAP protocol. While it is possible to create a REST style web service using WCF, it is recommended by Microsoft to use Web API technology for REST style web services.
ASP.NET Web API: Web API allows you to create a REST style web service (no SOAP support) using an architecture that is very similar to MVC. You create a set of controllers and configure a routing system to create your operations and accept parameters. This tends to be simpler to setup and configure as it is very close to what you would do to configure an MVC web application.
There are also a couple of terms that need to be defined:
REST style web service: When you apply a REST architecture to a web service, this changes the way in which the client calls the operations of the web service. With a traditional web service, each call is represented by an HTTP GET verb with the URL that represents the name of the operation. With a REST style web service, the URL represents a set of operations that are differentiated by the use of 4 different HTTP verbs (GET, POST, PUT, and DELETE). I believe that the advantage of REST is that it is (allegedly) easier and makes more sense when called from client side code like JavaScript. Therefore, it can be consumed by a wider variety of devices. Also REST is necessary when implementing a data API using the OData protocol.
OData Protocol: OData is set of specifications for creating a data API. A data API is a service that exposes your database more directly over the web. Instead of abstract operations that perform CRUD operations on the callers behalf, the data API allows the consumer to perform the CRUD operations nearly directly. For example, each table could be represented as a URL and the user could query the table directly or even join together multiple tables. This has the effect of moving the business logic to the client.
Conclusions
Using OData to access a database over the internet sounds like it could be useful. And obviously you need to use REST in order to implement this. However, setting up an OData endpoint using Web API is anything but straight forward right now. In my opinion, this technology needs to mature a little longer for me to recommend using it.
If you're not going to use OData, then I just don't see the big advantage of using REST. It seems to me that it would be awkward to organize your operations into sets of four HTTP verbs. Another big disadvantage to using REST, is that there is no automatic proxy generation for consumers of REST style services. Web API does seem like it works well, but it requires a REST style architecture.
This is my bottom line: If you want to use a REST architecture, use Web API. If you want to use a SOAP architecture, then use WCF. At this time, I will continue to use SOAP and therefore WCF.
WCF: Tried and true, this is the successor to the original ASMX style web services. WCF has capabilities to create any type of service that can be accessed over a network, not just HTTP. WCF tends to be a bit configuration heavy requiring the use of a special tool in Visual Studio to set the myriad of options up. WCF generally uses the SOAP protocol. While it is possible to create a REST style web service using WCF, it is recommended by Microsoft to use Web API technology for REST style web services.
ASP.NET Web API: Web API allows you to create a REST style web service (no SOAP support) using an architecture that is very similar to MVC. You create a set of controllers and configure a routing system to create your operations and accept parameters. This tends to be simpler to setup and configure as it is very close to what you would do to configure an MVC web application.
There are also a couple of terms that need to be defined:
REST style web service: When you apply a REST architecture to a web service, this changes the way in which the client calls the operations of the web service. With a traditional web service, each call is represented by an HTTP GET verb with the URL that represents the name of the operation. With a REST style web service, the URL represents a set of operations that are differentiated by the use of 4 different HTTP verbs (GET, POST, PUT, and DELETE). I believe that the advantage of REST is that it is (allegedly) easier and makes more sense when called from client side code like JavaScript. Therefore, it can be consumed by a wider variety of devices. Also REST is necessary when implementing a data API using the OData protocol.
OData Protocol: OData is set of specifications for creating a data API. A data API is a service that exposes your database more directly over the web. Instead of abstract operations that perform CRUD operations on the callers behalf, the data API allows the consumer to perform the CRUD operations nearly directly. For example, each table could be represented as a URL and the user could query the table directly or even join together multiple tables. This has the effect of moving the business logic to the client.
Conclusions
Using OData to access a database over the internet sounds like it could be useful. And obviously you need to use REST in order to implement this. However, setting up an OData endpoint using Web API is anything but straight forward right now. In my opinion, this technology needs to mature a little longer for me to recommend using it.
If you're not going to use OData, then I just don't see the big advantage of using REST. It seems to me that it would be awkward to organize your operations into sets of four HTTP verbs. Another big disadvantage to using REST, is that there is no automatic proxy generation for consumers of REST style services. Web API does seem like it works well, but it requires a REST style architecture.
This is my bottom line: If you want to use a REST architecture, use Web API. If you want to use a SOAP architecture, then use WCF. At this time, I will continue to use SOAP and therefore WCF.
Wednesday, July 16, 2014
WCF Message Tracing
Message Tracing logs details on each communication in and out of the web service. When making significant configuration changes, it is almost always best to use the Microsoft Service Configuration Editor to edit the web.config. In many cases, it is just not practical to edit the web.config directly. To access the editor, in the Visual Studio Solution Explorer, right click the web.config of the service and click Edit WCF Configuration.
To configure message tracing, follow these steps in the Configuration Editor:
To configure message tracing, follow these steps in the Configuration Editor:
- In the Diagnostics folder, click Message Logging and set LogEntireMessage to True. Also set LogMessagesAtServiceLevel and/or LogMessagesAtTransportLevel to True, depending on your needs. Most of the time these messages will be identical. If you are encrypting at the message level, then the message logged at the transport level will be encrypted. If you are encrypting at the transport level, then both will be unencrypted and practically identical. The Transport Level is the host level. So this is logged when the host (IIS in most cases) redirects the message to the service. The Message Level is the actual service level.
- In the Diagnostics folder, right-click Sources and choose New Source.
- From the drop down, choose System.ServiceModel.MessageLogging
- Choose Verbose as the Trace Level.
- In the Diagnostics folder, right-click Listeners and choose New Listener.
- Type "MessageLog" as the Name.
- In the InitData field, use the browse button to set a path and filename of the logfile.
- TraceOutputOptions: Click the drop down and select which options you want. I just like to include the DateTime
- TypeName: Click the ellipses and choose System.Diagnostics.XmlWriterTraceListener
To view the log file, you want to use a tool called Microsoft Service Trace Viewer. I believe this is included with Visual Studio. To find it just do a search using the start button for Service Trace Viewer. Or try just right-clicking the log file and choosing Open.
An alternative way to accomplish this is:
UPDATE 7/22/2015: Curiously, I couldn't find it on a PC that I know has .NET installed. I found it here: C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools
UPDATE 7/24/2015: Added the alternative method of setting up tracing. I had some issues getting the standard method to work.
An alternative way to accomplish this is:
- Click the Diagnostics folder and then click the Enable Message Logging link.
- Set the Log Level by clicking the link next to the "Log Level:" label and only check the Service messages box.
- Click on the link of the listener, and choose a location and options as described above.
- You may also want to click the Message Logging node and choose Log Entire Message.
- You may also want to click the Source that was created and select Verbose.
UPDATE 7/22/2015: Curiously, I couldn't find it on a PC that I know has .NET installed. I found it here: C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools
UPDATE 7/24/2015: Added the alternative method of setting up tracing. I had some issues getting the standard method to work.
Tuesday, July 15, 2014
Protect a WCF service using SSL - Part 1
We have a scenario where we are exposing a WCF service over the internet that is hosted in IIS. We want the communication with the service to be protected using SSL. The encryption/decryption is handled completely by IIS, so there is minimal need to edit the configuration of the service itself (See Part 2). First you need a certificate. To create your own for testing:
- Open IIS (as administrator).
- Choose the top level node in the Connections pane.
- Click Server Certificates in the IIS section of the middle pane.
- Click Create Self Signed Certificate. The friendly name should match the name of the computer.
Now configure SSL:
- Right click Default Web Site and choose Edit Bindings.
- Click Add, choose HTTPS, and select the certificate you just created.
Finally, set up your service to require SSL:
- Click on the web application that hosts your service.
- In the middle pane, IIS section, double click on SSL Settings and check the "Require SSL" box.
Thursday, May 15, 2014
Windows Communication Foundation
I like to think of Windows Communication Foundation (WCF) as the portion of the .NET Framework that concerns the hosting and consumption of services. A "service" can be thought of as a server program that exposes a set of operations that is accessible to client programs over a network. A service that provides its operations over the internet can be thought of as a "web service".
A WCF service consists of:
If you wanted to host this in IIS, just publish the files to a site. If you wanted to host it in a Windows Form app, do something like this:
Consuming a web service is pretty straightforward. Just add a Service Reference in Visual Studio and a proxy class will be generated for you to call the service operations with:
The client configuration is very similar to the server configuration. Essentially to connect to a service, the client must use the same configuration.
A WCF service consists of:
- An interface that defines both the service and its operations.
- A class that provides the implementation of the operations.
To create a WCF service in Visual Studio:
- Add an ASP.NET Web Application project (Use the "Empty" template) or a WCF Service Application project.
- Then add a WCF Service item.
The WCF service must be hosted. The obvious choice is to host it in IIS. However, the service can be hosted in any .NET Windows application as well.
How does the host know to listen for requests? Setting in the web.config file. The following configuration indicates to the .NET framework that a WCF service is running:
<system.serviceModel>
<services>
<service name="ProjectName.DayOfTheWeekService">
<endpoint address="http://localhost:8000/DayOfTheWeekService.svc" binding="basicHttpBinding"
name="basicHttpBinding_IDayOfTheWeekService"
contract="ProjectName.IDayOfTheWeekService" />
</service>
</services>
</system.serviceModel>
If you wanted to host this in IIS, just publish the files to a site. If you wanted to host it in a Windows Form app, do something like this:
Private MyServiceHost As System.ServiceModel.ServiceHost
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
MyServiceHost = New System.ServiceModel.ServiceHost(GetType(ProjectName.DayOfTheWeekService))
MyServiceHost.Open()
End Sub
Dim DayOfWeekProxy As New DayOfTheWeekServiceReference.DayOfTheWeekServiceClient("BasicHttpBinding_IDayOfTheWeekService")
Console.WriteLine(DayOfWeekProxy.GetDayOfTheWeek(InputDate))
The client configuration is very similar to the server configuration. Essentially to connect to a service, the client must use the same configuration.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IDayOfTheWeekService" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://SERVERCOMPUTER:8000/DayOfTheWeekService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IDayOfTheWeekService"
contract="DayOfTheWeekServiceReference.IDayOfTheWeekService"
name="BasicHttpBinding_IDayOfTheWeekService" />
</client>
</system.serviceModel>
Friday, January 10, 2014
AJAX Enabled WCF Service with Windows Authentication
Let's say you have a WCF Service that you are calling from jQuery code. Everything works fine until you set up Windows Authentication. Then it blows up. The problem is that the binding has to be configured to work with Windows Authentication. Let's first look at the endpoint in the web.config file. Take a look at the binding attribute. It should say "webHttpBinding". WebHttpBinding means that your web service responds to HTTP requests. This is necessary because this is the only way jQuery can call your service. In fact, I believe Visual Studio sets it in this way when you added the AJAX Enabled WCF Service rather than a plain old WCF Service. However there is no configuration for the WebHttpBinding itself, which means it just uses its default configuration which I believe is configured to use anonymous authentication. So we need to configure the WebHttpBinding to use Windows Authentication by putting the following nodes inside the system.servicemodel node:
<bindings>
<webHttpBinding>
<binding>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"></transport>
</security>
</binding>
</webHttpBinding>
</bindings>
Subscribe to:
Posts (Atom)