Integrating Claims and OAuth2
I just created a sample library that illustrates how Claims can be easily integrated when using OAuth2 identity providers for authentication. I created the OAuth2 library from scratch (it was quite straightforward). In this library I wanted to hide as much of the OAuth2 protocol and claims mapping as possible so that a consuming application would just have to say which OAuth2 provider to use and what page/URL to return the user to once all the login and claims magic has happened.
Is very easy to use from MVC (these client IDs and secrets are throw-away):
static void RegisterOAuth2Clients() { OAuth2Client.Instance.RegisterProvider( ProviderType.Google, "421418234584-3n8ub7gn7gt0naghh6sqeu7l7l45te1c.apps.googleusercontent.com", "KDJt_7Rm6Or2pJulBdy0gvpx"); OAuth2Client.Instance.RegisterProvider( ProviderType.Facebook, "195156077252380", "39b565fd85265c56010555f670573e28"); OAuth2Client.Instance.RegisterProvider( ProviderType.Live, "00000000400DF045", "4L08bE3WM8Ra4rRNMv3N--un5YOBr4gx"); }
Here’s my view to give the user a choice as to which provider to use:
<h2>Login With:</h2> <ul> <li>@Html.ActionLink("Google", "Login", new {type = ProviderType.Google})</li> <li>@Html.ActionLink("Live", "Login", new {type = ProviderType.Live})</li> <li>@Html.ActionLink("Facebook", "Login", new {type = ProviderType.Facebook})</li> </ul>
And then you just have a Login action method to choose the OAuth2 provider you want to use:
public ActionResult Login(ProviderType type) { // 1st param is which OAuth2 provider to use // 2nd param is what URL to send the user once all the login magic is done return new OAuth2ActionResult(type, Url.Action("Index")); }
And that’s it. Once the login has happened claims are available via ClaimsPrincipal.Current.Claims (which is where they normally are).
If you’re familiar with using OAuth2 you’re used to seeing a authorization code callback endpoint. This is a detail of the protocol I wanted to encapsulate so that the consuming application didn’t have to “deal” with that. To hide this I use an AreaRegistration internally in the library to define the callback endpoint. In this area when the OAuth2 callback arrives with the authorization code, the library continues the protocol to exchange the code for a token and then uses that token to obtain the profile information for the user. Once that profile data is acquired, it is converted into claims. Then the WIF Session Authentication Module (SAM) is used to log the user in. We then redirect back to the URL indicated in the OAuth2ActionResult above. Magic!
Code is up on GitHub. Library is up on NuGet. Feedback is welcome.
I took a look at the code. I have been working with WIF using Azure ACS. The federation module validates the tokens and makes sure they origin from ACS. Using the code you present, are there anything that prevent some “hacker” to send some self made token to your callback controller.
That’s what the “state” param in OAuth is used for (for CSRF style attacks).
Okay, i will take a look at that also then. I tried it out and ran into a problem when i combined it with my Azure ACS identities. I have set up a oauth provider for dropbox.com and writes the sessionTokenToCookie. Things works fine if the user first authenticate from Azure ACS and then dropbox. Then the dropbox ClaimsPrincipal is written, FedAuth cookie ends up at the user, overwriting the principal it got from AzureACS.
If the other way around, authenticate from dropbox and the session cookie is written and i authenticate from ACS after, i get an exception “The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.”. I can manually decode the cookie with no problem.
Now this is not my area of expertise and I might be missing something. I would like to get my oauth and sts to play along. I would be happy to send you a email with some code/demo of the problem if its of any interest.
If you’re having a problem with it, then send me the *smallest* sample that reproduces it and I can take a look.
Thanks. I am going to play a bit more my self first, (not that easy to send a little sample because of the Azure ACS setup ect).
I looked into the state thing, and i see what you mean now. Being new to Oauth it might be a stupid question. Coming from using Azure ACS as STS and the federation pipeline taking care of confirming that the tokens are legit and not edited, i am wondering if i can trust the “claims” coming from this oauth process. A state (key) is stored and then confirmed when the provider redirects it back. I could sniff this key and hit the callback controller with my own token that let the application think im someone else?
My problem from before, if you have a sample laying around where you authenticate you self using Azure ACS, you could try to add your oauth library and first log in using one of your providers and then log in using Azure ACS and you should get the same error. Otherwise i will try find a solution my self. Thanks for your comments so far.
I just found out the source of my issue.
<!—->
<!—->
When i created my project i told the Identity and Access dialog to make it farm ready or something, adding System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler
Do you have any idea if your code can be changed to support MachineKeySessionSecurityTokenHandler instead of SessionSecurityTokenHandler
how can i log out…
log out of what?
Hi Brock:
I adapted you code slightly to work with WebForms. Essentially copying the code in OAuth2CallbackController.CallBack() into a web form code behind. Stepping throuth it — it authenticates as expected in the callback. However after the callback code when I use
var id = System.Security.Claims.ClaimsPrincipal.Current;
The id.Identity.IsAuthenticated is always FALSE. Any suggestions?
Note:
cp = transformer.Authenticate(String.Empty, cp);
shows that is is true as expected.
If you create a ClaimsIdentity and don’t pass an authentication method, then IsAuthenticated will be false.
I think I like this since it automatically returns claims as the result. going to compare it with SimpleAuthentication and see which will fit in my current solution.
If you’re on OWIN/Katana then I’d suggest using Microsoft’s social media authentication middleware instead.