May 2012 - Posts - Jon Galloway

May 2012 - Posts

Why I Taught My Daughter To Code (A Little)

Literacy

Imagine a world in which very few people knew how to read or write. You kept to certain parts of town because you couldn't read a map or a street sign. When you needed to sign a contract, you just asked what it said and had to take it on faith. A lot of your experience was based on legend and rumor. Books, and the information in them, were mystical. Sometimes you suspected they were being used against you, but you never really knew.

There was definitely plenty of work around, but some high end jobs weren't remotely possible - not just clerical work, but professions that required a lot of information management, like the medical and legal professions.

Please don't tell me not to learn to code

Over this past year, there have been commentary back and forth on whether everyone should learn to code. A few free, interactive sites like Codecademy popped up which made it easy to start learning some basic coding. I was a big fan - I helped my eleven year old daughter go through it, and we both agreed it was great. More on that later.

Then Codecademy launched Code Year - a grand plan to teach hundreds of thousands of people to code. It was a big hit - even NYC's mayor signed up.

But no good deed goes unpunished, and there's been some snarky criticism from - you guessed it - professional coders. The latest if Jeff Atwood's post, Please Don't Learn To Code.

Hogwash. Learning some basic coding is an excellent investment of anyone's time.

Easy on the Straw Men

It's "please don't hear what I'm not saying" time.

I don't think that the general populace needs to be proficient at writing code. It would be silly to argue Mayor Bloomberg shouldn't be slinging Javascript on the job (as Jeff does). That's not at all the goal. Nobody's saying that everyone should become a programmer.

What I - and others - are saying is that learning some basic coding skills is an excellent investment of anyone's time. I'll make two arguments:

  • Understanding computers (including all the computing devices and products that surround us) is of huge and growing benefit to everyone.
  • Basic programming is an excellent way to gain some of that understanding.

Computer Literacy as a Basic Life Skill - Right Now

Right now, pretty much every part of life is in some way affected by computers - most pretty heavily. Shopping, socializing, medicine, education, law, entertainment and more are all at the very least affected by computers and the internet. My three daughters had a use for basic computer skills long before math would do them any good.

This only gets more intense with increasing age. Teenagers can greatly profit from computer expertise and tech savvy. It's hard to imagine a college program (at least one with any practical application) that didn't involve a hefty amount of computer usage - from basic internet research and word processing on up to simulation and research.

Most careers (in the industrial world) involve some amount of information management, and workers in these professions who have some computer skill can often be a lot more effective. That includes areas where even a decade ago people might not have expected to see computers - agriculture, construction, family-run restaurants.

I've talked with countless "civilians" who have profited from computer skills over the years - a real estate appraiser who figured out how to automate using Excel and worked circles around his colleagues, film makers and musicians who figured out how to record, market and distribute on the internet, non-techies who sell things online, a school facilities manager who figured out how to cut costs significantly using computer security and automation.

And many of those people had new doors and careers open to them due to their new-found computer skills. When the real estate market slowed down, the appraiser jumped right into a technology job. Heck, learning to code on my own - starting with my grade school's Apple II - gave me the option to jump into a computer career after I got out of the Navy (in a technical but not computer related field).

Computer Literacy as an Absolute Essential in the Very Near Future

"The future is already here — it's just not very evenly distributed."

- William Gibson

It should be obvious to anyone paying attention that computers have become a huge part of our daily lives, and that trend is rapidly accelerating.

I'm reading Physics of the Future (Michio Kaku). It's a fascinating book in which Michio points to obvious trends and extrapolates where things are going in the near future. In many cases, even a very conservative estimate - or even a look around - shows that every part of life will soon involve interacting with computers and the internet. He points out that global powers during World War II would have killed for the computational power in a simple musical birthday card. Now, we just throw them away. Average mobile phones rival supercomputers from the not-too-distant past, and they're connected to a growing internet and GPS satellites. In the next few decades, computers will continue to shrink and become integrated into everything we interact with.

The quality of our everyday lives will be greatly affected by how we interact with computers. How should that interaction take place?

Magical! Or, Should We See Computers as Products or Tools?

Technology companies work hard to package technology so that it can be used naturally, so consumers won't have to think or fiddle with details. This is a good thing. Anyone working in technology should be focused, consumed with the challenge of exposing the possible as the natural. We should be doing this not just because it's a worthy goal, but because it makes good business sense - friendly products sell.

