I am currently working on an MVC4 project that allows users to authenticate through OpenID. I don’t think I need to convince anyone about the benefits for both parties that come with that. Users don’t have to register at your site, and you have less of those tedious account maintance tasks.

Although it’s apparently coming later on as a built-in feature into the Visual Studio templates (Damien Edwards showed that stuff for Web Forms during aspConf), let me show how you can very quickly add simple OpenID support to your MVC4 application.

More after the jump.

Setting the satge

Let’s start with the basic MVC4 project, Internet template.

First of all you will need the excellent DotNetOpenAuth library, and that’s, obviously, something you should get from Nuget. After installing, you’d see a number of libraries added to your application.

Next thing to do is to grab openid-selector JS library. This can be downloaded from http://code.google.com/p/openid-selector/. Once downloaded, unzip the contents and copy all the images and CSS folders to your app’s “Content” folder. Additionally, copy openid-en.js and openid-jquery.js from the JS folder to your app’s “Scripts.” We only need that, because we will be relying on JQuery only.

This JS library will give our login page a familiar look you know for example from Stackoverflow, and would act as a ready plug and play interface for OpenID authentication.

Adding the CS and JS to the views

Since this is MVC4 we can leverage on bundles. Go to App_Start/BundleConfig.cs and add the following there:

C#

1

2

3

4

5

6

7

bundles.Add(newScriptBundle("~/bundles/openid").Include(

"~/Scripts/openid-jquery.js",

"~/Scripts/openid-en.js"));

bundles.Add(newStyleBundle("~/Content/css/openid").Include(

"~/Content/openid-shadow.css",

"~/Content/openid.css"));

This would allow us to easily embed the openid specific JS/CSS whereever we need them. We need a couple of more minor tweaks before we proceed. In the Scripts/openid-jquery.js change

JavaScript

1

img_path :'images/',

to

JavaScript

1

img_path :'/Content/images/'

so that it corresponds to our MVC4 project structure. If you are using the standard MVc4 project, go to site.css and remove padding-left and padding-right from the a tag definition, as that would ruin our OpenID layout.

Now let’s add the bundles to our view. Go to Views/Account/Login.cshtml. You can delete the existing FormsAuthentication form entirely, as we will be switching to OpenId authentication. Add our CSS bundle and in the scripts Razor section add our script bundle:

1

2

3

4

5

6

7

8

9

10

@Styles.Render("~/Content/css/openid")

@sectionScripts{

@Scripts.Render("~/bundles/jqueryval")

@Scripts.Render("~/bundles/openid")

<script type="text/javascript">

$(function(){

openid.init('openid_identifier');

});

</script>

}

Finally, add the HTML form that will act as our OpenID gateway for our application’s users.

Our front end work is done for now, and we should be getting a nice Login page by now (which, of course, does nothing for the time being).

Implementing OpenID authentication service

Before we start the C# implementation part of this, let me briefly explain how we are going to approach the problem:

1. We will be calling the OpenID provider to authenticate our user
2. The response claim will get wrapped into a custom OpenIdUser object
3. When the user is successfully authenticated we will issue a FormsAuthentication ticket based on that OpenIdUser, this way we will be able to leverage on ASP.NET IPrincipal implementation (HttpContext.Current.User).
4. We will actually have a custom IIdentity implementation as well. This way, you could access the information stored in OpenIdUser from anywhere in the application by just typing User.Identity.OpenIdUser

The model is rather simple, and has the properties you’d normally expect from the User type of model, except Password of course, which is not needed. We have two constructors, because there are two ways in which we will create this object.

First way is to takee in a string that’s been decrypted from Forms authentication ticket UserData part. This is how we will get the info of the user from the cookie and inject into HttpContext as our IIdentity (will show this later).

The second constructor takes in claims response from OpenId provider, and populates the properties based on that.

We also have a simple ToString method which we’ll use to “serialize” the object into a string and embed this data in the Forms ticket.

