
Windows Identity Foundation - for ASP.NET MVC Projects

In this article, I would like to talk about how you can use the Windows Identity Foundation in your ASP.NET MVC projects, and write your Identity Server, on the WIF platform. Firstly, because there is enough general information on the Internet, but when it comes to specifics, problems arise. Since ideology and special cases can still be found, but when it comes to specifics, you have to collect bit by bit. And secondly, what Microsoft now offers, using add-ons over Visual Studio, is not entirely suitable, I would even say it is not at all suitable for developing solutions, it is more difficult than a home page or a business card site. Among other things, I don’t really like when the mythical setup wizard did what with the solution, and said that “it should work, it seems.”
As an example, we will create and configure the most primitive client, which will log in through the most primitive authentication server (Identity Server) running on the WS-Federation protocol.
In order to decide in what cases it makes sense to “fence a garden” with your authorization server, just look at how it works. So, we want to provide the client with several services, for example several sites, for example with WCF services, and let's say the REST API. Agree that it will be quite uncomfortable for the user to log in separately on each of our sites, when switching from one to another. Here is a fairly simple idea, which is that the user, when authorizing on one of the services (resources), is given a certain Token. And in the future, another (or the same) service (resource) already trusts an authorized user, based on the existence of the same token at the client, and so on ...
Just for clarity of understanding, I will give the comparison you like (unfortunately I found links to). For example, a person receives a passport, after some verification procedure, and then, providing his passport, they trust a person based on his passport. In this comparison, the person’s passport is the client’s token.
We can immediately conclude that it is often pointless to issue a “passport” if it will be checked in only one place.
Of course, the token, like the passport, has a lifetime, and in the same way, the token can be recreated, based on the outdated one. And by analogy, like a passport, the token can be different, i.e. be almost anything, or it’s the request header, or base64 string, such as JWT Token ( JSON Web Token) And in fact, the token itself contains information about itself (the time it was last created, the public key of the certificate, etc.), as well as a list of hallmarks containing information about the client. To describe the token, we will use the SAML ( Security Assertion Markup Language ) language .
Another important concept is the Claims. Brands are part of our token, and carry information about the client as a whole. In fact, this is a dictionary consisting of a Key / Value pair, in which Key is a namespace describing the type of the Value field, and the Value field itself is a simple string. In .Net, this is represented by a typed list:
var claimsList = new List
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "Tester")
};
There are a number of registered namespaces that are more than enough to describe a client profile, from a list of roles to an avatar. If desired, we can create our own namespaces, the only thing we need to remember is that the Claim object itself must be serialized.
There are perhaps two main conclusions from this, the first is that for simply authorizing the user on the site, using Identity Server is simply unjustified, and the second is that since we are going to transfer the token between different resources, we will need transport security, first of all. And so let's get started, and let's start by setting up the transport, because if we don’t have trusted transport, it simply won’t be able to work, and for this, first of all, we need a certificate.
Certificate Creation
From around this point, many developers have questions about creating a certificate, setting up HTTPS on a server, and so on. For work and debugging, we will need IIS installed locally, a simple ASP.NET MVC application, which will be our client site, and a trusted certificate. We need not just a certificate, but a certificate issued for any domain name, buying it for testing purposes is not economically profitable, so we will do it ourselves.
For example, the domain name that we will use for testing purposes will be identity.com . First, use the makecert utility to create the certificate .
makecert -r -n "CN=*.identity.com" -cy authority -b 01/01/2000 -e 01/01/2099 -a sha1 -sr localMachine -sky Exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv identity.com.pvk identity.com.cer
As a result of execution, we get two files identity.com.pvk and identity.com.cer. Everything is very clear, and there is more than enough information on the makecert utility . The only point I would like to dwell on in more detail is on CN for which we issue a certificate. If you simply issue a certificate on identity.com , then, in the future, it will be inconvenient for us to locally model the situation with distributed resources, but the use of * greatly simplifies our task, i.e. * .identity.com . Using * , gives us the opportunity to locally create an arbitrary number of domains of the form "any name" .identity.com .
Next, to verify the publisher’s certificate, we ’ll use the cert2spc utility .
cert2spc identity.com.cer identity.com.spc
And as a result, we get the identity.com.spc file , which we will need for the pvk2pfx utility . With the help of which we will generate the pfx file we need .
pvk2pfx -pvk identity.com.pvk -spc identity.com.spc -pfx identity.com.pfx -pi "qwerty"
As a result, we got an identity.com.pfx file containing the private key, with the qwerty password. It remains to register it with IIS in the system.
HTTPS setup
In order for our IIS server to start working with our certificate, firstly, we need to import our pfx file into the Trusted Root Certification Authorities zone through the MMC snap-in and import in the IIS server itself, in the Server Certificates section.
Now everything is ready to set up our client site. To do this, create a new site in IIS, with the name client.identity (in other ways, no matter what), the main thing is that the App Pool of our site works under .Net 4.0 (this is if the site is compiled under .Net 4.0, 4.5). And point the Physical path to the directory of our client site.