But it is not in the average consumer's interest for them to blindly buy into this magical experience.

Think about the historical progress in how informed consumers approach medicine. In ancient times, mystics sold magic and consumers had no real insight into what they were buying. Over the centuries, the public has gained a much more informed view of science and medicine, and this is of huge benefit to them as consumers. We expect to see evidence, we read about product recalls, and the market for doctors who sell amulets and snake oils is pretty small. This applies to other areas of life, as well. While I could get around in a car I thought was powered by tiny horses, I'd be unlikely to make wise decisions regarding the purchase and repairs to said car. An informed approach to modern life helps.

2012-05-16 15h06_11

Now think about the consumer approach to computer products.

Think about how computers and technology are shown in movies and TV.

Think about how helpless your relatives and neighbors feel when they're buying techy products, or when something doesn't work.

Think about a friend or relative who paid too much for a website that never worked right.

Ah, but you may be thinking about a favorite company who sells you magical products right now, or runs some of your favorite websites, or sells you apps or movies. Well marketed brands that have built reputations for quality and ease of use have helped uninformed consumers navigate the technology landscape, but keep in mind that these companies have divided interests. It's their job. They want to sell you things, and their stockholders demand that they bring in as much profit as they can from those sales. Mindless and uninformed brand loyalty is not only expensive, it's by nature counter to your interests. It's no way to live - and thrive - in a digital world.

Computer Literacy as a Leadership Requirement

The basic level of technical understanding that we expect of our leaders must grow with time in order for them to govern. We expect them to understand our legal system, but we also expect they should know that medicines are chemicals which influence the biological processes in our bodies, that electricity powers lightbulbs and is transmitted over an electrical power grid, that mobile phones have radios in them, etc. I'd be scared to hear that our leadership didn't have a very basic understanding of our modern world. They write and enforce laws and policies that greatly impact our lives, so they need to have a clue about the world we live in.

In the same way, leaders who see computers as magic boxes, coders as wizards, and computer code as incantations can't effectively govern. They can't set policy, they can't have informed legal opinions, they have no framework for evaluating whether information being fed to them makes any sense (or even knowing if they should ask).

Leadership requires understanding, and more, it requires - well, a sense of direction. The future is going to include more and more technology, and leaders who don't understand the present or even have a rough idea of the road ahead are a menace.

Computer Literacy as an Educational Oversight

I've been describing why non-techies can benefit from some computer literacy. Unfortunately, they won't get it in from our schools.

Computer literacy is undoubtedly of more use to most people than the foreign languages most high schools require (I want those 4 years I spent on high school French back). I have rich conversations with people worldwide thanks to the internet and automated translations, not any time I've spent studying languages.

Though it may offend, computer skills are of more use to the huge majority than mathematics including and beyond first year algebra. Most people will rarely if ever use algebra; everyone uses computers.

Consider this: in the off chance that the average farmer / dentist / lawyer / car mechanic / stay at home parent / author / teacher / etc. needs to solve an algebra problem, they can punch it into the search engine and get the answer: 3x + 7y = 4y - 35. Or, more likely, just ask their phone.

The same goes for so many things we devote educational time to. Does the average high school graduate remember anything important about titration or Augustus or sentence diagram? Titian or Taft? Or even remember how WWI started? Does it matter when they can just look it up on Wikipedia?

I'm all in favor a balanced education. I got a B.S. Physics (with honors) from Annapolis and went through the US Navy Nuclear Power program (a very rigorous engineering program) following that. My parents were both school teachers. My wife and I homeschool our daughters and put a very high priority on their education. I see education is important, and I think STEM is an important priority.

But in the broader perspective, our educational priorities are often focused on a historical ideal, completely out of touch with a world in which nearly everyone interacts regularly with computers that most people barely understand.

This is a much bigger discussion, but here's the main takeaway: after thousands of hours in the classroom, our students emerge unempowered into an increasingly digital world. We're on our own here.

Excess Capacity: Oh, the Hours We Throw Away

Fine, most people could benefit from some computer literacy. But who has the time?

I don't buy it. We as a society throw away countless hours on games, entertainment, and apparently even tracking the adventures of someone called Snooki. We make video mashups and silly tumblogs and meme pictures of cats. We catalog Pokémon. We read books about vampires romancing high school girls. We write long blog posts.

We have the time.

