Tutorial: Using OpenID with Authlogic

Please note this tutorial is outdated and has been deleted, please see the official Authlogic OpenID addon, which makes OpenID integration extrmely easy. The content that used to be in this tutorial was for Authlogic 1.x and will not work properly with Authlogic 2.x.

  • Share/Save/Bookmark


15 Responses to “Tutorial: Using OpenID with Authlogic”

  1. Cristi Balan says:

    Interesting approach. I don’t use authlogic yet, but I’ll probably try it, along with the other new options (clearance and I forgot the other names), for some new projects.

    However, for the OpenID support, there are two things that I haven’t seen people supporting:

    1. If one signs up with openid, but provides only some of the required attributes (login, email, etc) they should be redirected to the signin page, with the fields they provided already filled in. Once they submit the rest of the field. The knowledge that they actually signed in via OpenId should remain. The other solutions I’ve seen just make the user go back to password auth.

    I implemented this once based on the open_id_authentication plugin. The only problem was that one could fake the form after authenticating to change the openid they authenticated with. This, combined with autologin if OpenId, would allow users to login once as whatever OpenID they wanted :/.

    2. IMO OpenID signin should be done in the same place as login, and just behave find_or_create style, instead of having two separate steps. Also, if one tries to signin with an already existing account, they should just be logged in.

    Come to think of it, these things should be in the ontroller so I’m probably a bit offtopic. I get the feeling the underlying auth logic (heh) should be able to accomodate some of these things (getting optional parameters, remembering that the user came through some SSI, etc)

  2. Ryan Bates says:

    I’ll repeat Cristi in saying that’s an interesting approach! However, I’m not a fan of forcing all authentication logic into a model. It’s taking the concept of skinny controller, fat model a bit too far.

    For example, here’s a controller I wrote which handles both normal authentication and OpenID authentication. It also handles cases where the user is not registered with OpenID, attempts to register there, or redirects them when not valid.
    http://github.com/ryanb/myideadrawer/tree/master/app/controllers/sessions_controller.rb

    Yep, there’s a lot of "cruft", but it’s the right kind of cruft. Take a look at the code. It’s all flash messages and redirects. That kind of thing belongs in a controller. It would be interesting to re-implement that same logic using your UserSession model approach. In my mind it would require auth logic to be spread across both the controller and UserSession model, but I may be wrong about that. Anyway, maybe an idea for the next blog post. :)

  3. CrazyDK says:

    In your tutorial (and also your live example), error occurs when a user signs up with login and password.
    The code in ‘user’ model, "validates_presence_of :password …" results in error because ‘acts_as_authentic’ make password field unaccessible.
    I think that some code like ‘attr_reader :password’ must be added to the user model for preventing error.

  4. Ben Johnson says:

    Cristi and Ryan, I think you guys make some excellent points, and they are precisely why I didn’t integrate OpenID directly into Authlogic. The approach above is one of many, there are many different flavors to OpenID authentication. Crisiti, I’m going to play around with your idea and try to implement it, just to make sure Authlogic doesn’t get in the way.

    Ryan, I agree there is a point where fat object skinny controller can go too far, but in this instance I don’t believe it is. The whole process of authenticating with OpenID is business logic and business logic belongs in the UserSession model. Rendering, setting up flashes, and things that deal with the interface belong in the controller. After all, the whole point of a controller is to pick which view to display and prepare everything for it. In my opinion checking the OpenID nonces and all of that madness really is more business logic. Keep in mind, I am not doing any of the redirecting either, the open\_id\_authentication plugin is doing that. A plugin written by the core rails team. My last point is that the controller you showed me is fine, but if you started adding other authentication methods, like the alternatives I listed in the tutorial, I think you would feel that controller becoming way too cluttered and confusing. Regardless, that is the beauty of rails, either approach will work just fine. In fact, integrating Authlogic into your controller would be extremely simple.

    Thanks for your comments, I love the feedback.

  5. Peter Zingg says:

    I built a small test application using OpenID and an earlier version of authlogic, but trying to follow the lead of another project, embark, in allowing users to authenticate with mulitple OpenIDs.

    I find the whole OpenID business pretty much under development and change; not sure if there is a "best practice" out there yet. In embark’s case the developer used the services at idselector.com that attempt to make it easier for users to login with common providers’ OpenIDs. I guess if you are going to allow logins using multiple OpenIDs it really makes sense for each of those logins to be a separate identity, all tied to a single user. To complicate matters, identities can pull their own nicknames and emails from the OpenID by default. At which point you have to worry about which email to use for notifications, nickname/username/email uniqueness problems, etc.

    And then there’s the question of whether your site wants to allow anyone to "sign up", or only permits approved users to access your resources. So either an administrator pre-builds user accounts (with username and password authentication) and then allows users to associate OpenIDs to them (after which point they can "forget" their passwords), or you need a state-machine-type user that goes through an administrator approval process before his account is activated.

    And there’s also the use of a captcha to try to make sure that there is a real human on the client side. Is there a state or flag that needs to be set at the first login to make sure that the user has correctly answered a captcha challenge? Again I’m not enough of an expert to know where to implement this–my main experience is managing a Google Apps domain. Accounts can be provisioned, but the first time the user logs in, he/she has to respond to a captcha as well as accept the terms of use before he can go on. Otherwise the account is "ready" but not "active".

    I notice that core authlogic *does* pay some attention to the user state, by looking for active? approved? and confirmed? methods on the user object. I guess the captcha/terms of service acceptance could represent the transition from :approved to :confirmed. Or :confirmed could represent the fact that the user responded to an activation email. Maybe you could clarify in you README or a tutorial what you think approved vs. confirmed vs. active means.

    I guess these are more subjects for tutorials and/or examples that don’t concern authlogic itself, but might be part of an authlogic-more gem. Sorry for the rambling, and I know you probably lots of reasons to just keep to authlogic proper; just thought I’d bring these up in case they had any impact on your wonderful design.

  6. Ben Johnson says:

    Hey Peter,

    Excellent post. The above approach is just my personal approach, you can integrate OpenID support into your app however you please, just use my method above to get you started. It all comes down to setting unauthorized_record to an object, so do whatever you want before that, if that makes sense.

    Also, yes Authlogic responds to the active?, approved?, and confirmed? methods as a convenience. You can define those methods any way you want in your model, Authlogic doesn’t do anything in terms of defining those. If you have more, just set up a before_validation callback or overwrite the validate method and do your own checking. The errors work just like ActiveRecord.

    Let me know if this helps.

  7. Matt Powell says:

    Great tutorial! However, shouldn’t the user creation and update methods validate the OpenId passed in as well? If the user mistypes their OpenId, they have no way of getting back into their account — not to mention the possibilities for spammers with one-time fake OpenIds.

    Or have you left that as an exercise for the reader? ;)

  8. Ben Johnson says:

    Hi Matt,

    The user model does validate the url by making sure it is actually an OpenID url, but you are correct, it does not redirect them to their OpenID service and make them authenticate. If you want to do this go for it. This tutorial was meant to be a starting point and more or less focus on integrating OpenID with authlogic. There are a million ways you can integrate OpenID, choose whatever you are most comfortable with. Nothing is hidden behind the scenes here, so if you wanted to integrate this feature it would be as simple as making the authenticate_with_open_id call in your UsersController. In fact, I will more than likely play around with that and add it into this tutorial. Thanks for your comment.

  9. Matt Powell says:

    Hi Ben,

    I’ve been playing around with it for a bit and think I have most of a solution. If you like, I could fork the tutorial project on GitHub and post my changes there?

    Thanks,
    Matt

  10. Nick says:

    I’m exploring Authlogic and really like the choices you’re making code-wise — thanks for releasing this to the public.

    One thing I ran into right away is that as a user I don’t necessarily "know" my OpenID url.

    If I’m using a service like myopenid.com, then the URL they give me is very obviously OpenID url. (Typically something like http://myusername.myopenid.com/.) However, if I use a service like Yahoo! then I might think my OpenID url is actually http://www.yahoo.com because that’s exactly what Yahoo! tells me it is. (See http://openid.yahoo.com/, there’s magic in the background that returns your actual OpenID url.)

    In other words, I don’t think it’s a safe assumption that the OpenID url people enter in your sample registration box is actually their OpenID url. Some verification needs to take place to protect users from themselves.

    I’m looking into some way to handle this appropriately, and if I come up with something elegant I’ll certainly pass it along. Just an FYI.

    Thanks again.

  11. Ben Johnson says:

    Hi Nick,

    Matt already forked the authlogic example and implemented what you are talking about. I have been meaning to merge his changes and update my tutorial. You might want to take a look at his repository of authlogic_example.

  12. Nick says:

    Thanks Ben, I reread Matt’s suggestion after posting mine (wrong order to do that in) and realized we were saying the same thing. Sorry for the repeat, I will definitely check out his repository. Thanks again.

  13. johnpg says:

    Ben,

    I just wanted to say thanks for this. I implemented AuthLogic for admin logins for a site I’m developing, and I had the Open ID part as "extra credit" at the bottom of my giant to do list. Thanks to this tutorial I had this working (on the first try) in about 10 minutes. I’m really, really impressed. I love AuthLogic, it’s really a clean way to handle authentication.

    My only comment is I don’t like the part where we have to override the save method in the UserSession model. Could this be done with some kind of callback hook instead?

    Cheers,
    John

  14. Charlie says:

    Wouldn’t it be better to have the sessions controller pass the entire request object, rather than just the params hash? After all, the authentication info may not be in the params at all. It might be using basic auth, or some other kind of auth request header, or it might be using cookies. If you only pass the params hash to the UserSession model, it may not have enough information to do the authentication.

  15. Hi Ben,

    first of all, thanks for AuthLogic and for the related tutorials. Nice work, and I appreciate it. :)

    Second: I’ve spent some time trying to figure out how to validate an OpenID identifier when an user signs up. Right now the user can enter pretty much anything. I know you’ve just laid out a good start here, and not a full solution, but I’ve struggled (and failed) to make this work in my app, and sadly, I have to declare defeat at this point.

    Would you mind helping me out? How would you add identifier validation to the `users_controller.rb`?

    Thanks in advance,
    Carlo