Authorize application, code level without hitting browser [Doorkeeper] - ruby-on-rails

I found a related question with no satisfactory answer, so asking here again:
I'm using Doorkeeper GEM for API calls for my application. I have followed the steps given in oauth2 gem docs:
require 'oauth2'
client = OAuth2::Client.new('client_id', 'client_secret', :site => 'https://example.org')
client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback')
As we see the last line execution gives a URL to be used in browser and get authorization code after clicking "Authorize".
But, I want to do all these in Rails model level, so that I don't have to hit the browser for authorization code and it should internally get the code which I can use later for token generation.
Is it possible?

It sounds like you want to use the resource owner password credentials flow for OAuth2. It is best described how to set this up with Doorkeeper and the OAuth2 gem in the Doorkeeper wiki.

Related

openid-connect client in ruby with example code

I want to read data from an API which requires authorization with openid-connect. My client should be written in ruby, so I can use it in a task to import data into my rails app.
I am given a configuration under theapi/.well-known/openid-configuration along with a client_id and a client_secret.
Having never worked with openid before I did some research an found the following gems that might help to obtain an auth token:
https://github.com/nov/openid_connect
https://github.com/m0n9oose/omniauth_openid_connect
Both gems lack of any documentation or examples to get started, in fact I am not even sure if they are useful for my case.
Also there is a similar SO-post, but it's five years old with zero answers:
Ruby Openid connect library with client consumption example
Could anybody help me out with a simple example on how to authorize against openid_connect in ruby, possibly using one of the mentioned gems?
I had the same issue and found a nice tutorial to solve this challenge FusionAuth
You require these 2 gems in your gemfile:
gem 'jwt' and
gem 'oauth2'
You will also be able to initialize the OAuth Client while replacing actual values of your Identity Server:
#oauth_client = OAuth2::Client.new(Rails.configuration.x.oauth.client_id,
Rails.configuration.x.oauth.client_secret,
authorize_url: '/oauth2/authorize',
site: Rails.configuration.x.oauth.idp_url,
token_url: '/oauth2/token',
redirect_uri: Rails.configuration.x.oauth.redirect_uri)
With the OAuthClient initalised you can be able to perform your desired OAuth Flow.
For Authorization with password_credentials you can use:
response = #oauth_client.client_credentials.get_token
token = response.token
You can find more Authorization Type References here: OAuth2 Docs
These two resources should enable you to complete your integration with any IdentityServer. Hope these resources stay updated.

Learning Doorkeeper