Next, configure our HTTPS in the Binding section. After selecting https in the type field, we need to select our generated certificate, and only after that the Host name field becomes available for editing. If we generated the certificate with " * ", then we can specify almost any name of our site, most importantly it would end with identity.com , i.e. The name of our test domain. In the future, we can change our bindings in the Bindings section of our site. All that’s left is the last “touch”, so change the hosts file along the path (c: \ Windows \ System32 \ drivers \ etc \) and add a line with the name of the binding for our site:
127.0.0.1 client.identity.com
Everything, you can check the operation of local https, just at: client.identity.com .

As a result, we should see our web application, and in the address bar, what our certificate is trusted, i.e. no browser warnings.
If you download IdentityTrainingKitVS2010 from the Microsoft website, you can make your life a little easier by running SetupCertificates.cmd, for example, along the path IdentityTrainingKitVS2010 \ Labs \ MembershipAndFederation \ Source \ Setup \ Scripts \ SetupCertificates.cmd
This script will do almost the same thing, only for a ready-made certificate from the localhost.pfx examples (it has the password xyz). Accordingly, accessing the site (for example, Default Web Site) will work through localhost, and all your applications that must work through https must be created neither as a Web Site, but as a Web Application, under a localhost site.
Client Application Configuration
Now we need to make some configuration of our client application. First, in the project settings, set the address of our site for the Custom Web Server field.

This will give Visual Studio the ability, at startup, to automatically do an Attach to Process to the w3wp.exe process (IIS site process).
Now, we need to deal with the references of our site, and add two assemblies from the GAC, System.IdentityModel.dll and System.IdentityModel.Services.dll . And also remove the superfluous, what will stop us - these are the NUget DotNetOpenAuth packages, we will not need them, they will only interfere, and for this, you need to remove the Microsoft.AspNet.WebPages.OAuth package. If, for some reason, you do not want to touch them, then as an optional option, this is the registration setting in web.config.
And the last step in configuring the client application is to configure web.config itself. First, in the sysytem.web section, set the authentication method to none.
Next, register our section for the Identity Model in the configSections section:
Add and configure these sections, first system.identityModel:
First, in the audienceUris section, add the address of our client site, then add an entry to the trustedIssuers section, where thumbprint is the Thumbprint value from our generated identity.crt file , or another transport certificate that the server will use.