We have a ridiculous amount of leisure time, hours we just throw away because we can't think of anything better to do.

Basic Coding as a Gateway To Basic Computer Literacy

While it might make sense, I'm not proposing citizens spend a mandatory two years in the "Get Smart About Computers" corps. I'm just proposing that:

  1. Non-techies consider spending a few hours learning how computers work.
  2. Techies encouraging them - and not actively discouraging them! Sheesh!

Thankfully, getting a basic idea of how computers work has gotten incredibly easy. Sites like Codecademy make it easy to learn - hands on - how computers work. These lessons are hands-on, and they're practical. You learn web fundamentals and how to build a simple website. This is empowering stuff. It's a great way to start the journey from a powerless consumer to a contributor, or at least informed consumer. It's not time consuming, and it's free.

2012-05-16 13h21_50

Maybe some of the negative reactions from coders to the Code Year phenomenon have come from not bothering to actually read what's in the classes and assuming that the goal was to turn a bunch of people into programmers. Maybe.

Case Study: Rosemary, Champion of the World (and my daughter)

This past year, my daughter asked me if I could teach her computers. I talked to my wife and rolled it into her school plan. We had ten lessons. During this time, we worked did the following:

  1. We worked through the Getting Started With Programming section of the Codecademy Javascript Fundamentals course. She did all the work on her own, and when she got stuck I would have her re-read the instructions out loud. She'd say "Oh! I should have read more carefully!" and move on. She learned some basic loops and conditionals - nothing difficult. I gave her at test on repl.it where she wrote a loop from 1 to 25, outputting either "You are too young to drive a car!" or "Your age is 20, you can drive a car!"
  2. She asked how the internet works, so we watched a video on YouTube about how the internet works. Then we went on a field trip to our router, and we talked about how to "fix the internet" when it's not working.
  3. She asked about making a web page, and how web pages work. We played with browser tools (Chrome and IE) and she learned the difference between HTML, CSS, and Javascript. I let her recolor my webpage pink, and we went to CNN and put her picture and the headline of her choice in the headlines.
  4. She asked about making a computer game. We talked about why that would take some work, and planned to do that when we started back up in the semester.

2012-05-16 14h05_30

This was fun. We both had a good time. She asked questions that wouldn't have come up otherwise, and she wouldn't have been interested in otherwise.

More importantly, after only 8 or so hours, I noticed she saw the world differently. She had ideas about what computers should do. She laughed when computers did silly things on TV. She read error messages and solved problems in an intelligent way. She keeps asking about building a computer game.

She understands that there's no reason that girls can't have fun with computers, too.

From a time investment of about 8 hours.

This started with writing some Javascript and HTML. That was critical because it helped her understand how computers work. She learned that they were unthinking and unforgiving in the way they followed her instructions. More importantly, she learned that they would do what she told her if she learned their language (in this case, some basic Javascript).

Case Study: The Judge

In a funny bit of timing, just last night I read about the judge on the Oracle / Google trial was able to make an informed decision about a Java function because he'd been -shocker - learning some Java. It was something that would have seemed obvious to most programmers, but was delightfully refreshing to hear in a court.

Now here is a later followup where the Judge slams Oracle:

Judge: We heard the testimony of Mr. Bloch. I couldn't have told you the first thing about Java before this problem. I have done, and still do, a significant amount of programming in other languages. I've written blocks of code like rangeCheck a hundred times before. I could do it, you could do it. The idea that someone would copy that when they could do it themselves just as fast, it was an accident. There's no way you could say that was speeding them along to the marketplace. You're one of the best lawyers in America, how could you even make that kind of argument?

Oracle: I want to come back to rangeCheck.

Judge: rangeCheck! All it does is make sure the numbers you're inputting are within a range, and gives them some sort of exceptional treatment. That witness, when he said a high school student could do it...

So here's a judge who's able to make an informed decision about a technology case. What's more shocking is that this is news.

Some Objections

Shouldn't everyone learn plumbing, too?

Jeff's been having fun equating the "learn to code" movement with "learn plumbing!"

This is one of those fun arguments that sounds good but falls apart after a moment of thought. What would the return on investment be - both in cost and time - for your average person to learn more about plumbing than working a plunger? How often do they work with plumbing in a way that would benefit from a deeper understanding?

How often do most people work with computers, smartphones, and internet sites in a way that would benefit from knowing a bit more?

Not more code! The world needs less code!

