A pleasant walk through computing

Comment for me? Send an email. I might even update the post!

Catching Up on Coding - Claims-Based Identity Part 1

the-magician

I’ve been a software developer for twenty years, but am behind in some skills. This series chronicles my self-improvement.

Claims-based identity isn't hard to understand, but it's been challenging to learn and implement in a legacy environment. In part one, we'll look at the concepts. In future posts, I'll have code samples using current (at the moment) frameworks.

Like Tony Blake producing a flock of doves, we'll learn some security sleight-of-hand.

Previously on CUOC: TDD Part 3
Next Episode: Claims-Based Identity Part 2

 

 

 

Claims-Based Identity Basics

What We're Used To - Passwords

The convention in authentication for decades--for millennia, in a sense--has been this exchange:

"Let me in."
"What's the password?"
"Swordfish."

You're allowed in because you know something only a few people should: the password. Once you're in, the exchange continues.

"I want to see the boss."
"Oh yeah? Who's asking?"
"I carry packages for Don Crosetti."
"A courier? Delivery boys don't see the boss, chum."

What you get to do is defined by your role. A lieutenant can see the boss. A courier can't.

This is very much how people understand software security to work. You enter a username and password on a web site, let's say a Content Management System. Once in the site, if you're a Viewer, you can look at all the pages but not edit anything. An Blogger can create posts for her own blogs, but not anyone else's. A Site Editor can modify anyone's posts.

This system works well enough for many situations (in both software and life). But there's another common approach: claims.

What We're Also Used To - Claims

It's Friday night and you go out to a dance club. To get in, you have to show your driver's license. The guy at the door looks it over, then waves you in. What was he checking? Two things.

  • Were you over 21?
  • Did the license look valid for the issuing state?

The first item is a claim. You're claiming to be over twenty-one. You have a driver's license issued by a recognized identity validator: the Bureau of Motor Vehicles. The doorman didn't verify your identity. He didn't ask for a social security card or birth certificate or fingerprints. He trusts that the BMV did all that. The most he did was check that the license was the expected color and had the right hologram.

As long as he trusted the license, he trusted the claims on the license: your birth date, name, height, weight, etc.

Roles can be claims: maybe at this particular club you have a VIP card that lets you into the upper floor. However, claims are not necessarily roles. Claims can allow more fine-grained and nuanced authorization.

There are two important aspects of this example:

  1. The bar didn't ask for a password. They trusted a third party's identity authentication.
  2. The bar authorized entry based on a claim, not a role.

With those two real-world examples in mind, we're ready to look at claims-based identity in software.

What's an Identity Provider (Security Token Service)?

In the bar example, the identity provider is the BMV.

An STS takes care of authenticating identity, if needed, and returns a token than contains claims about the identity. An STS could be local or remote. Examples include:

  • A local database containing user and password hashes.
  • A local Active Directory
  • Azure Active Directory
  • Google

What's a Token?

In the bar example, the token is the driver's license.

A token is just data about the identity and issuer. It could be an XML document, for instance. Generally speaking, it can have:

  • A unique ID
  • An issuer certificate id
  • A collection of claims

What's a Claim?

In the bar example, the claim is the age.

A claim is a fact about the identity (i.e., the user). A claim is trusted if the identity provider is trusted. Claims could include:

  • Name
  • Email
  • Date of Birth
  • Country

What's Federated Identity?

The word "federation" means "union by agreement."

A body formed by a number of nations, states, societies, unions, etc., each retaining control of its own internal affairs. [Dictionary.com]

When it comes to identity, otherwise unrelated organizations can trust each other's STSs, allowing a user to log in with one set of credentials that are accepted elsewhere. This forms the basis of single sign-on (SSO).

A simple example of real-world federation would be a background check company that wants to give its customers access to their reports without storing them as users. The company would:

  1. Set up a federated STS.
  2. Establish a trust with the customer's Active Directory (for example).
  3. The customer determines which AD claims to provide.

A customer's user would log into their computer using their corporate credentials. When she opened the background check web site, the site would check if there was an authenticate token, and find the user's credentials in a cookie (perhaps). The web site would contact the STS, which would contact the customer's AD and verify the token's validity and return claims. The site would then authorize access if the claims allowed.

At no time did the user retype credentials.

Simplest Steps of Getting Authenticated Claims - Code Neutral

Here's some pseudo-code (C# style) showing how an application might get and use a claims token. Note: These are not real .Net classes!

