📜 ⬆️ ⬇️

We write a little OpenID authorization


A look into the future

Recently, all sorts of social networks and Internet-leading services in general, in terms of attendance and number of accounts, have created a very good, in my opinion, habit - providing unique OpenID identifiers for users so that they can be used to access a third-party site. In addition, a very similar, but not entirely derivative OAuth technology is being developed in parallel, which was born thanks to the efforts of the creators of the notorious Twitter and, quoting Wikipedia, “allows the third party to access the protected resources of the user, without having to pass it on ) login and password".
Personally, I am very pleased with this trend, and, moreover, I am almost certain that this technology will have a future. In particular, in the future there will definitely be new mashaps for aggregating information from a heap of sites (in particular, I would like to recall the very good, but unfairly forgotten service Yahoo Pipes , which was never able to conquer hearts and minds simply because its time had not come yet Perhaps still ahead), and it is this “form factor” that requires a login for a bunch of services at once.
Singing the praises of such technologies can be very long, but personally, for example, I have always been strained by sites that need to register from scratch to download something. After all, we all invariably faced with the fact that when you are looking for where to download this or that material - it often ends up on some completely left and incomprehensible website with a name in the spirit of allbooksmusicwarezzz.omg.su, which also requires registration. No, it's not about piracy, the fact is that there are plenty of sites with all kinds of junk made on my knee. But the human memory for logins-passwords is limited, and there is nothing you can do. But the pleasant moment here is that many OpenID providers, besides the information that directly serves for authorization, can also provide, upon request, basic information about the user - e-mail, full name, preferred language, etc. And on many of these services, you can control what to give and what to keep secret. For example, wouldn’t it be nice for a user to see a friendly inscription “Welcome, Vasya!” In pure Russian, and the profile is already ready for use, along with the avatar, habits and nickname of a beloved cat ?

We do business and work work

Quite lyric, I think, to whom it is necessary as a developer - he already knows everything already written, and simple material is unlikely to be interesting to simple users. Even more laudatory speeches and reasonings are easy to find on Ivan Sagalayev’s blog , and we’ll try to make our authorization system through OpenID (for example, for a blog) in Python, with preference and pianists.
For my blog, which is currently under development in my Projects daddy, I decided to completely abandon the registration and authorization system using my login password, and leave OpenID only. Pylons was chosen as a framework, and for screwing OpenID to Django projects, there is a project with a simple and clear name django-openid . For Pylons, in general, there is also a solution called AuthKit , but I somehow didn’t have a very good relationship with him, and all I found on the network are a few snippets that I had to figure out.
First you need to install the python-openid module to provide technology support, and then create a controller (request handler by URL, the nearest association is jung views.py) and begin to conjure.
$ paster controller auth
At once I will make a reservation that the code is working exactly to the extent that it provides direct authentication, what to do next and how to arrange all this is up to you, gentlemen’s creators. The beginning is pretty standard:
Copy Source | Copy HTML
  1. from openid.consumer.consumer import Consumer, SUCCESS, FAILURE, DiscoveryFailure
  2. from openid.store import filestore
  3. from openid import sreg
  4. from datetime import datetime
  5. from hashlib import md5
  6. class AuthController (BaseController):
  7. def __before__ (self):
  8. self .openid_session = session.get ( "openid_session" , {}) # check if there is a openid session
  9. def index (self):
  10. return render ( '/accounts/enter.html' )
  11. @ rest.dispatch_on (POST = "signin_POST" ) # separate GET and POST requests for different handlers for convenience
  12. def signin (self):
  13. if c.user: # check if the login user has already tried to log in again
  14. session [ 'message' ] = 'Already signed in.'
  15. session.save ()
  16. redirect (url (action = 'index' )) # and if so, do not let go
  17. session.clear ()
  18. return render ( '/index.html' )

Now we come to the most interesting:
Copy Source | Copy HTML
  1. def signin_POST (self):
  2. problem_msg = 'A problem ocurred comunicating to your OpenID server. Please try again. '
  3. g.openid_store = filestore.FileOpenIDStore ( '.' ) # create temporary storage for storing OpenID data, g here is an array of global Pylons variables
  4. self .consumer = Consumer ( self .openid_session, g.openid_store) # yeah, that’s our client
  5. openid = request.params.get ( 'openid' , None) # get a string from the request with an OpenID - identifier
  6. ...