I am trying to set up APIs for a broker application (users can buy/sell items when they are logged in). Developers should be able to build on top of my app. Aiming to use Devise and Doorkeeper gems. I have already set up Devise (so my users have to log in/log out of their accounts). But I am having problems understanding how to use Doorkeeper gem.
I have read through the following so far
RFC 6749
Doorkeeper wiki
Tutorial for Oauth 2 on Rails (Wasn't very helpful as I got stuck while following the steps)
Oauth with Doorkeeper railscast
Problem is - I still don't quite get how to set up my servers, create pages which other developers can register their app on, attain their keys,etc.
What am I missing, is there any comprehensive tutorial which I can learn from to set up my API for developers to register their app on, login on behalf of users and execute buy/sell orders?
Registering applications
Doorkeeper comes with integrated controllers/views to manage oauth applications, request access tokens and authorizations.
If you installed and configured doorkeeper correctly, these routes are defined in your rails application :
GET /oauth/authorize/:code
GET /oauth/authorize
POST /oauth/authorize
DELETE /oauth/authorize
POST /oauth/token
POST /oauth/revoke
resources /oauth/applications
GET /oauth/authorized_applications
DELETE /oauth/authorized_applications/:id
GET /oauth/token/info
(See https://github.com/doorkeeper-gem/doorkeeper#routes)
When you go to /oauth/applications, you can add or remove oauth applications. This might help for your problem
create pages which other developers can register their app on, attain their keys
However, these are made for backend or quick setup purposes. It's not recommanded to use that in production. You can create controllers/views based on these to start with.
If you want to learn more about customizing these controllers/views, check these links :
https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes
https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-views
Setting up your API
If you don't use the Rails API mode, I recommend you use the grape gem, which is a framework for building APIs in Ruby. You can then mount your Grape::API application to a route of your Rails application.
Then, when users will register their applications, they will be asking for access grants (authorization code) for each individual user of their own applications. The /oauth/authorize routes are exactly that. They will use an OAuth2 client to build the authorize_url properly and setup their applications.
All this flow is compliant with the OAuth2 framework (RFC 6749). But as you said; you read it, so you should understand what's going on behind the scene.
All you have to do is provide your API endpoints :
# in your API class that extends Grape::API
post 'orders/:id/buy' do
# authorize a specific scope, this is just an example,
# this might not suit your app design
doorkeeper_authorize! :buy_order
# you can get the resource owner id with and other token infos,
# or put that in a helper method
current_user = User.find(doorkeeper_token.resource_owner_id)
# buy order logic goes here...
end
post 'orders/:id/sell' do
doorkeeper_authorize! :sell_order
current_user = User.find(doorkeeper_token.resource_owner_id)
# sell order logic goes here...
end
Hope that can help!

Using the new Devise for authentication for a mobile app

I am trying to implement user authentication using Devise for my Rails/iOS app. I am having trouble since I've mostly been a "user" of Devise and was using it for pure web apps so didn't need to bother so much with what goes on behind the scenes. Now that I have to build authentication for an API based app, it's entirely a different world.
I've read every single tutorial on the web that deals with this topic (most of them are outdated due to the fact that token_authenticatable has been deprecated) but still having trouble understanding what I need to do.
I also read the original GitHub gist talking about this issue and still don't understand what they are talking about.
I'm sure there are people out there just like me who've been just a "user" of Devise so don't really know what goes on behind the scenes.
Can anyone provide a concise solution to implementing an API based authentication system for a mobile app? I mean it can't be that complex, Devise used to be so awesome since all i needed to do was run rails generate, but this has been nightmare for me.
I am working on same things as you want,
for this you have to use token authentication rather than simple
Devise, add following gem in gemfile
# Use device for authentication
gem 'devise'
gem 'simple_token_authentication'
follow documention simple_token_authentication
Use Api like this
curl -v https://example.com/users/sign_in -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{"user": {"login": "7838712847" ,"password": "8489", "mobile_type": "ios", "mobile_key": "APA91bG6G4UvjeCWvb8mMAH1ZO3I-8FB3nkRPyCHnwZiXgd16HK18GgoV5n7gjJtZNs038iaFGutzdxnhes3WyaXEX52-xmOyvuEK8S1abBdpuhD9AD5bzLWeu-1Ow_yZRTVg3Nypz1z"}}'
I am using mobile number to login so customize gem according your need
please let me know if it is not working (mail me: er.mukeshsharma.rj21#gmail.com)
Here is an approach that works excellent for me, when using Devise for authentication in a Rails app. If tests for a token first (you can set the token by any iOS, Android, ... app) and falls back to the default authentication method for your web users.
Rails
Add your own token to the user model, by adding an :api_token string column and fill that with a unique value per user. Using a Digest::SHA1 of some user data (like id + email) is a good starting point, but you can (and should) go as crazy as you like when it comes to generating a unique token.
Create a method for authentication over that token. You can add it to your main ApplicationController for easy access (don't forget to put the method in your private section of the controller);
def authenticate_user_by_token
#api_token = request.headers['HTTP_AUTHORIZATION']
if #api_token.present? && #user = User.find_by_api_token(#api_token)
sign_in #user
return #user
else
return false
end
end
Next create a (private) method and chain this method to the devise before filter method you are using (like :authenticate_user! for example). Put it in the same controller as the method above for easy access;
def authenticate_by_token_or_devise!
return authenticate_user! unless authenticate_user_by_token
end
Now Replace your current before_filter call from :authenticate_user! to the newly created one; :authenticate_by_token_or_devise!, like so;
before_filter :authenticate_by_token_or_devise!
Or, starting from rails 4 (Rails 4: before_filter vs. before_action), use before_action;
before_action :authenticate_by_token_or_devise!
iOS
Now all you have to do is add that token to your iOS app. Depending on the framework that you use in your app, this might be different then the code below.
I use AFNetworking (https://github.com/AFNetworking/AFNetworking) in this example. This is how you set the Authorisation header token in your AFHTTPRequestOperationManager so it gets added to every request you make.
NSString *apiToken = #"your-token-here";
[[_manager requestSerializer] setValue:apiToken forHTTPHeaderField:#"Authorization"];
Optional
Additionally, you can create a before filter method that allows access to token-based authentication only (e.g. if you have a set of /api routes that you only want to be accessed using the token) like this;
def authenticate_user_by_token!
if !authenticate_user_by_token
render nothing: true, status: :unauthorized and return
end
end
Recently, we also had to set up token based authentication for our webapp (for API access) - and we also stumbled upon the fact that it has been removed from Devise.
We went with Simple Token Authentication which worked just beautifully.
When I recently implemented an API, I grudgingly followed a suggestion to use Warden, a rack-based authentication gem. My sense was that an authentication gem that required you to write your own authentication was broken. But this gem provides just the right level of control. My only complaint is that the gem don't handle POST parameters well. I was able to work around it, but that kind of concern should be (IMO) handled by the gem.
Having used it, I highly recommend this gem for any scenario requiring non-generic authentication. Rolling your own authentication strategies is a joy because (a) it's pretty simple and (b) you aren't bound by other devs' assumptions.
To help you get started, here is my config/initializers/warden.rb file.
You can use a combination of the devise gem and doorkeeper gem to support web and mobile authentication.
For example, I used devise for signing up users and handling forget password and email confirmation flow. For mobile clients, I used the doorkeeper gem as a oauth2 provider to protect my apis. There are many oauth2 grant flows supported by the doorkeeper gem and I suggest you can take a look at those.
Here's a link! to get started

Flickr and Flickraw gem authentication

I'm trying to use the Flickraw gem to upload some images to my account in Flickr. I get this to work, but how to get the access code, without to visit Flickr page, like this:
token = flickr.get_request_token
auth_url = flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
puts "Open this url in your process to complete the authication process : #{auth_url}"
puts "Copy here the number given when you complete the process."
verify = gets.strip
...
I donĀ“t understand this process.
This is because the auth process is done using OAuth, explained here: http://www.flickr.com/services/api/auth.oauth.html
It's basically making the request to the flickr server getting an access token for the currently signed in user and returning that to an call back URL on your server(The docs explain it way better and in much more details so I recommend reading that instead).
If however you want to use the application only in your name you can look into other authentication mechanisms, explained here: http://www.flickr.com/services/api/auth.spec.html
The one you want to use - as it looks to me - is the non web based authentication. This is a much easier way to authenticate and only uses a key and secret for authentication. Which also seems to be supported by the FlickRaw gem: http://hanklords.github.io/flickraw/ (Look at the first simple example)

How to use omniauth to make authenticated calls to services?

I've received a token / secret from a service using OmniAuth and can store it for users, but I'm stuck as to how to actually use these to call a service.
The closest thing I've seen to this question is here but the way he's solved that there doesn't feel right. I feel like OmniAuth likely does this all for you if you know what you're doing.
Netflix has a pretty involved auth process, so I was hoping to skirt all of this by using OmniAuth to abstract me from all of this.
Given that I have a token and secret for a user, how to use these in calling a service like Netflix?
Many thanks :)
Hey, I'm the author of the OmniAuth gem. OmniAuth is meant to be used for the authentication process. In the case of OAuth providers like Netflix, this means exchanging a request token for an access token which is then used to pull user information from the API. These one-off calls are specifically designed for each provider and are not meant to be a generic API client for the given provider.
What you can do it use OmniAuth to obtain the credentials and then use another specific library for the site itself (such as ruby-netflix or anything else, I'm not sure what the best one is) to make calls. You can retrieve the access token and secret that is obtained in the authentication dance by accessing env['omniauth.auth']['credentials'], then use those to initialize the API client.
You can also use the OAuth library directly to make these calls, but I would strongly recommend just using an existing library, it will be much faster and easier. Does all of that make sense?
OmniAuth is all about authentication; you should probably look at another gem for making actual calls to the service. E.g., for Facebook, I use the OAuth2 gem and code like the following:
module Facebook
class Client < OAuth2::Client
# Return a new OAuth2::Client object specific to the app.
def initialize
super(
APP_CONFIG[:facebook][:api_key],
APP_CONFIG[:facebook][:app_secret],
:site => 'https://graph.facebook.com',
:parse_json => true
)
end
end
class Token < OAuth2::AccessToken
# Return a new OAuth2::AccessToken specific to the app
# and the user with the given token.
def initialize(token)
super(
Facebook::Client.new,
token
)
end
end
end
access_token = Facebook::Token.new(users_fb_token)
url = "https://graph.facebook.com/#{user_fb_id}/feed"
response = access_token.post(url, :message => "My update")
Note that there are gems for popular services, like Facebook and Twitter, that can manage the behind-the-scenes things like creating tokens, managing URLs, etc. For Netflix, you might check the following:
https://github.com/tiegz/ruby-netflix
https://github.com/rares/netflix
http://code.google.com/p/flix4r/
Also keep in mind that OmniAuth just returns the service data to you; you're free to store it and use it how you will (Devise has it's own pattern for OmniAuth that you might butt heads with if you try to go outside the lines). The other question you linked doesn't look too far fetched to me.

Resources