This is a bad argument for three reasons:

  1. This is a coder's argument to other coders. Solving your application's issues is often best solved by thinking, not mindless coding - granted. That has nothing to do with whether it's worthwhile for your average person to learn about how computers work.
  2. It's - perhaps unintentionally - elitist. We nerds have a huge advantage in dealing with the computers that surround us. Telling the rest of the world that they can't join in, or acting like it's harder than it is, is just wrong.
  3. It assumes that amateurs won't be writing code anyways. I've seen tons of code over the years written by people who had a problem to solve. They cobbled it together from internet searches. It was inelegant, but it worked in that it solved their problem. There are a lot of populist pseudocoding tools out there - think of ifttt or the use of countless Wordpress plugins - that let real people solve real problems without having to wait for nerds to get involved. They'll do what they need to do, the question is whether we'll encourage them and help them find the structure.

Fine, learn computer literacy, not coding!

This is at least something. The problem here is twofold:

  1. It assumes people know what questions to ask, or how to ask them. As long as they see computers and the internet as mystical boxes full of magic, that's not the case. I'm continually helping people solve problems by prompting them to actually read what a program or web page is telling them (and saying that computers and web pages need to give better messages or not require input is irrelevant).

    Without knowing the fundamental differences between how computers and people think, people resort to just yelling louder in their own language.
  2. It assumes we know what people need to learn. That's only kind of true. Often the approaches I've seen here come down to teaching them how to use a particular product or software package, which is definitely not "teaching a man to fish." Real problem solving skills in the technology world come down to thinking systematically and rationally about technology, which requires some basic understanding.

Where Next?

If you don't know how to code, get started. It's free, fun and easy.

If you know how to code, encourage others around you when they want to learn. Odds are, someone did that for you a while back.

ASP.NET MVC Authentication - Customizing Authentication and Authorization The Right Way

We're continuing the ASP.NET Authentication series (yes, I'm doing a few overlapping series, and yes, it's making me dizzy). The previous post covered Global Authentication and Allow Anonymous. This one continues with a simple tip that can be summed up as follows: keep it simple by extending rather than rewriting.

I see a lot of questions that involve unnecessary complications, and very often it's due to customizing authentication and authorization. For example, developers see that the AuthorizeAttribute won't work for their case, so they start to write a lot of code - using HttpModules, custom view engines, injecting authentication services and sprinkling authorization service calls throughout their controllers, etc.

Sometimes that's necessary, but it's rare. Most of the time you can handle things with either a custom membership provider, a subclassed AuthorizeAttribute, or both. Craig Stuntz summed this up well in a blog post back in 2009:

If you are developing a web application which requires authentication or security features not included in the regular ASP.NET membership feature, you might decide to implement these features yourself. But it seems as if the first instinct of many ASP.NET MVC developers is to do this by customizing their Controllers, because they’ve decided that AuthorizeAttribute can’t possibly serve their needs. They will decide that the way to do this is to write some sort of parent Controller type which examines the currently logged-in user when an action executes and changes the result of the action based on who they are. Others will try to re-implement AuthorizeAttribute without ever examining the source code for the original.

These are very bad approaches, for two important reasons:

  1. They don’t work. If your action result is cached by ASP.NET, then the action will not even run the next time it is requested. AuthorizeAttribute handles this correctly. Code you write in a Controller cannot handle this. Code you write in a custom action filter could work, if you cut and paste the implementation from AuthorizeAttribute. But AuthorizeAttribute is unsealed, so why cut and paste, when you can subtype?
  2. The modularity is wrong. You should aim to develop MVC sites which can be used with any authentication (or role) provider, whether it is ASP.NET membership, domain authentication, OpenId, or a custom membership provider. Wiring authentication concerns into a Controller makes this extremely difficult.

Craig recommends the same thing I'll be recommending - leverage the existing security systems in ASP.NET and ASP.NET MVC.

ASP.NET MVC's authorization system runs directly on top of the existing ASP.NET security system, and both have well established and tested extensibility points.

  • If you need to customize the way ASP.NET MVC integrates with the underlying ASP.NET security system, subclass the AuthorizeAttribute
  • If you need to customize the way the underlying ASP.NET membership system works, leverage the existing ASP.NET security provider system

I'll throw in one more - make sure you really need to customize anything at all.

Step Zero: Do you need to customize anything?

