Ever wonder how to implement OpenID authentication in your Rails app? This episode will show you how to add it to a site with an existing authentication system.
Actually, from a readability standpoint, this width is nearly perfect. The with of the main column is about 60 characters--6 less than is considered optimal.
Maybe I missed something, but is a user record created in the database upon successful Open ID authentication? What is the user ID of the user that is created? Is it the nickname provided by the Open ID authenticator? What if the nickname conflicts with an existing user name already in your Users model?
@Dave, a new user model is automatically created and saved if they don't already have an account. If they already have an account then it just fetches the record from the database instead of creating it.
This behavior is not required when using OpenID, it is just one way to do it. Alternatively you may want to redirect to the signup page with some fields prefilled.
The user ID is generated automatically through auto-incrementing like all other Rails models.
Currently it doesn't complain if there's already a user with that name. That's because I disabled validations with save(false) call. However, this is just a temporary solution, how you want to handle validations is up to you. One solution is to redirect to the signup page.
Does this handle the use case where the user is already registered on your site, but then decides to log in using OpenID? It looks like in that case, you'd get an additional user added to your DB because the find_or_initialize_by_identity_url would not return anything.
Beware that if you're not going to automatically create users on-the-fly like Ryan does, you'll need to make sure that when a user adds an identity URL to his record manually, it gets normalized.
Authentication won't work if the value in the DB isn't normalized (e.g. missing a trailing slash). I accomplished this with a before_validation callback: http://pastie.caboo.se/91417
Thanks Ryan. It seems to me really that since a user can have multiple Open ID's, really one ought to allow a user to register additional ones against their user account (has many relationship, not an attribute in the User model) so as to prevent multiple User records being added (if that's a concern, of course).
Just downloaded and expectations are same for it too. Videos tutorial always very affected than reading paper/e-paper books.
Now, the collection is getting bigger so I thought or imagine there should be a rating bar people start rating their best tutorials and new user could easily find the best ones.
This and the restful_authentication Railscasts were both very useful. Thanks!
Would like to see a follow-up showing open-id login of new user going to sign-up form instead (to allow additional required fields to be entered/validated: e.g., email, city, real/screen names, etc.).
Would also like to see a deeper exploration of getting information from the user’s OpenID source. I know that some OpenID servers provide access to additional info fields, but haven’t figured that part out yet.
Finally, I’d like to see a way to turn around and offer a user’s login on my server as an OpenID service for other websites the user wants to access.
Heh. I guess I’m asking a lot :-) These are just suggestions and I’ll look forward to your future Railscasts regardless of whether you cover these topics or not.
This screencast and code makes the OpenID part easy. It's the integration with an existing system, and writing specs that takes a bit more work.
The article by Ben Curtis was helpful. I also ended up stuffing a bunch of the code into the User model: authenticate_by_openid(identity_url, registration). This takes care of the logic in the model so it can be reused from a signup form as well as the login form.
I am wondering a bit about how people are handling duplicate nick names. What pattern are you using in your app in order to prevent this from happening. I considered just letting validation fail the attempt to save the user object, but the problem is that some open ID providers (Verisign) allow you to implement a remember me functionality, essentially skipping over the login process on a subsequent login, and sending the same sreg information. This makes it difficult for a user to try a different nick name.
@Corrigan, what I have done in the past is store the sreg parameters in a session and redirect to the registration page. This allows the user to complete the registration process and choose a different name if they need to. I'll see if I can put some code up somewhere and I'll add a link here for it.
Whoops! Should have tested that before posting... Now when a user signs up, they could send a post request with anything in the indentity_url field and not have to put in a password.
When using this is there anything different for Rails 2.0.2 (specifically cookie-based session stores)?
Also, I keep getting 'uninitialized constant OpenID::Store'
I looked at http://drnicwilliams.com/2007/07/26/sample-app-rails-multiple-openids-per-user/
and there is a 'gems' directory in vendor which contains ruby-openid-x.x.x and there's 'stores.rb' inside.
Well, it seems that Rails2.0's restful controller.
I put some code to show like this,
-----------------------------
params(:result) do |status, identity_url, registration|
case status
when params(:result):missing
failed_login "Sorry, the OpenID server couldn't be found"
when :canceled
failed_login "OpenID verification was canceled"
when :failed
failed_login "Sorry, the OpenID verification failed"
when :successful
@current_user = User.find_by_identity_url(identity_url)
Hi, I've been googling about a week to solve a ruby-openid problem. And found a working example on Rama McIntosh's blog. http://myutil.com/2007/12/29/openid-2-0-2-with-rails-2-0-2
At first, I thought rails Ticket #10604 patch worked. But it was more complicated than that. Since Rails, ruby-openid, and open_id_authentication plugin versions were not consistent. And Rama McIntosh went through all. You can find Dr. Nick’s example application fully ported to rails 2.0.2.
Here is my development environment.
------------------------------------------------------------
LG LW25 advanced laptop IntelCore2 T5600, Mem 2G
XP Pro version2002 SP2
Cygwin + CygPutty
Ruby ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-cygwin]
Rails 2.0.2
ruby-openid (2.0.3)
rake (0.8.1)
mongrel (1.1.3)
mongrel_cluster (1.0.5)
ruby-yadis (0.3.4)
app db : sqlite3
------------------------------------------------------------
Besides his README, some extra typing was needed to run the app.
1. svn co http://drnicwilliams.com/svn/openid/demos/apps/openidauth_multiopenid/trunk openidauth_multiopenid
2. Do not erase some svn directories because he made some rake tasks related with svn.
3. rake freeze TWICE!
4. mkdir log
5. require_gem => gem
openidauth_multiopenid/vendor/gems/ruby-openid-1.1.4/lib/openid/service.rb:7
/home/yunsoo/auth_openID/openidauth_multiopenid/vendor/gems/ruby-openid-1.1.4/lib/openid/discovery.rb:8:
6 rake db:create
7. gem install mocha
8. rake test
9. config/environment.rb
config.action_controller.session = { :session_key => "_openidauth_multiopenid_session", :secret => "mocramagic" }
Great screencast, although after installing it i'm getting a "Install the ruby-openid gem to enable OpenID support" error even though the gem is definitely installed. any ideas?
>>You should consider increasing >>the width of your website. >>almost everyone has a wide >>screen monitor these days, and >>on my monitor 2/3'rds of your >>site is white space.
I strongly disagree. What you don't see is the negative effect this has on those people who must stay with a 680x800 screen for reasons of poorer vision. Making the text wider requires us to continuously scroll left and right, which is really aggravating.
I think with this there should be a step right after initial OpenID auth that is a CAPTCHA before the account is created to reduce the possibility of spam. This is critical for anyone adding OpenID authentication to his/her system.
OpenID is not inherently trustworthy.
So:
1. User goes to login page
2. User logs in with OpenID
3a. If it's his first time logging in redirect to a CAPTCHA and then, if successful on CAPTCHA, create the account and log the user in.
3b. If he is already on the system authenticate without the CAPTCHA and log the user in.
My last comment isn't meant to say that this should've been included in the tutorial; I realize it's definitely in the realm of a separate discussion/tutorial. It's just a suggestion for those actually wanting to implement OpenID.
Everyone should be aware that there is a level of trust that needs to be established before allowing any user to login via OpenID, just as with normal authentication. Trust could be established with a CAPTCHA, whitelist, email verification or other methods.
Wow this was a critical failure for me, but I can't see where I went wrong. I tried using the openid railscast on top of the restful_authentication using the stateful engine.
Everything seemed to work until I tried logging in. The engine went out to my openid provider and when control was passed back to my app it was looking for session/show.html.erb.
Retried this tutorial after setting up restful_auth as in episode 67.
I get the error when trying to execute a login using my openid:
Unknown action
No action responded to show
Something is getting confused with the routing within the controller and is trying to call the show action. Anyone run into this or find a work around?
So, if openID works with your app, but you're having problems dealing with giving your openID users, a user name, here is what i did.
Following RESTful Authentication with OpenID, allow your user to login to your site using their openID.
Redirect them to a profile page, prompting them to change their e-mail/username/whatever-else-you- want. I also added an observe_field to alert the user whether the desired name had already been taken. Then, when passing the information to the current_user, before you can current_user.save! it must have a password.
So, generate a random number, and or string. Then simply set this as the password, you can then e-mail it to the user (in theory i haven't tested this out yet), if they want to change it, but it doesn't matter if they only login using openID.
this works great for me, let me know if i'm doing something horribly wrong. This way our openID users can get and change their usernames while not allowing anyone to create a user without a password.
Hey, I have a great suggestion, what about mention on every episode that it is obsolete (if it is)? So, there won't be anymore stupid guys like me, trying to do like you describe in the episode and finding out that your code doesn't work with latest stable version. I can help you starting ranging the episodes: this one is obsolete!
Made my way here from Tardate 11.1[1] ... all worked as advertised (Rails 2.2.2, restful_authentication and open_id_authentication plugins). There was some minor breakage in open_id_authentication and request.relative_url_root which Josh fixed in Sep 2008[2] but hasn't made its way into the default plugin install. Don't know why @George had a hard time with it; he didn't give much info to go on.
Thanks, Ryan, for the excellent screencasts. I have great admiration for you. ;]
--j
p.s. @Darryl (comment #4): Thanks for the link to http://webtypography.net ... an excellent site I didn't know about.
After following this tutorial I'm having trouble to convince open id to send me "required" fields specified authenticate_with_open_id.
The result from registration is just something like this: registration = #<OpenID::SReg::Response:0xb6841870 @ns_uri="http://openid.net/extensions/sreg/1.1", @data={}, @ns_alias="sreg">
Hi,I am getting this error
uninitialized constant OpenID::Store
Could you please help me to resolve this issue, i have rails 2.0.2 with latest version of OpenId.
Thanks,
Shahroon
I noticed that when I tried this and error came back saying the method "request.relative_url_root" was undefined. Apparently this was moved out of the request object and into the AbstractRequest object, but what I did instead was change
yeah, I agree with A about the website width, anyways if you use google analytics you can know that for sure.
Could always turn off stylesheets and just read the plain text :) But increasing the width wouldn't be a bad idea.
Great job on the screencast, love 'em! Always informative and great topics!
Actually, from a readability standpoint, this width is nearly perfect. The with of the main column is about 60 characters--6 less than is considered optimal.
Read: http://webtypography.net/Rhythm_and_Proportion/Horizontal_Motion/2.1.2/
Maybe I missed something, but is a user record created in the database upon successful Open ID authentication? What is the user ID of the user that is created? Is it the nickname provided by the Open ID authenticator? What if the nickname conflicts with an existing user name already in your Users model?
@Dave, a new user model is automatically created and saved if they don't already have an account. If they already have an account then it just fetches the record from the database instead of creating it.
This behavior is not required when using OpenID, it is just one way to do it. Alternatively you may want to redirect to the signup page with some fields prefilled.
The user ID is generated automatically through auto-incrementing like all other Rails models.
Currently it doesn't complain if there's already a user with that name. That's because I disabled validations with save(false) call. However, this is just a temporary solution, how you want to handle validations is up to you. One solution is to redirect to the signup page.
Nice job, as usual, Ryan.
Does this handle the use case where the user is already registered on your site, but then decides to log in using OpenID? It looks like in that case, you'd get an additional user added to your DB because the find_or_initialize_by_identity_url would not return anything.
Thanks Ryan. Just added OpenID to my new app.
Beware that if you're not going to automatically create users on-the-fly like Ryan does, you'll need to make sure that when a user adds an identity URL to his record manually, it gets normalized.
Authentication won't work if the value in the DB isn't normalized (e.g. missing a trailing slash). I accomplished this with a before_validation callback: http://pastie.caboo.se/91417
a quick pointer:
in your routes, make sure you put the map.open_id_complete *before* the map.resource :session which is already there.
the resulting error shows up as an un-intuitive #show call on the SessionController
@Todd, Good point. If they try to log in through openid when they have an existing account it will create another user.
To get around this problem, add some instructions telling them to log in normally and edit their account profile. Here they can set their openid url.
Hi, Ryan.
One episode that I want see is about navigation (include nested navigations/level navigation) like the TabNav-plugin.
Thanks Ryan. It seems to me really that since a user can have multiple Open ID's, really one ought to allow a user to register additional ones against their user account (has many relationship, not an attribute in the User model) so as to prevent multiple User records being added (if that's a concern, of course).
@Dave, I kept one OpenID per user for simplicity, but you can certainly put it in a separate model and set up a one-to-many association with user.
MKA: Thank you! Got the same mysterious problem here too.
Just downloaded and expectations are same for it too. Videos tutorial always very affected than reading paper/e-paper books.
Now, the collection is getting bigger so I thought or imagine there should be a rating bar people start rating their best tutorials and new user could easily find the best ones.
Best of Luck
This and the restful_authentication Railscasts were both very useful. Thanks!
Would like to see a follow-up showing open-id login of new user going to sign-up form instead (to allow additional required fields to be entered/validated: e.g., email, city, real/screen names, etc.).
Would also like to see a deeper exploration of getting information from the user’s OpenID source. I know that some OpenID servers provide access to additional info fields, but haven’t figured that part out yet.
Finally, I’d like to see a way to turn around and offer a user’s login on my server as an OpenID service for other websites the user wants to access.
Heh. I guess I’m asking a lot :-) These are just suggestions and I’ll look forward to your future Railscasts regardless of whether you cover these topics or not.
Thanks again!
Hi,
one quick note: the css should probably be
input#openid_url { ... } since it's an id not a class name.
thanks for your great screencasts!
Roland
@Roland, thanks. Fixed. :)
Very useful!
This screencast and code makes the OpenID part easy. It's the integration with an existing system, and writing specs that takes a bit more work.
The article by Ben Curtis was helpful. I also ended up stuffing a bunch of the code into the User model: authenticate_by_openid(identity_url, registration). This takes care of the logic in the model so it can be reused from a signup form as well as the login form.
Ok...one more. Here's a sample of an RSpec method to mock the OpenID request. Some work is needed, but it covers the basics.
http://pastie.textmate.org/102377
I am wondering a bit about how people are handling duplicate nick names. What pattern are you using in your app in order to prevent this from happening. I considered just letting validation fail the attempt to save the user object, but the problem is that some open ID providers (Verisign) allow you to implement a remember me functionality, essentially skipping over the login process on a subsequent login, and sending the same sreg information. This makes it difficult for a user to try a different nick name.
@Corrigan, what I have done in the past is store the sreg parameters in a session and redirect to the registration page. This allows the user to complete the registration process and choose a different name if they need to. I'll see if I can put some code up somewhere and I'll add a link here for it.
Instead of passing false to the save method, I just modified the password_required? method in the user model.
It now reads:
(crypted_password.blank? && identity_url.blank?) || !password.blank?
Is doing something like that ok?
Whoops! Should have tested that before posting... Now when a user signs up, they could send a post request with anything in the indentity_url field and not have to put in a password.
@Ryan, have you found a permanent solution to creating the record instead of save(false)?
When using this is there anything different for Rails 2.0.2 (specifically cookie-based session stores)?
Also, I keep getting 'uninitialized constant OpenID::Store'
I looked at http://drnicwilliams.com/2007/07/26/sample-app-rails-multiple-openids-per-user/
and there is a 'gems' directory in vendor which contains ruby-openid-x.x.x and there's 'stores.rb' inside.
Still getting the error, any ideas?
I've found a patch from here(http://dev.rubyonrails.org/ticket/10604 ) and downloaded it.
after that,moved to
vendor/plugins/open_id_authentication
$ patch -p0 < update_openid_plugin_to_ruby_openid_2.diff
It worked!
@Jai-Gouk, thanks alot for that information about the patch. /@
@All
I get the following error after returning to the website with OpenID authentication.
"Unknown action - No action responded to show"
Anyone got an idea?
Well, it seems that Rails2.0's restful controller.
I put some code to show like this,
-----------------------------
params(:result) do |status, identity_url, registration|
case status
when params(:result):missing
failed_login "Sorry, the OpenID server couldn't be found"
when :canceled
failed_login "OpenID verification was canceled"
when :failed
failed_login "Sorry, the OpenID verification failed"
when :successful
@current_user = User.find_by_identity_url(identity_url)
Hi, I've been googling about a week to solve a ruby-openid problem. And found a working example on Rama McIntosh's blog. http://myutil.com/2007/12/29/openid-2-0-2-with-rails-2-0-2
At first, I thought rails Ticket #10604 patch worked. But it was more complicated than that. Since Rails, ruby-openid, and open_id_authentication plugin versions were not consistent. And Rama McIntosh went through all. You can find Dr. Nick’s example application fully ported to rails 2.0.2.
Here is my development environment.
------------------------------------------------------------
LG LW25 advanced laptop IntelCore2 T5600, Mem 2G
XP Pro version2002 SP2
Cygwin + CygPutty
Ruby ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-cygwin]
Rails 2.0.2
ruby-openid (2.0.3)
rake (0.8.1)
mongrel (1.1.3)
mongrel_cluster (1.0.5)
ruby-yadis (0.3.4)
app db : sqlite3
------------------------------------------------------------
Besides his README, some extra typing was needed to run the app.
1. svn co http://drnicwilliams.com/svn/openid/demos/apps/openidauth_multiopenid/trunk openidauth_multiopenid
2. Do not erase some svn directories because he made some rake tasks related with svn.
3. rake freeze TWICE!
4. mkdir log
5. require_gem => gem
openidauth_multiopenid/vendor/gems/ruby-openid-1.1.4/lib/openid/service.rb:7
/home/yunsoo/auth_openID/openidauth_multiopenid/vendor/gems/ruby-openid-1.1.4/lib/openid/discovery.rb:8:
6 rake db:create
7. gem install mocha
8. rake test
9. config/environment.rb
config.action_controller.session = { :session_key => "_openidauth_multiopenid_session", :secret => "mocramagic" }
Replace to other key, secret like this,
config.action_controller.session = {
:session_key => '_foo_session',
:secret => '4747ba80asdfqwertwertwee5y4365345ertf gadrtvrwebterwvd653b65tyt93c45cb53242eb43'
}
10. mongrel_rails start!!
Does anyone know if these instructions work with the new version of the ruby-openid gem (version 2.0.3)?
"Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]"
14th Feburary 2008
http://dev.rubyonrails.org/changeset/8872
P.S.: Thank you very much for Railscasts!
Great screencast, although after installing it i'm getting a "Install the ruby-openid gem to enable OpenID support" error even though the gem is definitely installed. any ideas?
Hi,
I've implemented OpenID authentification like described in Ryan's tutorial and got a error like this:
NameError in SessionsController#create
uninitialized constant OpenIdAuthentication::OpenID
Can somebody help me?
Greetings,
Jonathan
I am having the same problem as Jonathan with the NameError.
Add
require "openid"
at the top of your controller
Regards,
Amit
>>You should consider increasing >>the width of your website. >>almost everyone has a wide >>screen monitor these days, and >>on my monitor 2/3'rds of your >>site is white space.
I strongly disagree. What you don't see is the negative effect this has on those people who must stay with a 680x800 screen for reasons of poorer vision. Making the text wider requires us to continuously scroll left and right, which is really aggravating.
I think with this there should be a step right after initial OpenID auth that is a CAPTCHA before the account is created to reduce the possibility of spam. This is critical for anyone adding OpenID authentication to his/her system.
OpenID is not inherently trustworthy.
So:
1. User goes to login page
2. User logs in with OpenID
3a. If it's his first time logging in redirect to a CAPTCHA and then, if successful on CAPTCHA, create the account and log the user in.
3b. If he is already on the system authenticate without the CAPTCHA and log the user in.
My last comment isn't meant to say that this should've been included in the tutorial; I realize it's definitely in the realm of a separate discussion/tutorial. It's just a suggestion for those actually wanting to implement OpenID.
Everyone should be aware that there is a level of trust that needs to be established before allowing any user to login via OpenID, just as with normal authentication. Trust could be established with a CAPTCHA, whitelist, email verification or other methods.
Wow this was a critical failure for me, but I can't see where I went wrong. I tried using the openid railscast on top of the restful_authentication using the stateful engine.
Everything seemed to work until I tried logging in. The engine went out to my openid provider and when control was passed back to my app it was looking for session/show.html.erb.
It never seems to run open_id_authentication.
Retried this tutorial after setting up restful_auth as in episode 67.
I get the error when trying to execute a login using my openid:
Unknown action
No action responded to show
Something is getting confused with the routing within the controller and is trying to call the show action. Anyone run into this or find a work around?
Important Safety Tip:
The custom route:
map.open_id_complete 'session', :controller => "session", :action => "create", :requirements => { :method => :get }
Should be placed BEFORE the main route:
map.resource :sessions
When I switched these, everything started working ok
For some reason as soon as I followed this screencast I get this error. Anyone have any ideas?
I go to /login use the openid then I get this.
uninitialized constant SessionsController::User
I'm getting that error too
uninitialized constant SessionController
any idea whats causing this?
Ok If you guys are having the problem i listed above pay attention:
Look at the map.open_id_complete line in your code. You might need to change :controller => "session" to :controller => "sessions"
it should look like this, for me anyway
map.open_id_complete 'session', :controller => "sessions", :action => "create", :requirements => { :method => :get }
I hope this helps people out.
So, if openID works with your app, but you're having problems dealing with giving your openID users, a user name, here is what i did.
Following RESTful Authentication with OpenID, allow your user to login to your site using their openID.
Redirect them to a profile page, prompting them to change their e-mail/username/whatever-else-you- want. I also added an observe_field to alert the user whether the desired name had already been taken. Then, when passing the information to the current_user, before you can current_user.save! it must have a password.
So, generate a random number, and or string. Then simply set this as the password, you can then e-mail it to the user (in theory i haven't tested this out yet), if they want to change it, but it doesn't matter if they only login using openID.
this works great for me, let me know if i'm doing something horribly wrong. This way our openID users can get and change their usernames while not allowing anyone to create a user without a password.
After following this tutorial I'm having trouble. I cannot get the "remember me" function to work. Has anyone else experienced this?
Great videos, Ryan! Keep up the great work!
Hey, I have a great suggestion, what about mention on every episode that it is obsolete (if it is)? So, there won't be anymore stupid guys like me, trying to do like you describe in the episode and finding out that your code doesn't work with latest stable version. I can help you starting ranging the episodes: this one is obsolete!
Made my way here from Tardate 11.1[1] ... all worked as advertised (Rails 2.2.2, restful_authentication and open_id_authentication plugins). There was some minor breakage in open_id_authentication and request.relative_url_root which Josh fixed in Sep 2008[2] but hasn't made its way into the default plugin install. Don't know why @George had a hard time with it; he didn't give much info to go on.
Thanks, Ryan, for the excellent screencasts. I have great admiration for you. ;]
--j
p.s. @Darryl (comment #4): Thanks for the link to http://webtypography.net ... an excellent site I didn't know about.
[1] http://tardate.blogspot.com/2008/10/restfulauthentication-and-openid-with.html
[2] http://github.com/rails/open_id_authentication/commit/00d8bc7f97b9a3113e004475b63dbf84f5397237
Great videos, Ryan! Keep up the great work!
After following this tutorial I'm having trouble to convince open id to send me "required" fields specified authenticate_with_open_id.
The result from registration is just something like this: registration = #<OpenID::SReg::Response:0xb6841870 @ns_uri="http://openid.net/extensions/sreg/1.1", @data={}, @ns_alias="sreg">
Please help!
Hi,I am getting this error
uninitialized constant OpenID::Store
Could you please help me to resolve this issue, i have rails 2.0.2 with latest version of OpenId.
Thanks,
Shahroon
I noticed that when I tried this and error came back saying the method "request.relative_url_root" was undefined. Apparently this was moved out of the request object and into the AbstractRequest object, but what I did instead was change
#{request.protocol + request.host_with_port + request.relative_url_root + request.path}"
to (on line 151 of open_id_athentication.rb)
#{request.protocol + request.host_with_port + request.request_uri()}"
and I got around that error message
Is this still up to date?
Sadik, doesn't seem so. But I've blogged about setting up OpenID here http://thirstyforcola.wordpress.com/2013/06/30/setting-up-openid-on-rails/