Only without spaces. And the name field is the address of our poor server, for example, in our case server.identity.com + / issue / wsfed. Using wsfed is not necessary, especially in our case of a poor server - it can be anything. Just wsfed is short for WS-Federation .
Next, add the system.identityModel.services section:
Everything is quite simple here, iisuer is our server from the section above, and reply is the address where, in the future, reply to the server.
It remains to register the modules in the system.webServer section.
That's it, you can check the work of our client, just try using View to call any method or controller method marked with the Authorize attribute.
After launching the application (by F5), and the user attempts to call our method, we will see a GET request at the address of our poor server:
https://server.identity.com/issue/wsfed?wa=wsignin1.0&wtrealm=https%3a%2f%2fclient.identity.com%2f&wctx=rm%3d0%26id%3dpassive%26ru%3d%252fAccount&wct=2014-09-23T13%3a36%3a21Z&wreply=https%3a%2f%2fclient.identity.com%2f
This is a request for authorization, from the client to the server, at server.identity.com/issue/wsfed .
The above described is quite enough for minimal configuration of the client application working with WIF Identity Server, even if you use a third-party server, the main thing is that the server supports WS-Federation .
Server creation
Before starting to create an Identity server, you should definitely mention such a thing as STS ( Security Token Service ). In fact, this is a service, and it does not matter what language and platform it is written in. And our ASP.NET MVC Identity Server is essentially a UI shell. To create our STS, since we still use .Net, it will be convenient for us to use the tools that are already on the platform.
Our Identity Server, like the client, is essentially an ASP.NET MVC application for which HTTPS must also be configured, and, in our case, we will assign server.identity.com binders to it , using the same one generated by us certificate.
Create a SignIn View for the SignIn method of the Account controller, and add the following entry to web.config:
And add to the routes, an entry for the controller method, which will be executed when accessing the path issue / wsfed.
routes.MapRoute("wsfederation",
"issue/wsfed",
new { controller = "WSFederation", action = "issue" });
This is the path our client addresses to <> / issue / wsfed. If we control the controller with the Authorize attribute, then the client application, when trying to log in, will first get to the SignIn method of the Account controller, which in turn will return the View, with the login form of our server.
Next, the user enters the data necessary for entry (for example, login password), and gets to the Issue method. Please note that the RequestString does not change, but remains the same as the client application accessed.
In order for our server to understand what exactly the client wants from it, we will analyze the request arguments:
public ActionResult Issue()
{
WSFederationMessage message = WSFederationMessage.CreateFromUri(HttpContext.Request.Url);
// Sign in
var signinMessage = message as SignInRequestMessage;
if (signinMessage != null)
{
return ProcessWSFederationSignIn(signinMessage);
}
// Sign out
var signoutMessage = message as SignOutRequestMessage;
if (signoutMessage != null)
{
return ProcessWSFederationSignOut(signoutMessage);
}
return View("Error");
}
Accordingly, the WSFederationMessage.CreateFromUri method returns the instance of the descendants of the WSFederationMessage abstract class. Next, we perform actions, either logging in or logging out.
When logging in using the WS-Federation protocol, we execute the static method:
FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest
This method, based on the received instance of the SignInRequestMessage class and the list of stamps (Claims), will form a certain RequestSecurityToken object, which in essence is our client token, and will give it to the GetScope method of our STS service. To create our STS service, we will inherit from the abstract SecurityTokenService class:
public class TokenService : SecurityTokenService
and redraw the GetScope method:
protected override Scope GetScope(ClaimsPrincipal principal, RequestSecurityToken request)
it is in this method that the analysis or filling of the RequestSecurityToken object will occur. the formation itself, and verification of the client token. I simply don’t see the point of describing the whole test, since it is easiest to go through the method by debug, since there is nothing trivial in the method.
In principle, the above described in its very minimum is enough to get a primitive Identity Server through which the client can log in.
If it will be interesting how this all works, I “glued” the simplified client and server, on with github .
This is just a lite version of the server https://github.com/thinktecture/Thinktecture.IdentityServer.v2, collected so far, at the moment, for the purposes of demonstration and nothing more, and of course does not withstand any criticism.
Finally
What I would like, I personally get as a result, is a full-fledged Identity Server, in which all the work with the user profile is taken out i.e. login, registration, social networks, viewing your profile, etc. Accordingly, with the proper level of security. But in fact, so that the server itself could be connected to the resource being developed, and each time not to spend time on the authorization system. And of course, I would like to integrate the server’s work with WCF and REST services, with the distribution of access to methods by client roles. But this is only in the plans so far.
useful links
What is Windows Identity Foundation: http://msdn.microsoft.com/en-us/library/ee748475.aspx
Sources of several Identity servers, and useful libraries: https://github.com/thinktecture
A good report on Claims-based authorization : https://www.youtube.com/watch?v=WHSDIiwQlS8
More examples: http://claimsid.codeplex.com
And codeproject: http://www.codeproject.com/Articles/504399/Understanding-Windows-Identity -Foundation-WIF