I've seen some examples that use the techniques below to implement authorization features... which didn't need implementing because they were already built in. AuthorizeAttribute, for example, already includes support for role-based authorization, but I've seen sample code that "adds in" role checking.

I've also seen examples which were built because the author assumed that AuthorizeAttribute only worked with Forms Authentication. That's not true - it just verifies if the user (1) is authenticated (2) is in the listed users and/or roles (if any are set). The same AuthorizeAttribute works with other authentication methods - the same attribute is also used with Windows Forms in the Intranet Application template, as well.

The AuthorizeAttribute has a pretty narrowly defined job, so it doesn't take much work to verify whether it already does what you need - check first.

Quick Note: Authentication and Authorization

Any sufficiently long article on web security must eventually devolve into distinguishing between authentication and authorization, so here goes:

  • Authentication is the act of establishing who the user is.
  • Authorization is the act of determining if that user should have access to a resource.

A user may be authenticated but not authorized to access a resource - e.g. a simple user isn't authorized to access site administration pages. A user may be authorized, but not authenticated - e.g. a site which allows anonymous access, a site which controls access using an API / access key, etc.

Customizing ASP.NET MVC's Interaction with ASP.NET Authorization by subclassing AuthorizeAttribute

Subclassing an AuthorizeAttribute is pretty straightforward. In most cases you just create a class that inherits form AuthorizeAttribute and override AuthorizeCore. Here's a very simple example: a key based login.

A simple key based AuthorizeAttribute

In this example, we'll be setting up a custom authorization scheme based on a key which will be validated using a very simple algorithm. This isn't secure for any number of reasons, but with some minor modifications (e.g. expiring a key once it is used) it would be sufficient for things like simple beta program for a pre-release website.

We'll accept a parameter called X-Key and validate that it's a number that passes a simple check.

To start with, we'll create a new class called KeyAuthorizeAttribute that inherits from AuthorizeAttribute:

public class KeyAuthorizeAttribute : AuthorizeAttribute  
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string key = httpContext.Request["X-Key"];
        return ApiValidatorService.IsValid(key);
    }
}

public static class ApiValidatorService
{
    public static bool IsValid(string key)
    {
        int keyvalue;

        if (int.TryParse(key, out keyvalue))
        {
            return keyvalue % 2137 == 7;
        }
        return false;
    }
}

This AuthorizeCore method checks a value (via header, querystring, form post, etc.) and calls into a service to validate it. In this case, validation is a simple static method that runs our validation algorithm. In your case, you'd probably want to check against a list of pre-issued keys in a database, call out to an external service, etc. AuthorizeCore returns a boolean value - pass or fail.

We can then slap that [KeyAuthorize] attribute on any action or controller in the site, or register it globally (as shown in my previous post).

This request would be allowed: http://localhost:8515/?X-Key=26381272 (because 26381272 mod 2137 equals 7)

This request would be denied: http://localhost:8515/?X-Key=12345

Handling Authorization Failures

AuthorizeAttribute is based on Forms Authentication, so when a request fails a call to the AuthorizeCore method of an applicable AuthorizeAttribute, it will by default redirect to the login page so that, hopefully, the user can get authorized. I walked through the mechanics of this redirection process in a previous post.

The default login page doesn't make any sense in a lot of scenarios, including the example above. If someone comes to my site with a missing or incorrect API key, the login page isn't going to help them. For that specific case, I'd perhaps want to redirect them to a page that tells them how to apply for an access key.

If you need to change what happens when users fail authentication, you've got a few options:

  1. If you want to change the site-wide redirection URL for when a request fails authorization, you can change the authentication/forms/loginUrl setting in web.config. Keep in mind, though, that this affects all authentication redirects for your entire application.
  2. If you want to run custom logic - including but not limited to redirecting to a URL - you can override the AuthorizeAttribute's HandleUnauthorizedRequest method.

Many more examples

This is a very simple example. You can find a lot more by searching on the override code above, e.g. "override authorizecore httpcontextbase". Some examples:

Note: The last one on the list implements the base AuthorizeAttribute interfaces rather than subclassing AuthorizeAttribute, which bears some discussion.

Subclass AuthorizeAttribute or Implement FilterAttribute, IAuthorizationFilter?