The service

Now let’s add the service, which will do all the heavy lifting for us.

We will start off with an interface – although it’s not necessary, it could come in handy if you wish to provide an alternative implementation later on. For example, in this particular tutorial, I will not be saving user information into our database at all – we simply use OpenID to authenticate a user. If you wish to store those OpenIDs in your user repository you might very easily do that.

Moreover, since we have an interface in place, you might want to change the implementation to not piggyback on FormsAuthentication tickets as we will do in this example, but use a completely different IPrincipal implementation.

It’s not very complicated at all. In the constructor we instantiate an OpenIdRelyingParty, which will be our OpenID authentication mediator. When the client calls ValidateAtOpenIdProvider and passes the username to be authenticated, we set up a set of request fields (Claim, in OpenID lingo) which we’d like the OpenID provider to return – in our case Email, FullName and NickName.

Once client has validated (authenticated itself), it can request the user object. This is done by invoking the GetUser method. This method uses a couple of helpers, but the gist is that we obtain the claim through the use of OpenIdRelyingParty. This claim can the be converted into our custom user object.

Finally, the service API exposes one more method (not defined in the interface), and that’s CreateFormsAuthenticationCookie. This creates a FormsAuthentication ticket which can be sent down to the browser.

To make this all work we need one more small extension method for the IAuthenticateRequest.

If you recall, ValidateAtOpenIdProvider will return IAuthenticationRequest, which in turn exposes a property RedirectingResponse. This lets us send the user to the external page, at the OpenID provider server (i.e. Google login), where he will need to authenticate himself. Then the view re-renders, so that automatically means we have to put the rest of the login in the default GET version of the public ActionResult Login() action.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

[AllowAnonymous]

publicActionResult Login()

{

var user=openidemembership.GetUser();

if(user!=null)

{

var cookie=openidemembership.CreateFormsAuthenticationCookie(user);

HttpContext.Response.Cookies.Add(cookie);

returnnewRedirectResult(Request.Params["ReturnUrl"]??"/");

}

returnView();

}

So when the view re-renders, we try to get user from our OpenID membership service. If the user is successfully returned – meaning the user has authenticated without any problems, we proceed by issuing a FormsAuthentication ticket and redirecting the user back to the original page on which his Authentication challenged was raised – that’s levering on the standard ASP.NET ReturnUrl query string.

The user should at this point see the following:

We might as well end this tutorial here. If you need very basic authentication mechanism, this should already be OK. I will take it a step further and let’s add a customized IIdentity. This would allow us to have access to our OpenIdUser anywhere within the application by calling User.Identity.

Custom IIdentity

We will implement System.Security.Principal.IIdentity interafce, and add an OpenIdUser property there.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

publicclassOpenIdIdentity:IIdentity

{

privatereadonlyOpenIdUser _user;

publicOpenIdIdentity(OpenIdUser user)

{

_user=user;

}

publicOpenIdUserOpenIdUser

{

get

{

return_user;

}

}

publicstringAuthenticationType

{

get

{

return"OpenID Identity";

}

}

publicboolIsAuthenticated

{

get

{

returntrue;

}

}

publicstringName

{

get

{

return_user.Nickname??string.Empty;

}

}

}

We will inject this custom implementation of Identity into the Principal object using a custom AuthorizeAttribute. You could do that also by registering a handler for PostAuthenticateRequest in Global.asax but that’s a bit of an overkill, as it would execute numerous times, whereas the logic in the attribute will only execute once per request.

This grabs the OpenIdUser object from the FormsAuthentication ticket (or rather its UserData section) and inject it into the HTTP context. As a result, we now can go to our partial view for the login, _LoginPartial.cshtml, and replace the login section with

1

2

3

4

<p>

Hello,@User.Identity.Name

@Html.ActionLink("Log off","LogOff","Account")

</p>

Now this isn’t anything spectacular yet. But we could also use our custom Identity