A simple user.

class User
{
    public string Email { get; set; }
}

The claims class. (The real .Net class is just about this simple.)

class Claim
{
    public string Type { get; set; }
    public string Value { get; set; }
}

A token/identity class, which contains a collection of claims.

class IdentityToken
{
    public string TokenId { get; set; }
    public IEnumerable<Claim> Claims { get; set; }
}

A helper class that checks authentication status.

static class AuthenticationManager
{
    public static bool IsAuthenticated(User user)
    {
        bool authState = false;
        //check a cookie, or database, or session state, etc.
        return authState;
    }

A provider service to handle getting the token. This is the complex part if you wrote it yourself, since it needs to properly manage the network connection, protocol, certificates, trust, etc.

class Provider
{
    public string SignOnUri { get; set; }
    public string ProviderId { get; set; }
    public string ApplicationId { get; set;}
    public IEnumerable<Claim> GetClaims(string email)
    {
        //Set up network connection, such as to a remote Azure AD server
        var connection = GetConnected();
        //The provider knows which applications are allowed.
        if (!connection.IsValidApp(applicationId)) { throw new Exception("Invalid app");}
        //The user might need to sign in via a remote page NOT on our site.     
        var response = connection.Authenticate(email, SignOnUri);
        if (response.Authenticated)
        {
            //The framework-specific provider takes care of the messy work of parsing the claims.
            var claims = new List<Claim>();
            foreach (string clm in response.TokenClaimCollection)
            {
                claims.Add(new Claim() { Name = clm.Identifier, Value = clm.ValueUri }
            }
            var token = new Token() { TokenId = response.TokenGuid, Claims = claims};
            //store the token info somewhere, like in a cookie or database
            Session.Store(token);
            return token.Claims;
        }
        else
        {
            throw new Exception("Not a valid user.");
        }
    }
}

And here's how authentication might look, using that provider.

void Main()
{
    //The user who wants access.
    User user = new User() { Email = "jane.doe@example.com" };
    //We have a class that checks if the user's authenticated.
    if (!AuthenticationManager.IsAuthenticated(user))
    {
        //Set up a service class that does the work of communicating
        //with the STS. Behind the scenes, it could use WS-Federation, OpenId, SAML, etc.
        var sts = new Provider()
        {
            SignOnUri = "https://login.otherplace.com",
            ProviderId = "ef26632f-9af6-4ea0-8fcc-a8cee466cb6d",
            ApplicationId = "mysite.mydomain.com/isunique"
        };
        //Get the claims collection in our format. If we get this without error,
        //the user was authenticated. We don't care how! This is where the user
        //might be redirected to a login page.
        var claims = sts.GetToken(user.Email).Claims;
        //Check if the user can do something
        if (claims.First(c => c.Name == "Company").Value == "XyloTech")
        {
            //Show their reports
        }
    }
}

In the above method, we

