Using OAuthWebSecurity without SimpleMembership
I’ve been researching the new support in ASP.NET for OAuth and OpenID authentication. It provides a nice and easy to use wrapper on DotNetOpenAuth. The main APIs are on the OAuthWebSecurity class and they provide methods to authenticate against your OAuth and OpenID providers as well as associate those OAuth and OpenID accounts to an account with your local membership provider (and strictly speaking your simple membership provider). Personally, I dislike the coupling between the authentication piece and the persistence piece. Fortunately this API can still be used without using providers.
The main APIs to be aware of are:
- OAuthWebSecurity.RegisterXxxClient
- OAuthWebSecurity.RegisteredClientData
- OAuthWebSecurity.RequestAuthentication
- OAuthWebSecurity.VerifyAuthentication
RegisterXxxClient (where Xxx is: Microsoft, Twitter, Facebook, Google, Yahoo or LinkedIn)
The various Register APIs allow you to initialize which OAuth/OpenID identity providers you want to use (many of these require passing your application identifier and secret).
RegisteredClientData
This API provides the list of the registered identity providers. This is necessary for the ProviderName property when requesting authentication.
RequestAuthentication
This is the API to invoke to trigger a login with one of the identity providers. The parameters are the identity provider name (so one of the ProviderName values from the RegisteredClientData collection) and the return URL where you will receive the authentication token from the identity provider. Internally it does a Response.Redirect to take the user to the identity provider consent screen.
VerifyAuthentication
This API validates the authentication token from the identity provider and returns the results. You can then use these results to log the user into your own application (typically with Forms Authentication). There is also additional data returned from the identity provider such as email, gender, etc. Different identity providers return different information.
So to trigger the authentication, this is all you need to do:
[AllowAnonymous] public void LoginWithProvider(string provider) { OAuthWebSecurity.RequestAuthentication(provider, Url.Action("AuthenticationCallback")); } [AllowAnonymous] public ActionResult AuthenticationCallback() { var result = OAuthWebSecurity.VerifyAuthentication(); if (result.IsSuccessful) { // name of the provider we just used var provider = result.Provider; // provider's unique ID for the user var uniqueUserID = result.ProviderUserId; // since we might use multiple identity providers, then // our app uniquely identifies the user by combination of // provider name and provider user id var uniqueID = provider + "/" + uniqueUserID; // we then log the user into our application // we could have done a database lookup for a // more user-friendly username for our app FormsAuthentication.SetAuthCookie(uniqueID, false); // dictionary of values from identity provider var userDataFromProvider = result.ExtraData; var email = userDataFromProvider["email"]; var gender = userDataFromProvider["gender"]; return View("LoggedIn", result); } return View("Error", result.Error); }
It’s really up to you how to identify the username when you’re logging them in with FormsAuthentication, but it’s important that to uniquely identify your users your must use both the provider name and the provider user id. In the above snippet we’re just logging the user in with that, but you could built your own back-end database to allow users to manage their own username for your application. This mapping is in essence what the new SimpleMembership system is doing, and if you don’t need or want to control the database schema then it’s a fine solution.
Also notice the extra data from the authentication result. This contains additional user information the identity provider has returned to the application. From what I can tell, here is the breakdown (beyond ProviderUserId) of what data each provider returns (these are the actual keys used in the ExtraData dictionary):
Google: email, country, firstName, lastName
Microsoft: name, link, gender, firstname, lastname
Facebook: username (which is really an email address), name, link (URL to their facebook page), gender, birthday
Twitter: name, location, description, url (URL to their Twitter page)
Yahoo: email, fullName
LinkedIn: name, headline, summary, industry
Microsoft did a nice job making it easy to use DotNetOpenAuth.
What does it take to add more providers than those that are pre-built. According to the asp.net site it’s “easy” to add your own providers. Since OAuth is a common WebService to WebService communication channel it would be great to see an example where new Clients are added.
Good question. The extensibility model is just that of DotNetOpenAuth, so I imagine there are already examples. I’ll look into it and if I can’t find any then that would make for another good post.
This did a better job of explaining the API than the bloody Microsoft docs. Thanks mate.
This. is. golden.
Thank you kindly and so much for taking the time to share this information. Can’t begin to describe how helpful this is.
Nice. But the Facebook username returned is not always an email address, so you can’t rely on that. Indeed this has to be separately requested somehow, but I can’t figure out how this is possible using this new OAuth wrapper.
Stu, thanks for the info. I’ll have to go confirm and change the code as necessary. To get the email as its own field the email scope needs to be requested. I don’t recall if DotNetOpenAuth has a setting to also request it (more confirmation needed).
How does one logout? For instance I allow the user to login via openid, once authenticated I make a REST call to further authenticate within my system. If the REST call returns an unsatisfactory result I want to “unauthenticate” the user and redirect back to login page. The redirect works but when I click on the yahoo login it thinks I am already authenticated.
I don’t follow. Logout of which system — your web app or Google/Yahoo/Facebook?
Let’s say they log in to my site using a yahoo account. Then the session times out and I force them back to the log in page. All they have to do is click on yahoo and they are immediately in my application again. I would like to force them to log in again through yahoo.
Unfortunately OAuth2 doesn’t address this. Each provider either doesn’t support this or has a custom approach.
Facebook just allows you to make an API call to remove yourself from the user’s apps: https://developers.facebook.com/docs/howtos/login/server-side-logout/
I’ve not seen anything from Google or Live on this. And Yahoo doesn’t support OAuth2 anyway.
Any IDP that you log into likely will create its own cookie, independent of your application. Thus if you decide the user is logged-out of your app, it has no bearing on the IDP cookie still available to them. That cookie will not be readily accessible by you or the DotNetOpenAuth library. Indeed the stackoverflow site exhibits the same problem. If you log into stackoverflow with google, then explicitly log out, then log in again with google, you will not be asked for credentials a second time.
How can I use OAuthWebSecurity to do authentication for Single Sign On using SQl Server Database
You don’t. OAuthWebSecurity is meant for federation, which is where you outsource the authentication. If you want to do self-authentication then look into forms authentication or this: https://brockallen.com/2013/01/26/replacing-forms-authentication-with-wifs-session-authentication-module-sam-to-enable-claims-aware-identity/
Then what is the purpose of the methods OAuthWebSecurity.RegisterClient method
Looks like it allows you to register a custom OAuth client.
Can you guide me how to crate custom OAuth client. Because on creating custom OAuth client it gives me error.
Can i use oAuth to authenticate using the DB of Third Party?
Yes, you can sort of use OAuth to authenticate, but it’s not designed as an authentication protocol. I’d suggest reading google’s OAuth docs to understand how OAuth works and then you’ll have a better idea how to use any OAuth provider:
https://developers.google.com/accounts/docs/OAuth2
Hi Brock. Thanks for the sample code. But it doesn’t work as-is with the Microsoft (LiveID) provider. A return URL must be passed in the VerifyAuthentication method. Please see http://stackoverflow.com/a/15058420/634824 and update your sample. Thanks!
IIRC, live won’t allow you to register localhost as a callback URL, so that’s why that won’t work. You’d need to deploy to a real host (or use DNS tricks to work with localhost).
Hi
This example is great. I can login a user without using membership now.
Do you know of a way I can check whether the web browser currently has a logged in facebook user using server side code before calling the requestauthentication call? I want to check if someone is already logged into facebook, if so maybe prompt user and ask if this is them before doing the requestverification call.
Many thanks
There are tricks/hacks out there for knowing if the user on your site is currently logged into facebook — search for it.
Thanks Brock. Really helpful article. I’ve look carefully and the default simplemembership implementation and the OAuth implementation you get with the MVC4 website template. It is messy to say the least. It’s hard to decouple and hard to follow the flow logic at times.
This is a brilliant article and just what I have been looking for. Thank you so much for writing this mate. Cheers!
This is working for me when I’m running the application directly from VS. If I’m setting it in IIS , throwing forbidden Error. Please help me…
Sorry Mark — that’s not really enough to go on. Perhaps post on the ASP.NET forums, as more people will look at your question and might be able to help. http://forums.asp.net/
Ok Brockallen. With the same code are you able to host in IIS & able to run in VS? in var result = OAuthWebSecurity.VerifyAuthentication();
line Exception is throwing . The remote server returned an error: (403) Forbidden.
Sounds like maybe your client id and/or secret are being denied? Not sure, without seeing/debugging it.
where does OAuthWebSecurity.VerifyAuthentication get its parameters from.What are its parameters exactly.
Is has some internal values it uses to validate the response. It doesn’t really matter — I’d strongly encourage you to use the newer Katana authentication middleware to do social logins. It’s a more robust approach and is supported by Microsoft.