As a side note, we don’t need any implementation of logout, since the standard one included in the out of the box AccountController does that for us (it deletes the FormsAuthentication ticket).

Trying it out

Now that we have the application’s pieces in place, let’s try it. If you used the attributes like I did above, you should be challenged right away.

If you try Google, you should see this:

And then login successfully

Same with Yahoo:

Summary

In a few simple steps we have implemented OpenID authentication thanks to the very robust DotNetOpenAuth library. You could obviously take it further, as I mentioned before, perhaps store user’s accounts in some repository of yours. Either way, this should be a decent start and I hope someone someone finds it useful. Source code is below (github).

Of course you could (and probably should) offer Twitter and FB alongside OpenID providers as an option for your users, but the implementation of that would have to be separate from the solution shown here.

http://xdrtas.blogspot.com David Lastra

Thank you very much! Firefox bookmarks!

Here

I like reading blogs about Membershipservice. You did really good work on here. I’ll bookmark your site. Thanks, Klaus.

StyleBundle(“~/Content/css/openid”) is a virtual path (doesn’t need to have actual corresponding folder) – we are telling MVC4 that if this virtual path is called, return a bundle of:
“~/Content/openid-shadow.css” and
“~/Content/openid.css” (in turn, these are concrete paths).

The bundle path could be anything and doesn’t have to exist i.e. StyleBundle(“~/Content/abcdefg/”)

Then when you render the bundle you just call that virtual path and that would render the files you included.

I get an error at the very end of part one.
Specifically in the post method of the login method.

return response.RedirectingResponse.AsActionResult();
Please help I would love to get this to work.

Error 1 ‘DotNetOpenAuth.Messaging.OutgoingWebResponse’ does not contain a definition for ‘AsActionResult’ and no extension method ‘AsActionResult’ accepting a first argument of type ‘DotNetOpenAuth.Messaging.OutgoingWebResponse’ could be found (are you missing a using directive or an assembly reference?) C:\EcommerceApplication\EcommerceApplication\EcommerceApplication\Controllers\AccountController.cs 39 53 EcommerceApplication

Great post, I am so close to making this work. Any Help is greatly appreciated.

babiloba

Just add:

using DotNetOpenAuth.Messaging;

babiloba

Great tutorial:)
One question. How do you get the fullname??

babiloba

Solved it…

The question was directed @ goolge login and got it to work by using fetchrequest instead of claimrequest
and same for the response part

Martina

Thanks! Really good help. I’m using it only for the google account provider.

I was trying to add a bussiness error for the login, let’s say, besides being a valid google user which is checked by the provider, I’d like to reject all those users who are not valid (I can easily check this with the email address).
The problem is, wherever I set the validation, either it still logs in or it loops in the login view without allowing me to try with another account.
So, I basically need to know where the IF sentence should go and how to prevent the guy from logging in.

Some tip would be great!
Thanks in advance.

Martina Cortés

Hey! Thanks a lot for the post, it really helped Absolutely clear for a beginner like me.
Still need some extra help. I need to add some bussiness validation so as to reject users who don’t have a certain email domain (using google but with @privatedomain.com accounts).

Where should I place that validation? Wherever I try, either it allows the login eitherway or it simply doesn’t show the google input view again.
Thanks!!

http://twitter.com/aarnott Andrew Arnott

Thanks for writing up this tutorial. But I need to call out a serious security flaw with part of your code. You’re using email address as the username when you log the user in. The OpenID protocol does *not* guarantee any assurance on email address and as you’ve implemented it, it’s fairly trivial to impersonate any user on your site. Instead, you really must use the ClaimedIdentifier as the username. You can certainly *display* the email address to the user as if that were their username, but for all user identity checks in your system you must use ClaimedIdentifier.

Thanks for tutorial. I tired and run the app. I face one issue. When I select google, it allow me to select my google account but instead of going to home(welcome) page, it redirects me to same login page with URL