I'm writing an API aside with an web page. I want to have Oauth2 login so i can keep a session from the mobile device, i've already install the GEM doorkeeper and run the migration how it's explained on this site.
where i'm getting stuck is in the resource_owner_from_credentials parts, since i have an User model which has authenticate method given by the has_secure_password helper from rails. this is how my /config/initializers/doorkeeper.rb file looks like
Doorkeeper.configure do
# Change the ORM that doorkeeper will use.
# Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_from_credentials do
User.find_by_email(params[:email]).authenticate(params[:password])
end
##lots of comments
end
and when go to
localhost:3000/oauth/authorize
i get this:
config/initializers/doorkeeper.rb:8:in `block (2 levels) in <top (required)>'
then i tried:
http://127.0.0.1:3000/oauth/authorize?email=puca#gmail.com&password=porche
and the same
what i'm i doing wrong? how should i configuresource_owner_authenticator block? how do i get the token?
As per this doorkeeper wiki page you need to send a POST request to /oauth/token API with the following params:
{
"grant_type" : "password",
"username" : "user#example.com",
"password" : "sekret"
}
When this request is processed, doorkeeper calls the resource_owner_from_credentials block and passes the params to it. So you have access to the param named username and not email.
To summarize, fix the API endpoint to /oauth/token , change params[:email] to params[:username] and everything should work.
Related
I have a rails(4.2.0) application that uses Facebook login functionality. The main gems are devise(3.4.0) and omniauth-facebook(2.0.0). I have registered the application on Facebook and have been using its test app for development. The Facebook login functionality works in the development env.
When trying to use the facebook login feature on the production server, I get error as "Given URL is not allowed by the Application configuration: One or more of the given URLs is not allowed by the App's settings. It must match the Website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains."
The details for settings for test app being used in the dev env are as -
Settings:
Basic:
App Domains: 'localhost'
Website:
Site URL: 'http://localhost:3000'
Advanced:
OAuth Settings:
Embedded browser OAuth Login: Yes
Valid OAuth redirect URIs: "http://localhost:3000/users/auth/facebook/callback"
The details for settings for registered app being used in the production env are as -
Settings:
Basic:
App Domains: 'www.mysite.co'
Website:
Site URL: 'http://www.mysite.co'
Advanced:
OAuth Settings:
Embedded browser OAuth Login: Yes
Valid OAuth redirect URIs: "http://www.mysite.co/users/auth/facebook/callback"
I have specified the following in my secrets.yml
development:
secret_key_base: some_secret_key
facebook:
app_id: test_app_id
app_secret: test_app_secret
production:
secret_key_base: some_secret_key
facebook:
app_id: registered_app_id
app_secret: registered_app_secret
And have been using the creds from secrets.yml in the devise initialiser as
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
require 'omniauth-facebook'
config.omniauth :facebook, Rails.application.secrets.facebook['app_id'], Rails.application.secrets.facebook['app_secret'], scope: ['user_photos', 'email', 'public_profile']
The actual domain name(blackened) has no typos anywhere and is same wherever it is used.
Contains of routes.rb related to omniauth are as
cat config/routes.rb
Rails.application.routes.draw do
root 'home#index'
devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
# routes related to other controllers
end
The routes are as below
bundle exec rake routes | grep user
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_omniauth_authorize GET|POST /users/auth/:provider(.:format) users/omniauth_callbacks#passthru {:provider=>/facebook/}
user_omniauth_callback GET|POST /users/auth/:action/callback(.:format) users/omniauth_callbacks#:action
The only code related to omniauth in the entire app is as
$ cat app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
#You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, event: :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
Upon further digging the problem, it was observed that the error didnt occur when 'www.example.com' was specified in the url and hence, the callback worked. When 'example.com' was specified in the address bar and facebook login tried, the login crashed with the above error.
So, I fixed the above issue by making some changes to the settings in for the facebook app. I donno if this is the right approach but it worked out. Just making the change as in point 2 didnt solve the problem.
Changes are:
1) Specified the 'App Domains' with 'example.com' and 'www.example.com'
2) Enabled 'Client OAuth Login' to 'Yes'
3) Specified 'Valid OAuth redirect URIs' with 'http://example.com/users/auth/facebook/callback' and 'http://www.example.com/users/auth/facebook/callback'
Ok, so I assume that you have a web app NOT running on Facebook that simply uses the Facebook OAuth flow for login functionality, correct? If so, you must enable "Client OAuth Login" in your application settings for the production environment. If you don't, then the web OAuth flow will not work. See this article: https://developers.facebook.com/docs/facebook-login/security
In Facebook login view an user can decline permissions. Perhaps the permissions which were declined affect to the app functionality (you can do less things with this app because some data is not accessible)
I would like warn to the user and re-ask the permissions again. But I don't see what is the way with Omniauth-Facebook. In Facebook documentation shows the way to accomplish this is adding a parameter to the request login called: auth_type=rerequest
https://www.facebook.com/dialog/oauth?
client_id={app-id}&
redirect_uri={redirect-uri}&
auth_type=rerequest&
scope=email
I have not seen the way to do this directly with Omniauth I tried to call this url from 'auth/facebook/callback' after checking the permissions by myself, but I get the following error when the user re-accepts the permission in the new login:
ERROR -- omniauth: (facebook) Authentication failure! invalid_credentials: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
OmniAuth::Strategies::OAuth2::CallbackError: csrf_detected | CSRF detected
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/failure_endpoint.rb:25:in `raise_out!'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/failure_endpoint.rb:20:in `call'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/failure_endpoint.rb:12:in `call'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/strategy.rb:475:in `fail!'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-oauth2-1.2.0/lib/omniauth/strategies/oauth2.rb:73:in `callback_phase'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-facebook-1.6.0/lib/omniauth/strategies/facebook.rb:71:in `callback_phase'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/strategy.rb:227:in `callback_call'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/strategy.rb:184:in `call!'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/strategy.rb:164:in `call'
/home/ciro/.rvm/gems/ruby-2.1.3/gems/omniauth-1.2.2/lib/omniauth/builder.rb:59:in `call'
This is my 'auth/facebook/callback'
on get do
on 'auth/facebook/callback' do
on param(:code) do |code|
email = req.env['omniauth.auth']['info']['email']
if email.nil? or email.empty?
res.redirect "https://www.facebook.com/dialog/oauth?client_id=#{ENV['APP_ID']}&redirect_uri=http://localhost:9292/auth/facebook/callback&auth_type=rerequest&scope=email"
end
end
end
end
The solution is to pass the parameters in the OmniAuth request:
if email.nil? or email.empty?
res.redirect "/auth/facebook?scope=email"
end
I want a password site wide just like Rack's Basic AUTH
/config.ru
use Rack::Auth::Basic, "Restricted Area" do |username, password|
[username, password] == ['admin', 'admin']
end
run Rails.application
But I don't want it to block paths /API and /mailgun/incoming_email with password access. Can I accomplish this in Rack? Or should I implement a scope within the routes.rb that almost all resources are behind a Rack (enter once) password?
For the record I am using Devise within the site... that's separate. I need a sitewide password before it.
[Revised Question]
Specific Routes
I would like to password protect only the root path / and /visitors with the Rack like password. I've seen something used in a Rails routes.rb file before with a lambda condition requiring the password. I'm not having luck finding that information at the moment.
My website already redirects unauthenticated Devise users to /users/sign_in. So I only needed to password protect /, /users/sign_in, and /users/sign_up. This is how I did it.
config.ru
class RootSiteAuth < Rack::Auth::Basic
def call(env)
request = Rack::Request.new(env)
if ['/', '/users/sign_in', '/users/sign_up'].include? request.path
super
else
#app.call(env)
end
end
end
use RootSiteAuth, "Restricted Area" do |username, password|
[username, password] == ['admin', 'admin']
end
run Rails.application
And it works. Every controller that has before_filter :authenticate_user! redirects to the Rack password page. After authentication we're good to go. Anything without the filter permits outside access as planned. ^_^
Goal: I'm trying to get a Ruby on Rails application to send me emails whenever a user fails to log into OmniAuth. I want the e-mail to include (1) the username entered in the form, and (2) an MD5 hash of the password field.
Obstacle: OmniAuth returns a POST after a successful login, and a GET after an authentication failure. The "success" POST includes the username and a filtered password, but the "fail" GET does not include these two parameters.
So I guess my question is "Can I make OmniAuth return the parameters I want? If not, how can I make Rails remember the form data after it gets POST'ed to OmniAuth?"
I emailed the OmniAuth team and they gave me the solution below (thank you so much!):
You can do custom failure handling by adding an on_failure action.
OmniAuth.config.on_failure = Proc.new { |env| #do stuff }
https://github.com/intridea/omniauth/blob/master/lib/omniauth/failure_endpoint.rb
is the default failure endpoint as an example
So I added the following in config/initializers/omniauth.rb:
OmniAuth.config.on_failure = Proc.new{|env|
myLog = ActiveSupport::TaggedLogging.new(Logger.new("log/omniauth_log.txt"))
myLog.tagged("OmniAuth", "ENV") { myLog.info "Failed login attempt - username: #{env["rack.request.form_hash"]["username"]}, password: #{env["rack.request.form_hash"]["password"]} "}
OmniAuth::FailureEndpoint.new(env).redirect_to_failure}
...and it records the username and password correctly. All that's left to do is encrypt the password.
If you want to display everything that's going on, you can log #{env.inspect} itself. It's a very large hash though (that also contains smaller hashes), so maybe log #{env.inspect} once and pick out the fields relevant to your task.
I'm using Django-socila-auth plugin. It uses google API for Oauth 1.0 Authentication. Question is have anybody used it with google python API (gdata). I mean how to apply auth session_token, stored in django-social-auth model to my api call.
Can you help me with code to get this token from model and apply to gdata.PhotoService() instance. For now it is like this:
#getting model instance from django-social-auth model
association = Association.objects.get(user=request.user)
google_session_token=association.handle
google_secret=association.secret
#token string from django-social-auth
#model Association field "handle" looks like:
#google_session_token = '.......XG84PjwytqJkvr8WQhDxm1w-JplWK5zPndSHB13f.........'
gd_client = gdata.photos.service.PhotosService()
gd_client.debug = 'true'
gd_client.auth_token = google_session_token
#image.image is a file field, but problem not in this.
#it tries to send file in debug text.
#It just recieves 403 unauthorised callback.
photo = gd_client.InsertPhotoSimple(
'/data/feed/api/user/default/albumid/default', 'New Photo',
'Uploaded using the API', image.image, content_type='image/jpeg')
I'm recieving error
403 Invalid token string.
I understand that it needs secret too but how to apply it to API for auth?(To receive authorization to post photos.). BTW I added Picassa feed URL, as an option string for social-auth to ask permissions, so token I have asks for Picassa feed permissions when authorizing with google.
BTW. Google tutorial I've used is: here
I understand it's Oauth 1.0 rather than AusSub, but question is:
how to authenticate with token and secret I have and post a photo with this permission?
Just to answer my own problem. I used wrong way to do it, because problem in 'gd_client' and AuthSub.
It must check token on server. And it can not do it on localhost. You need to look ahead to Oauth/Oauth2 for better debugging and so on... No matter that it is much complex than AuthSub