📜 ⬆️ ⬇️

OpenID, OAuth and other buns

Why do I need OpenID



It happens like that, you go to your favorite site, and there is a link to another site, and there the article is very interesting and, most importantly, useful — and you want to add a comment like “Well done!” And you need to register to add a comment, and to register you need to enter “First Name”, “Last Name”, “Login”, “Email”, “Email Again”, “Password”, “Again Password”, “I read the rules and agree with everything that will happen here” and “Captcha”. And click “Register”, and then bang - “Login” - is busy, and the fields “Password”, “Password Again”, “Captcha” are erased. So like this. Enter your other login (which is the second, and not the main and not favorite) and again the password, again the password (post) and the captcha again, and the bang - everything is OK, I just forgot to put the checkbox “Read the rules ...” again. Well, okay, the circles of hell passed once again, a letter came out on the soap, activated the account, so where was the article there, and well, they are not great, they are Well done and hell with this, they know so.
Conduct an experiment, in your favorite mail do a search for the word "activate" - that's about how much you registered on the sites.
On the other hand, you think, and let's simplify, and make a simple addition of a comment: “Name”, “Email”, “Message” - and “Email” can be omitted. After 3 months you enter, and there - SPAM! Okay, I clean it - and the effect is zero, the spam continues, I added a captcha - well, sort of ok, but then somehow they bypass it again. And attention (!) - enter the registration ... Oh!
But there is (Hurray!) - OpenID.

Began to understand. Here's an article about how OpenID works. It was a bummer here, in that the OpenID provider sends only ClaimedIdentifier to our site, which in this case looks at me andriy-chernikov.myopenid.com , okay, you can cut off a part and select andriy-chernikov - quite readable. We continue, we argue - normal boys use gmail, and google also has OpenID, only my ClaimedIdentifier (I don’t roll up at all, I’m not at all, I’m not happy with the milestones, I’m not at all; .
"Hello, AItOawl7JUIQLXJf1Z_x1MoYu21XbfBuzvoriso, glad to see you again on our site"
- something somehow does not sound. We find out that in addition to the OpenId identifier, you can retrieve other data, even Email! Well, you understand, i.e. if a potential user leaves a message “Well done! Only here it doesn’t work for you, it is also under IE, and here you invented a bicycle, you just had to look at such a library, but it’s very well written here on such a link ”I can write him“ Dude, I am very grateful. ” The site (knowing his email) will send a notification to him, and this Man will understand that the comment left 3 months ago was useful, and he will be pleased. In addition, as you understand this email is not necessary to check, since it has already been verified.
In general, the task:
To make the user not registering, but just clicking a button gave us information about himself, and was registered, and was able to leave a comment, well, or put a rating (for ZOYCH vote, for example), and spammers go sideways.

Openid



First, we choose a provider. These will be: Google, Yandex, MailRu, Rambler, Livejournal, MyOpenID. In my opinion - these are the most popular services with us. (about vkontakte, twitter and facebook - later).
Links for OpenID authorization:
')


Lists of authorized sites (when you test, you will need it):


