#304 OmniAuth Identity
With the release of OmniAuth 1.0 there is a new Identity strategy which allows users to register/login with a password if they don't want to use an external provider.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Ryan,
Great screencast!
One thing though.. you did not illustrate how to remove the default OmniAuth identity pages once you've created your own.
-ra
You can't really remove the default OmniAuth identity pages because they are part of the omniauth-identity gem. You just stop using them.
i think rails should have an authentication and permission engine by default. Anyway sorcery authentication looks much more clean to me.
In that case, see: http://railscasts.com/episodes/283-authentication-with-sorcery
humm, not sure about default, what if just choose one!
Well there is
has_secure_password
, not an engine, but does most of the repeatable code. Generally the way I want my controllers and views to behave is dependent on the application. Also it adds more overhead and technical debt to the development and probably won't stack up against OmniAuth or Devise. (Haven't used others)Thanks for doing this Ryan. Again, you have read my mind by creating this. Also, thanks to Intridea for kicking ass!
You can also pass other options such as form in the initializer to override the login form. Like this:
Added a link to the 'cast to the project README. Thanks, Ryan!
If working with an app to provide email/password AND external authentication, would this be the way to go over OmniAuth/Devise in combination?
Great screencast Ryan, shows how great tool omniauth is. I just have one question, kinda in the lines of what Josh said earlier - how do you integrate this with Devise? I noticed there are no features like forgot password or confirmation emails using identity so how would you go about implementing those?
Consider the previous screencast by Ryan where he does a TDD based overview of adding in the remember me and forgot password into the first 'auth from scratch' application. I would imagine a similar approach here would work fine.
As for sending out emails etc, these can be done in the
sessions#create
action but do not sign the user in. Instead, you'd generate a confirmation token (which is emailed) and setup another general controller expecting someone to visit it with that token as a parameter - inside the action, you can handle the auth process and even automate the user login and enable whatever attributes on the user in question to indicate they've confirmed their email address.I am getting stuck on the same issue. I implemented identity just fine in a new app I am working on.. however, when I try and bake in the reset password functionality from the other video that covers that concept it does not work.
It seems that simply updating the password attributes on the identity model does not work.
Has anyone done this successfully? Mind sharing the solution you came up with?
Aaron -- I have the same question. Were you able to find a solution to this? I know that the password_reset controller and edit form aren't set properly, but it isn't clear to me how to remedy this to update the identity model with a new password. Any help would be great!
Here is my password_resets controller code, in case this is still a problem:
def update
@user = User.find_by_password_reset_token!(params[:id])
@identity = Identity.find(@user.id)
if @user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif @identity.update_attributes(params[:identity])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
Excellent screencast Ryan! Always incredibly helpful!
By the way, I created a basic formatting gem based on your gem publishing screencast called validates_formatting_of. This eliminates the need for pasting in special regex (and allows custom regex if you want). Thought I would throw that out there for you to take a look at.
+1
Good one:)
I was getting
401: Unauthorized
errors for twitter so decided to give Facebook a try. This causedThe solution was to remove the
ENV[ ]
from the initialiser for both twitter and facebook and it seems to be working fine.I personally prefer how you integrated omniauth with Devise in episode #236. Do we need to point the omniauth gem to an earlier version to keep that working?
You'll need at least Devise 1.5 to work with OmniAuth 1.0+
Hi,
I downloaded the source code, when I try to use google (and I need to use it), I get
http://localhost:3000/auth/failure?message=invalid_credentials
Any hint please?
And you're sure your credentials (key and secret) are correct and set in the environment?
Actually, it's my mistake, you are right, my credentials where wrong and I fixed it. so it'w working now. Thanks!
I'm using this, and wondering how you can tell the difference between a new registration and a login, since they both hit the same callback URL. I'd like to collect some metrics on sign-ups, but I'm not sure how I would be able to tell in the rails app.
After the user typed his information, and when you're handling the callback you just check if the authentication the user typed in exists in your database. If it doesn't, then it's a register. Is this what you meant?
Another outstanding video - thank you! A question (perhaps a novice question):
how can I enable users to register so that all of the services record their "first_name" in the user model rather than "name" which is required in the auth hash schema?
It seems that omniauth-identity requires "name" while with omniauth-facebook I can distinguish between "name" and "first_name" and "last_name" when the user model creates records.
I do not work the path "/auth/google_apps" on the form, change it to "/auth/google_oauth2" and works great!!
Hi,
i'm trying to extend this example with json response
in session controller
def create
auth = request.env["omniauth.auth"]
user = User.where(:provider => auth['provider'],
:uid => auth['uid']).first || User.create_with_omniauth(auth)
session[:user_id] = user.id
respond_to do |format|
format.html { redirect_to root_url, :notice => 'Signed in!'}
format.json { render :json => user }
end
end
Started POST "/auth/identity/callback.json"
i got error 500
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]):
app/controllers/sessions_controller.rb:10:in `create'
coresponding with this code in the session create controller
How to add json response in this schema?
i need able authorize to my site via mobile app
Based on the source code for this episode (I have yet to watch it), you made a typo, should read:
auth = env['omniauth.identity']
Can I ask a couple of questions here Ryan or others.
1) does the "sign in with google" use OAuth-2 or OpenID? Getting confused here. Reading the google pages it almost seems you need to use OpenID for "sign in using google", and OAuth to get data via google api's?
2) if I want to both (a) use "sign in with google" for authentication, and also (b) access data from a google api, then do I need to use two separate processes in rails for this? E.g. do I need to use say (a) OmniAuth to do the "sign in with google", and then (b) OAuth Plugin configured to google calendar API to get the data?
I ask this last question noting I've successfully so far used (a) OmniAuth to do the "sign in with google", and (b) OAuth Plugin to get google calendar API data.
I'm running into a strange problem with cucumber. When I run my feature that tests that a user can create an account, the feature fails with the error
No route matches [POST] "/auth/identity/register" (ActionController::RoutingError)
It does, however, work just fine if I test it in my browser. Runningrake routes
dos not show that route, but I assume that's because it's provided byomniauth-identity
through rack middleware. Does anyone have any ideas? Any Google searches I've done just bring me back here. Thanks for the great screencast!Not exactly sure what this has to do with this issue but we were having the same problem. Testing in browser worked fine but the test kept failing. We ended up removing:
OmniAuth.config.test_mode = true
from the spec/support/omniauth_support.rb file and now our tests are passing.
Hope this helps :)
Just an fyi, using Rack 1.4.0 may cause the following error,
undefined method 'include?' for nil:NilClass
. Upgrading to 1.4.1 fixed the problem.ref: Link
Does anyone know how to make the custom login form remember login details? E.g. when I type my email but with an incorrect password it now forgets about the email address.
We ctually figured out a way. Here's the Gist for those of you needing this:
https://gist.github.com/2006770
What about we don't get user email from twitter, and I want user to specify it manually like you did with devise+omniauth in ep 236?
Help!, I'm tring to create an omniauth-identity login following the screencast but I get the following error;
ActiveModel::MassAssignmentSecurity::Error
Can't mass-assign protected attributes: password, password_confirmation
That's because the protection feature called MassAssignmentSecurity is active. For a quick and dirty (very dirty) fix just comment the following line in Application.rb
config.active_record.whitelist_attributes = true
This will disable this protection for ALL your models.
You can read something more about it here: Rails Tip #7: Mass Assignment Security
I'm running into the same problem - getting the mass assignment error. I have tried to comment out that line and restarted the rails server but I still get the error. The new O'Reilly learning rails book also follows this strategy and I get the same error.
Is there a step that I'm missing?
Add
:password
and:password_confirmation
to Identity model as accessible attributes:Why would you make the
:password_digest
accessible? The:password
and:password_confirmation
are the only password attributes that should be accessible. If you open up the:password_digest
you just compromised your security model.Yes, that's true! Thanks
I'm a little bit confused. Previously you made all Omniauth authentication options exist via the authentication model. Here you seem to have broken from that by making the user the authentication provider holder. Why isn't identity just another authentication?
The challenge I see is that I want to manage roles for a user, but with the user constructed the way that it is, it would appear I have to create another variation of a user model in order to do make a user a truly singular entity (with many users instead of authentications? both users and authentications?)
@jleecbd have you figured out any more regarding the questions you ask here--I'm interested in similar topics.
Also, when you say (to Ryan) "previously you made all Omniauth authentication options exist" which RailsCast are you referring to? I'm interested in which one you mean.
Thanks
~ Brad
Sorry, I hadn't come back to look as I had found some solutions. There is another omniauth rails cast, pre-identity.
I actually ended up going the user as identity route, as multi-authentication with identity seems to cause problems. I followed this page as well for assistance: http://bernardi.me/2012/09/using-multiple-omniauth-providers-with-omniauth-identity-on-the-main-user-model/
That said, I'm currently running into a password validation bug, where I'm getting a undefined method `to_sym' for nil:NilClass in the lambda being called in the omniauth identity initializer in the event of a failed authentication. I'll be happy if and when I figure that out.
Hi,
I did all like in the guide, but on failed registration I got the following:
Details of my issue can be found at http://stackoverflow.com/questions/11506734/routing-error-no-route-matches-when-omniauth-failed-on-registration
Does anybody have any ideas what can be the reason for this?
I've played a lot with OmniAuth Identity, and in the end for the little it provides, I don't think it's worthwhile.
I found that after having to override it's views, and having to add password recovery, authentication redirection and session remember me functionality myself, I may as well just use Rails 3's has_secure_password myself. I've ditched OmniAuth Identity in favor of Rails built-in has_secure_password, and I'm much happier and find it's much cleaner and nothing is abstracted away.
Ah @Dom so I guess all the things Ryan points out in the video still haven't been added to Omniauth Identity since Dec 2011? I was assuming a number of them had probably been added since then.
Hmmm. In your opinion, would it be worth (me, or someone) adding them--in the spirit of open-source--to the OmniAuth Identity code, for everyone to use, or is it--in your opinion--just a fundamental problem over has_secure_password and probably isn't the right way to build support for something like this?
(Background: I'm mostly interested in building a internal, shared login solution (SSO) for all app services/assets in-house that will use Rails to login via a centralized login service--that we'll build. We may also provide FB/OpenID etc. auth, too, but that's not the main goal. It's mainly a 'separable' login service in-house for all our devs to use.)
You might have trouble authenticating with Google, under sessions/new.html.erb on line 30 replace:
a href="/auth/google_apps" class="auth_provider"
with
a href="/auth/google_oauth2" class="auth_provider"
I set this up exactly as the RailsCast suggested. When I'm creating an identity it populates the identity table with the login information, however, it doesn't connect this data to the user table until the user actually logs in.
According to the logs it looks for a null email then throws an error "/auth/failure?message=invalid_credentials".
I'm probably missing something very simple. The only change I made was replacing first_name and last_name with name.
Started POST "/auth/identity/register" for 127.0.0.1 at 2012-12-13 08:28:44 -0500
(57.1ms) BEGIN
Identity Exists (55.7ms) SELECT 1 AS one FROM
identities
WHEREidentities
.email
= BINARY 'email@email.com' LIMIT 1SQL (56.5ms) INSERT INTO
identities
(created_at
,email
,first_name
,last_name
,password_digest
,sha_password
,updated_at
) VALUES ('2012-12-13 13:28:44', 'email@email.com', 'first', 'last', '$2a$10$o4axANSpONo.GsIRIqpWET5FG', NULL, '2012-12-13 13:28:44')(56.8ms) COMMIT
Identity Load (55.4ms) SELECT
identities
.* FROMidentities
WHEREidentities
.email
IS NULL LIMIT 1Started GET "/auth/failure?message=invalid_credentials" for 127.0.0.1 at 2012-12-13 08:28:44 -0500
Processing by SessionsController#failure as HTML
Parameters: {"message"=>"invalid_credentials"}
Redirected to http://localhost:3000/
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)
Started GET "/assets/rails.png" for 127.0.0.1 at 2012-12-13 08:28:44 -0500
Served asset /rails.png - 304 Not Modified (0ms)
[2012-12-13 08:28:44] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Hi!
I want to add "sign in with google" to my application. I already have Twitter and Facebook based on episode 235 "Devise and omniauth" and their implementations were fair and simple.
Is it the omniauth-google_oauth2 gem I should use if I want people to be able to login with their gmail-accounts? If so, where will I find the ['GOOGLE_KEY'] and ['GOOGLE_SECRET']?. Is it by visiting https://code.google.com/apis/console/ and creating an api and using "Client ID" and "Client secret" from there? Or do I have to create some google business app or something?
I mean with Twitter and Facebook I only had to go to their dev-pages and create an app and then it was ready to use.
Or is it an entirely other omniauth gem I should use if I want people to login with gmail/google-accounts?
regards!
Made simple google sign-in work with omniauth-google-oauth2 via https://code.google.com/apis/console/
I guess there exists other gems if I have a business google app and only want people with gmail-accounts connected to my specific company to to be able to sign in. Will keep researching that and am thankful for suggestions.
Hi. Im using Rails 4 and getting errors. I have followed episode 241 and the twitter integration works perfect. In this episode Im getting:
No route matches [POST] "/auth/identity/callback"
Is it because Im using Rails 4?
Yes, there are some changes with how routes are defined in Rails 4.
Go into
config/routes.rb
and change your/auth/:provider/callback
route to accept :post requests:Thank you Dave that sorted out my ongoing problem