  1. Found the user wasn't authenticated
  2. Instantiated our identity provider. This included a way to verify to the provider who we were, and vice versa. (Note: technically, the application trusts the STS, but the STS doesn't trust the application.)
  3. Asked the provider to authenticate the user. We may not know or care how this happens.
  4. Got back claims. Our provider class did the messy work of converting it to our class collection.
  5. Checked for a specific claim and took action.

In the next post, we'll look at some of the various namespaces Microsoft uses for claims-based identity.

--------------------

Tony Blake's Corvette, license plate: "SPIRIT"

image

Creating Forms Authentication Membership Tables in LocalDB–Right and Wrong Ways

The Problem

There’s a rare chance these days that you’ll need to manually create the authentication tables used by the old ASP.NET WebForms Forms Authentication Membership Provider. The tool to do this is aspnet_regsql.exe , found in the .Net Framework folders.

Even rarer will be using the tool to create the tables in LocalDB. You don’t really have to. The database will be automatically created, even in Visual Studio 2015, if you do the steps at the end of the article.

The Wrong Way

But let’s say you do use the tools, as in this command line for Windows 64-bit.

%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regsql.exe -E -S (LocalDB)\v11.0 -d aspnetdb -A mr

Assuming you have the SQL 2012 LocalDB installed, running the above will create a file in your user profile named aspnet.mdf, for example C:\Users\charles\aspnetdb.mdf.

Here’s the rub. If you delete the file, then run the command again, you’ll get this error.

An error occurred during the execution of the SQL file 'InstallCommon.sql'. The SQL error number is 5120 and the SqlException message is: Unable to open the physical file "C:\Users\charles\aspnetdb.mdf".

This happens because LocalDB is still storing an entry in sys.databases (in the master database) for the database you just created. In my opinion, it shouldn’t, but what do I know?

Cleaning Up from the Wrong Way

To remove this entry, you need to delete the entry by connecting to LocalDB like a server. These instructions show using Visual Studio’s database tools, but the steps are similar for SQL Management Studio.

  1. Open Visual Studio
  2. You don’t need to create a project. Just Tools > Connect to Database.
    image
  3. Choose Data source = Microsoft SQL Server
  4. Enter Server name = (LocalDB)\v11.0
  5. Drop down the list of databases. You should at least see master, etc.
    image
  6. You will not see aspnetdb. Choose the master database.
  7. Open the Server Explorer, right-click the connection and choose New Query.
    image
  8. In the query window, enter and run:
    select * from sysdatabases
  9. You’ll see aspnetdb listed.
    image
  10. Run this command:
    drop database aspnetdb
  11. You’ll get an error message:
    Unable to open the physical file "C:\Users\charles\aspnetdb.mdf". Operating system error 2: "2(The system cannot find the file specified.)".
    File activation failure. The physical file name "C:\Users\charles\aspnetdb_log.ldf" may be incorrect.
  12. But that’s OK. If  you select from sysdatabases again, you’ll see the aspnetdb has been removed.
    image

Creating the Database the Right (Easy) Way

I didn’t find documentation saying I could do this, and tried it on a whim. Basically, if you configure your project to use a LocalDB as your membership provider, the first time the app tries to use it the database will be created with the correct tables. At least, this is true in Visual Studio 2015.

Add Old-Style Forms Authentication

Web.Config

Open web.config. Under <configuration>, add this connection string.

  <connectionStrings>
    <!-- ASP.NET MembershipProvider-->
    <add name="ASPNETDB" connectionString="Data Source=(LocalDB)\v11.0; Integrated Security=true;AttachDbFileName=|DataDirectory|ASPNETDB.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>

In <system.web>, add this.

     <!--ASP.NET MembershipProvider, must be None for other providers such as OWIN-->
    <authentication mode="Forms">
      <forms loginUrl="~/" timeout="2880" />
    </authentication>
    <membership defaultProvider="AspNetSqlMembershipProvider">
      <providers>
        <clear />
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ASPNETDB" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    <!--Set enabled to false for OWIN-->
    <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
      <providers>
        <clear />
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ASPNETDB" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>

Create the Membership Database

In Default.aspx.cs

Add these using statements.

//MembershipProvider (forms authentication)
using System.Security.Principal;
using System.Web.Security;

In some part of your app’s startup, add this code, which we'll run once to create the database, add a user, and add a role. For example, this could go in a Web Form’s Page_Load, in a Controller action, or in a console app’s Main. You could, of course, not create a role, or delete the user afterward, etc.

            string email = "charles@aspnetmembership.com";
            string role = "Dev";

            Membership.CreateUser(email, "BadPassword!", email);
            if (!Roles.RoleExists(role))
            {
                Roles.CreateRole(role);
            }
            Roles.AddUserToRole(email, role);

Run the project. The aspnetdb.mdf database file will be created in App_Data, with the needed tables.

Delete the code added above.

Windows 10 Complaint #1: Auto Update State Loss

I wouldn’t care that Windows 10 restarts my computer after updates, if it would capture and restore my entire desktop state. That’s perfectly possible; I have hibernation enabled.

But I don’t like discovering my computer no longer has the twenty developer-related browser tabs open that took me two hours to find.

It feels like this.

Photo: Stephen Edmonds


EDIT 2016-10-12

I say “it’s perfectly possible,” but could be ignorant, know-it-all baloney on my part. I don’t know exactly how hibernate works. I suspect that it depends on several Windows OS files not being fully closed. In other words.

  1. The entire PC memory, including running processes, is written to disk.
  2. The PC can be powered off.
  3. Normally, on restart, there have been no changes to the Windows OS, so the processes can be resumed.
  4. But what if there are changes? What if an entire dll or exe has been removed? Then, trying to start that process could fail.

But….but!…can’t the application state be stored? Maybe not. These things often seem simple until you know what the hell you’re talking about.