Create a new ASP.NET MVC3 project. Download and add DotNetOpenAuth to it. OpenID you most likely have.
We create the controller for authorization on OpenID (as at handcode [thanks]):
public class OpenIdController : Controller { private static OpenIdRelyingParty openIdProvider = new OpenIdRelyingParty(); public ActionResult Index(string userOpenId) { //    . IAuthenticationResponse response = openIdProvider.GetResponse(); // response  null,    OpenID    . if (response == null) { Identifier id; //   OpenID . if (Identifier.TryParse(userOpenId, out id)) { try { //      OpenID IAuthenticationRequest request = openIdProvider.CreateRequest(userOpenId); return request.RedirectingResponse.AsActionResult(); } catch (ProtocolException ex) { TempData["error"] = ex.Message; } } return RedirectToAction("Index", "Login"); } else { //     OpenID switch (response.Status) { //   case AuthenticationStatus.Authenticated: { TempData["id"] = response.ClaimedIdentifier; return RedirectToAction("Index", "Main"); } case AuthenticationStatus.Canceled: { TempData["message"] = "   "; return RedirectToAction("Index", "Main"); } case AuthenticationStatus.Failed: { TempData["message"] = "     ."; TempData["error"] = response.Exception.Message; return RedirectToAction("Index", "Main"); } default: { return RedirectToAction("Index", "Main"); } } } }} 


Now the most important thing is how to request additional data? There are 2 ways: FetchRequest and ClaimRequest. By practical consideration we find out that:
  1. For google - you need to use FetchRequest
  2. For yandex, mailru, rambler, myopenId - you need to use ClaimRequest
  3. Livejourns live in Live Journal and don’t give any details at all.

For FetchRequest, do this. After creating a request to the provider, we indicate that we would still be interested to find out:
 try { //      OpenID IAuthenticationRequest request = openIdProvider.CreateRequest(userOpenId); FetchRequest fetch = new FetchRequest(); fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Contact.Email, true)); fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.First, true)); fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.Last, true)); fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Preferences.Language, true)); request.AddExtension(fetch); return request.RedirectingResponse.AsActionResult(); } 


And after successful authentication, we take this data:
 case AuthenticationStatus.Authenticated: { var fetches = response.GetExtension<FetchResponse>(); if (fetches != null) { string str = string.Empty; str += string.Format("Email : {0} <br/>", fetches.Attributes[WellKnownAttributes.Contact.Email].Values[0]); str += string.Format(" : {0} <br/>", fetches.Attributes[WellKnownAttributes.Name.First].Values[0]); str += string.Format(" : {0} <br/>", fetches.Attributes[WellKnownAttributes.Name.Last].Values[0]); str += string.Format(" : {0} <br/>", fetches.Attributes[WellKnownAttributes.Preferences.Language].Values[0]); TempData["info"] = str; } TempData["id"] = response.ClaimedIdentifier; return RedirectToAction("Index", "Main"); } 


For ClaimsRequest similarly: <br/>
 try { //      OpenID IAuthenticationRequest request = openIdProvider.CreateRequest(userOpenId); ClaimsRequest claim = new ClaimsRequest(); claim.BirthDate = DemandLevel.Require; claim.Country = DemandLevel.Require; claim.Email = DemandLevel.Require; claim.FullName = DemandLevel.Require; claim.Gender = DemandLevel.Require; claim.Language = DemandLevel.Require; claim.Nickname = DemandLevel.Require; claim.PostalCode = DemandLevel.Require; claim.TimeZone = DemandLevel.Require; request.AddExtension(claim); return request.RedirectingResponse.AsActionResult(); } 


AND
 //   case AuthenticationStatus.Authenticated: { var claims = response.GetExtension<ClaimsResponse>(); if (claims != null) { string str = string.Empty; str += string.Format(" : {0} <br/>", claims.BirthDate); str += string.Format(": {0}<br/>", claims.Country); str += string.Format("Email: {0}<br/>", claims.Email); str += string.Format(" : {0}<br/>", claims.FullName); str += string.Format(": {0}<br/>", claims.Gender); str += string.Format(": {0}<br/>", claims.Language); str += string.Format(": {0}<br/>", claims.Nickname); str += string.Format(": {0}<br/>", claims.PostalCode); str += string.Format(" : {0}<br/>", claims.TimeZone); TempData["info"] = str; } TempData["id"] = response.ClaimedIdentifier; return RedirectToAction("Index", "Main"); } 


Well, everything seems to be super, but (!) There are problems, the first problem is the response from Rambler, dotNetOpenAuth treats its response as erroneous. In general, the Rambler in openid.claimed_id and openid.identity returns a link to the specification, well, in general, we do the processing in the section when there seems to be an error:
 case AuthenticationStatus.Failed: { //     DoTNetOpenAuth var email = Request.Params["openid.sreg.email"]; var fullname = Request.Params["openid.sreg.fullname"]; var nickname = Request.Params["openid.sreg.nickname"]; if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(fullname) || string.IsNullOrEmpty(nickname)) { TempData["message"] = "     ."; TempData["error"] = response.Exception.Message; } else { AuthOpenID("http://id.rambler.ru/users/" + nickname, fullname); } return RedirectToAction("Index", "Main"); } 


Problems with MailRu. By the way, it does not work with the site, which is located locally. Those. localhost is not a site and an error “Bad Realm” is issued - that is solved by actually loading the site somewhere.
I am from Ukraine, I have an hour an hour earlier than Moscow time, so the response record comes an hour more, i.e. it turns out that the answer was received not in a couple of seconds, but in 1 hour and a couple of seconds. And is treated as an error. Add a few settings to the Web.Config:

 <configSections> <section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/> </configSections> ... <dotNetOpenAuth> <messaging clockSkew="60:00:00" lifetime="10:00:00" strict="true"> <untrustedWebRequest timeout="00:10:00" readWriteTimeout="00:01:00" maximumBytesToRead="1048576" maximumRedirections="10"> ... </untrustedWebRequest> </messaging> </dotNetOpenAuth> 


The second error is that ClaimResponse does not parse the parameters. Just pick up from Request.Params:
 //   case AuthenticationStatus.Authenticated: { string str = string.Empty; str += string.Format("Email: {0}<br/>", Request.Params["openid.sreg.email"]); str += string.Format(" : {0}<br/>", Request.Params["openid.sreg.fullname"]); str += string.Format(": {0}<br/>", Request.Params["openid.sreg.gender"]); str += string.Format(": {0}<br/>", Request.Params["openid.sreg.nickname"]); TempData["info"] = str; TempData["id"] = response.ClaimedIdentifier; return RedirectToAction("Index", "Main"); } </code> 


OpenID Summary

We have a quick authentication using OpenID, of which only livejournal does not provide email there.

Oauth


In general, OAuth is not intended to be authenticated instantly, very well written about OAuth here . But (!) Authentication is there, we will use it. First, choose the heroes:
Facebook, Twitter, VKontakte.
In each of them you need to create an application, it is simple, I will simply indicate where it is done:


Then we still need to remove permissions for applications, so here are the links:



Let's start with Twitter


Twitter has Consumer key and Consumer secret - important values ​​that need to be hidden somewhere and not shown to anyone.
Also in the settings it would be necessary to set the Callback URL, Website and Application Website - but it works without it.
Then everything is simple, we just see that in the examples of dotNetOpenAuth there is an example of authentication through twitter - so we just copy to ourselves and everything.
Facebook


Oh, here I killed 4 hours to screw OAuth which in dotNetOpenAuth for authorization in facebook. But it did not. Then I found an excellent library: http://facebooknet.codeplex.com/ - there is a working example (it did not work for me on localhost, but on the site everything is ok). By the way, very convenient for directional integration with Facebook, a lot of everything. There are in NuGet.
So, having created a Facebook application, we have 3 keys:


We will need them all. Also customizable in the tab Web Site Site URL and Site Domain.
Go to http://developers.facebook.com/docs/authentication and learn how it happens there:
Step 1. I give my ApplicationID and ask me to give me information about myself, by the way, add scope = email to the request so that they give us another email:
graph.facebook.com/oauth/authorize?client_id={0}&redirect_uri={1}&scope=email

User sees:


If the user allows - we get the code (code). If not, then we get an error_description that the user does not want to give you anything.
Step 2. Further we go with this code and request access_token:
graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&client_secret={2}&code={3}


Here we get a string like:
access_token=114782445239544|2.izExIa_6jpjjhUy3s7EDZw__.3600.1291809600-708770020|VbEE8KVlLTMygGmYwm-V08aVKgY&expired=12010


Or an error in JSON, which seems to be illogical as it is to play with formats, but oh well.
Step 3. Having access_token, we request user data:
graph.facebook.com/me?access_token={0}

What in JSON format we get a lot of information about the user. Hooray!
In contact with


Vkontakte, as always, stood out to write step by step how to do what was like on Facebook - they just gave their widget and wrote “tell me where to send the data.” Widget must pass ApplicationID and that's it. Vkontakte does not transmit any email at all - but it throws off a link to the avatar and photo of the user. Javacript:
VK.init({ apiId: vkontakteAppId });
VK.Widgets.Auth("vk_auth", { width: "210px", authUrl: '/vkontakte' });


And in the desired controller we get:
 public class VkontakteController : Controller { public ActionResult Index(string first_name, string last_name, string uid) { var str = string.Empty; str += string.Format(" : {0}<br/>", first_name); str += string.Format(": {0}<br/>", last_name); TempData["info"] = str; TempData["id"] = "http://vkontakte.ru/id" + uid; return RedirectToAction("Index", "Main"); } } 


Results


We partially solved the task. MyOpenID, Yandex, Google, Facebook, MailRu - generally well done and cool. Twitter, Livejournal, VKontakte - squeezed email (I don’t particularly vouch for Twitter). Rambler - in general, well done would be if they had not messed with the implementation of the protocol.
An example can be found here: http://cocosanka.ru
Sources download here: http://bitbucket.org/chernikov/smartauth
PS: I know about Loginza. When trying to authenticate to google, he also requests contacts from Google Mail. I'm certainly not paranoid, but why should they? BUT?
PPS: I learned here that Yandex did OAuth too ...

Source: https://habr.com/ru/post/109700/


All Articles