If you just look at the AuthorizeCore code in the AuthorizeAttribute, you may think it's so trivial you might as well just build your own IAuthorizationFilter from scratch. The problem with that idea is that it's very easy to do that incorrectly. A large amount of the code in the AuthorizeAttribute is there to make sure it works correctly with caching. If you get this wrong, you can very easily open yourself up to this scenario:

  1. Authorized user accesses an action and is correctly granted access.
  2. The action uses output caching, so the output is cached for future views.
  3. Unauthorized user accesses the action. Since it was cached, the restricted (or user-specific) content is served to the unauthorized user.

The AuthorizeAttribute has ensured that the code in the CacheValidateHandler and OnCacheAuthorization methods interact correctly with ASP.NET's caching system. Unless you really know what you're doing, it's a much better idea to start by subclassing AuthorizeAttribute.

Using AuthorizeAttribute with ASP.NET Web API

ASP.NET Web API uses the same AuthorizeAttribute scheme. It works the same way - drop an attribute on actions, API controllers, or globally, and you're set. All good so far.

But, if you use System.Web.Mvc.AuthorizeAttribute (or a subclassed attribute) on an Action Controller, nothing will happen. ASP.NET Web API uses a very similar, but different AuthorizeAttribute, found in the System.Web.Http namespace. There are some important differences (beyond the scope of this post), but a good place to look is at how the two AuthorizeAttributes handle unauthorized requests:

This reflects the difference between the target focus of both systems - ASP.NET MVC primarily focuses on HTML code that's viewed by people in a browser, and ASP.NET Web API primarily focuses on HTTP traffic that's handled by code. In ASP.NET Web API, you don't tell someone they're not authorized with a login page, you return the appropriate HTTP status code.

Fluent Security

If you have complex authorization requirements - particularly around configuration - you might want to look at Fluent Security. It provides a fluent, code-based configuration system, which lets you do define your application's authentication requirements in one place, like this:

SecurityConfigurator.Configure(configuration =>
{
    // Let Fluent Security know how to get the authentication status of the current user
    configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);

    // Let Fluent Security know how to get the roles for the current user
    configuration.GetRolesFrom(() => MySecurityHelper.GetCurrentUserRoles());

    // This is where you set up the policies you want Fluent Security to enforce
    configuration.For<HomeController>().Ignore();

    configuration.For<AccountController>().DenyAuthenticatedAccess();
    configuration.For<AccountController>(x => x.ChangePassword()).DenyAnonymousAccess();
    configuration.For<AccountController>(x => x.LogOff()).DenyAnonymousAccess();

    configuration.For<BlogController>(x => x.Index()).Ignore();
    configuration.For<BlogController>(x => x.AddPost()).RequireRole(BlogRole.Writer);
    configuration.For<BlogController>(x => x.AddComment()).DenyAnonymousAccess();
    configuration.For<BlogController>(x => x.DeleteComments()).RequireRole(BlogRole.Writer);
    configuration.For<BlogController>(x => x.PublishPosts()).RequireRole(BlogRole.Owner);
});

GlobalFilters.Filters.Add(new HandleSecurityAttribute(), 0);

Customizing ASP.NET MVC Authorization using the existing ASP.NET Security systems

ASP.NET has been around for a long time. It's been beaten on pretty hard, and the existing systems have undergone a huge amount of real-world testing. When you run into a constraint that pushes you towards writing some custom code, the best approach is to make sure you understand how the existing systems work and integrate with them as closely as possible.

Since ASP.NET has been around for a long time, there is a good amount of existing information on the security system. It's a big topic. I'm just going to (try to) summarize, pointing out the best extension points from an ASP.NET MVC point of view.

Extending the Forms Authentication provider

As mentioned earlier, authentication is the process of establishing who the user is. It doesn't say anything about what access you've got, it just verifies that you are who you claim to be. ASP.NET has some built in systems to handle that - the Forms Authentication provider handles browser based login and account management, and there's a Windows Authentication provider which integrates with the Windows authentication. If you need to modify how authentication works, you'll almost certainly be working with the Forms Authentication provider.

You might assume that you extend authentication by plugging in a new Authentication Provider, but that's not the case. There are two in the box Authentication Providers, and you can't add new ones. That's pretty much never an issue, though, because the Forms Authentication provider gives you plenty of hooks for extension.

Warning: The next paragraph is exceptionally nerdy, but it sets some important background for interfacing with Forms Authentication.