Yeah, and here is some magic. SReg is the extension that allows us to request additional user data from the server. Fields, the value of which I would like to know, are listed in the optional list, and additional data, if anything, can always be requested from the user later. If any additional information is required directly from the nose, you can request it in required, but if the server does not give it away - there will be an error.

Copy Source | Copy HTML
  1. ...
  2. sreg_request = sreg.SRegRequest (
  3. #required = ['email'],
  4. optional = [ 'fullname' , 'timezone' , 'language' , 'email' , 'nickname' ]
  5. )
  6. if openid is None:
  7. session [ 'message' ] = problem_msg
  8. session.save ()
  9. return render ( '/index.html' )
  10. ...

Here I allowed myself to shuffle and wrote this code only to explain the difference between a simple OpenID and cross-login from a Google account. The fact is that Google does not represent users of the OpenID-identifier of the form vasya_pupkin.google.com , but everything is much simpler and more fun. The identification URL for all Google users looks exactly the same - www.google.com/accounts/o8/id . It is curious that when requesting this URL, Google returns a ready-made XRDS (XML-like document returned by the server using the OpenID 2.0 standard ), which already contains everything necessary for authorization, and you as a user are assigned a unique ID, which is essentially , id OpenID.
Copy Source | Copy HTML
  1. if openid == 'google' :
  2. openid = 'https://www.google.com/accounts/o8/id'
  3. try :
  4. authrequest = self .consumer.begin (openid) # paneled
  5. except DiscoveryFailure, e: # what if an error in the address or such a provider exists only in your imagination?
  6. session [ 'message' ] = problem_msg
  7. session.save ()
  8. return redirect (url (controller = 'auth' , action = 'signin' ))
  9. authrequest.addExtension (sreg_request) # connect SReg to extract the required fields for the profile
  10. redirecturl = authrequest.redirectURL (h.url_for ( '/' , qualified = True),
  11. return_to = h.url_for (action = 'verified' , qualified = True),
  12. immediate = False
  13. ) # after all that we had with the server, we must somehow live on
  14. session [ 'openid_session' ] = self .openid_session
  15. session.save ()
  16. return redirect (url (redirecturl))

Well, now you can talk to the server, will we stay friends.

Copy Source | Copy HTML
  1. ...
  2. def verified (self):
  3. problem_msg = 'A problem ocurred comunicating to your OpenID server. Please try again. '
  4. self .consumer = Consumer ( self .openid_session, g.openid_store)
  5. info = self .consumer.complete (request.params, (h.url_for (controller = 'auth' ,
  6. action = 'verified' ,
  7. qualified = True)))
  8. if info.status == SUCCESS: # all in a bunch
  9. sreg_response = sreg.SRegResponse.fromSuccessResponse (info) # retrieve the fields requested in SReg
  10. user = User (by_openid = info.identity_url) # looking for user by id in database
  11. if not user .exist: # but here you can do anything. For example, to add a user to the database
  12. newuser = User ()
  13. try :
  14. email = sreg_response.get ( 'email' , u '' ),
  15. except :
  16. email = u ''
  17. newuser.create (
  18. openid = unicode (info.identity_url),
  19. email = email,
  20. password = unicode ( md5 (info.identity_url) .hexdigest ()),
  21. ip = request.environ [ 'REMOTE_ADDR' ]
  22. )
  23. session.clear () # mutima session
  24. session [ 'openid' ] = info.identity_url
  25. session.save ()
  26. if 'redirected_from' in session:
  27. red_url = session [ 'redirected_from' ]
  28. del (session [ 'redirected_from' ])
  29. session.save ()
  30. return redirect (url (red_url))
  31. return redirect (url (controller = 'auth' , action = 'index' ))
  32. else : # the fakir was drunk
  33. session [ 'message' ] = problem_msg
  34. session.save ()
  35. return redirect (url (action = 'signin' ))

That's all. What to do with the received data - put in cookies, continue registration and ask the user for additional information - it is up to you to decide. Yes, and more, this code does not work with OpenID from Yahoo . If the hunt on Kozma Prutkov's will to pozor in the root - there is information all in the same blog of Ivan Sagalayev . I will be glad to hear any criticism, clarification, suggestions. I will try to deal further with OAuth and organize interested in a little code on the cross-topic from Twitter.

For the opportunity to scratch my head with an awl, like Master Vinogradinka, I really thank this link and all the comrades who left their snippets there.

UPD: Habrayuzer mustangostang reveals the secrets of AX (how to get the returned information from Google), because Google does not give out SReg.

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

All Articles