Tuesday, June 3, 2008

MVC and OpenId

I've read a lot about mvc and openid the last months, but i haven't had the time to use it for anything yet. One of the problems with mvc as i saw it, was the dependence on the url routing module. IIS 5 and 6 don't like modules in .net, i've tried som ways to get around it, but couldn't make it good enough.

Since the routing module workes without filename extensions i assumed that it wouldn't work. I was wrong however. Just by adding a ".mvc" to the controller names, everything workes fine.

The next step was adding openid. I've been looking for better ways to log in and authenicate users on the web a long time. And openid seemes like nice system. Anyone can use it, there are many id providers, the authenication method is not on your site and even microsoft and google seems to like it.

First part getting and installing mvc preview 3 was easy. Getting it to work with iis5 wasn't as hard as i tougth.

Gave the aspnet and iusr users access to my project folder

chaged the routing to "{controller}.mvc".

           routes.MapRoute("Default", "{controller}.mvc/{action}/{id}",
               new { controller = "Home", action = "Index", id = "" }
           );

And all references to "~/*" to "~/*.mvc".

The Dotnetopenid library has an mvc sample, so maybe the task at hand wasn't that hard afterall.

added this to my config.web

<authentication mode="Forms"><forms defaultUrl="~/Home.mvc" loginUrl="~/User.mvc/Login"/></authentication>

Added the userconrtoller and views/user from the sample

To make the usercontroller work with preview 3 i had to change the "void" to "ActionResult", and change the "RenderView" to "return View("Login");" instead of the old syntax.

       public ActionResult Index()
       {
           if (!User.Identity.IsAuthenticated) Response.Redirect("~/User.mvc/Login?ReturnUrl=Index");
           return View("Index");
       }

This is everything i did to make it work, now i can log in with any of my openid accounts.

In my opinion openid by itsself isn't that usefull, the openid provider needs to give us some more information, like email and nickname the very least. The openid has two ways to do this (or dotnetopenid), we got simple registration extension and attribute exchange.

I couldn't get attribute exchange to work, maybe my providers didn't support it, or i used the api wrong.

I did manage to get som info from the myopenid.org provider with simple registration, but the blogger openid didn't want to share anything.

I'm not sure if this is the current weakness in openid, that these protocols aren't that strandarized and supported yet. I'll do some more research on this soon.

to send request for simple registration fields and recive them i changed the usercontroller.authenitcate() method a bit.

               var openid = new OpenIdRelyingParty();
 
               IAuthenticationRequest authReq = openid.CreateRequest(Request.Form["openid_identifier"]);
               ClaimsRequest fetch = new ClaimsRequest();
               fetch.Email = DemandLevel.Request;
               fetch.Nickname = DemandLevel.Request;
               fetch.TimeZone = DemandLevel.Request;
               authReq.AddExtension(fetch);
               authReq.RedirectToProvider();

requests the email,nickname and timezone data

                       ClaimsResponse fetch = openid.Response.GetExtension(typeof(ClaimsResponse)) as ClaimsResponse;
                       if (fetch != null)
                       {
                           string email = fetch.Email;
                           string nickname = fetch.Nickname;
                           string time = fetch.TimeZone;
                       }

extracts the data from the response

So what now? we have a mvc site that we can login with openID. We need a authenication and autorsation framework for our pages, so we don't have to write the same code and checks on every controller action. http://blog.wekeroad.com/ has an excelent atricle on using .net attributes in the code to regulate access to controller actions. It was really easy to get working too, all i did was copy the code for RequiresAuthenticationAttribute and decorate the actions that required an authenicatied user.

       [RequiresAuthentication]
       public ActionResult ShowSecretData()
       {
           ViewData["Title"] = "Personal information";
 
           return View();
       }
Brilliant!

links:

dotnetopenid http://code.google.com/p/dotnetopenid/ (you'll find links to all the specs here too)

1 comment:

Zin-KoAung said...

Did you get ClaimsResponse? I have tried as you said exactly. But I cannot get ClaimsResponse. It is null. Can you please guide me to get ClaimsResponse? Thank you.