Forms Authentication uses a Forms Authentication Ticket to track your identity - essentially your authenticated username. The ticket is stored in an encrypted Forms Authentication Cookie.  There's support for cookieless authentication, which automatically appends cookie information via an encrypted querystring value. There are two main components that make Forms Authentication work - the FormsAuthetication class which sets the authentication cookie for authenticated users, and a FormsAuthenticationModule which checks for the cookie, authenticates you and sets your identity in the HttpContext. Since FormsAuthenticationModule is an HTTP module, it runs for every request, way at the beginning of the pipeline. That's an important part - a secure authentication system needs to check authentication at the beginning of the request.

This is all underlying machinery - the important part is that when someone logs in, something in your application calls FormsAuthentication.SetAuthCookie(). That sets the Authentication Cookie, which is then checked by the FormsAuthenticationModule on each request. You can see an example of how the default ASP.NET MVC AccountController Login method uses it:

[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

A good example is DotNetOpenAuth, which adds OpenID, OAuth, and InfoCard authentication to ASP.NET by integrating with the ASP.NET Forms Authentication ticket. You can see how this works in the DotNetOpenAuth OpenAuthAuthenticationTicketHelper.cs class, and in the OpenIdRelyingPartyMvc sample's UserController.

The important points here:

  • While it's tempting to look at your custom authentication needs as completely unique, most can be handled by determining who a user is (your code) and then telling ASP.NET Forms Authentication to track them.
  • If you decide that you'd rather just handle authentication outside of ASP.NET, you've got a lot to think about. You'll be writing a lot of new code which hasn't gone through near the security testing, beating, improvements, etc., that Forms Authentication has.

The ASP.NET Roles and Membership System

The above authentication discussion was completely independent of where and how the actual user information was stored, and that's important. Forms Authentication doesn't handle passwords, logins, user roles, etc., it just tracks users once it's been told about them.

ASP.NET Membership

The default system for managing user and is ASP.NET Membership. There's a SqlMembershipProvider that runs against a SQL Server database, but the provider system allows you to plug in your own custom Membership Provider for managing users. Your login process can use your membership provider to validate the user's credentials, and you can use the membership system to store user information if you want. More on that in a bit.

ASP.NET Role Management

For some sites, authorization and authentication are nearly synonymous - the only goal of authorization is to prevent anonymous access. But for many sites, you've got different roles - users, administrators, superadministrators, and omegasupremeadministrators. For those cases, you need something that maps users to roles, and that's what Role Management Providers do. In most cases, roles and membership are managed together - the same system that tracks who your users are controls what rights they have. You'll often see role and membership providers in one big package, but they're two separate things.

Customizing Membership in ASP.NET

As before, the zeroth rule of customizing membership is to reconsider if you need to. There are plenty of hooks into the existing flow, such as overriding OnAuthorization and just adding some information to HttpContext.Items, Session, etc.

If you really do need a custom user management system, the first thing to do is look to see if someone's already written it. There are membership providers for a lot of backing systems. A quick search shows tons of them:

Implementing your own Membership Provider

If you don't find a Membership Provider that works for you, it's not difficult to write your own. Remember that you're taking more responsibility for your system's security. Matt Wrock wrote a great overview: Implementing custom Membership Provider and Role Provider for Authenticating ASP.NET MVC Applications.

There's plenty of information on MSDN as well, for example: Implementing a Membership Provider

Simple Membership

You'll notice that a lot of Matt's overview of creating Membership and Role Providers included a lot of System.NotImplementedException, because the ASP.NET Membership Provider system shows some underlying assumptions around user data going in relational databases. You can just ignore those parts and use what you want, but if you find the whole thing a little too complex and are tempted to throw it all out, I'd recommend looking at SimpleMembership from ASP.NET Web Pages.

Matthew Osborn wrote an overview of what SimpleMembership is and how it works in ASP.NET Web Pages. Although it was originally written for ASP.NET Web Pages, it can be readily adapted to ASP.NET MVC using the SimpleMembership.Mvc NuGet package.

Wrapping Up

ASP.NET MVC gives you a huge amount of flexibility, and it's tempting to want to write a lot of custom code. If you understand the underlying security systems that ASP.NET MVC uses, though, you can usually integrate with what's in place. You'll be saving yourself a lot of unnecessary code, along with the added costs of testing, debugging and maintenance. More important, though, you'll be using a system that's undergone a huge amount of security testing over the past decade.

For further study:

Posted by Jon Galloway | 4 comment(s)
Filed under: ,
More Posts