I have backend app with devise and a ionic app from which I make requests to backend.
Whenever I send requests, gets me 401 status unauthorized.
In this project I've already doorkeeper to manage authorization, so I don't rly need authorization from devise. Can I somehow make these requests( add something to headers ) always authorized without sending post request with credentials?
You need to identify the user somehow. If you're not using a session, you'll need to generate some kind of access token, which Doorkeeper can do for you. I'm not sure if this is helpful or not, but I set up the following flow recently.
One option when using OAuth2 through a trusted client, e.g. a front-end app you build/distribute yourself, is the Resource Owner Password Credentials Grant. Doorkeeper has a guide in the docs for this with advice on dealing with Devise.
I ended up with something like this in my Doorkeeper initializer. You don't need to authorize the client, because you trust it:
resource_owner_from_credentials do |routes|
request.params[:user] = {:email => request.params[:email], :password => request.params[:password]}
request.env['devise.allow_params_authentication'] = true
request.env['warden'].authenticate!(:scope => :user)
end
skip_authorization do |resource_owner|
true
end
Then you should be able to send a request using the password grant type as follows (also shown in the docs).
RestClient.post 'http://localhost:3000/oauth/token', {grant_type: 'password', email: 'email#gmail.com', password: 'password'}, {:accept => 'application/json'}
You should receive the same JSON back as shown in the docs.
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}
Now you have a way of identifying your user. You just attach this token to each request to endpoints protected by doorkeeper_for.
RestClient.get 'http://localhost/api/v1/users', { 'Authorization' => 'Bearer 1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa', :accept => 'application/json'}
Related
I am using OAuth2 gem, for making a client_credential authentication. My code is as below,
require 'oauth2'
client = OAuth2::Client.new("my_client_id", "my_client_secret", :site => "my_site_url", :token_url => "oauth2/token")
client.client_credentials.get_token
When I execute above code block, it respond with below error,
OAuth2::Error (invalid_client: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method))
{
"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)",
"error_hint":"The OAuth 2.0 Client supports client authentication method "client_secret_basic", but method "client_secret_post" was requested.
You must configure the OAuth 2.0 client's "token_endpoint_auth_method" value to accept "client_secret_post".","status_code":401}
I checked the using 'net/http' library, and my client_id & client_secrets are valid and working.
The only problem I see is with the authentication method as said in hint of above message,
The OAuth 2.0 Client supports client authentication method "client_secret_basic", but method "client_secret_post" was requested. You must configure the OAuth 2.0 client's "token_endpoint_auth_method" value to accept "client_secret_post"
What I want to know is?
How OAuth2 gem decide on using client_secret_post vs client_secret_basic? I mean How can I request with client_secret_basic in OAuth2 gem?
If not above then, How should I specify token_endpoint_auth_method to accpet client_secret_post?
OK, so finally I cleared these points.
OAuth2 gem does make a request to OAuth server with --token_endpoint_auth_method set to 'client_secret_post'.
While registering an client with OAuth server we will have to set token_endpoint_auth_method to 'client_secret_post', so that it will work.
In my case I was using Hydra, so I used below command to create a client:
hydra clients create --endpoint <OAuth server url> --id CLIENT_ID --secret CLIENT_SECRET \
--token-endpoint-auth-method 'client_secret_post' -g client_credentials
Now, using these CLIENT_ID and CLIENT_SECRET with oauth2 works.
But still one point which is unclear - can I make a request with token_endpoint_auth_method set to client_secret_basic using oauth2 gem.
I also encountered the same issue.
Please add or change this client option setting in your client code.
:auth_scheme => :basic_auth
The default settings is below.
:auth_scheme => :request_body
I have excerpted a part of the OAuth2::Client code.
Please check it.
require 'faraday'
require 'logger'
module OAuth2
# The OAuth2::Client class
class Client # rubocop:disable Metrics/ClassLength
attr_reader :id, :secret, :site
attr_accessor :options
attr_writer :connection
# #option opts [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
def initialize(client_id, client_secret, options = {}, &block)
opts = options.dup
#id = client_id
#secret = client_secret
#site = opts.delete(:site)
ssl = opts.delete(:ssl)
#options = {:authorize_url => '/oauth/authorize',
:token_url => '/oauth/token',
:token_method => :post,
:auth_scheme => :request_body, # <-- Here !!!
:connection_opts => {},
:connection_build => block,
:max_redirects => 5,
:raise_errors => true}.merge(opts)
#options[:connection_opts][:ssl] = ssl if ssl
end
Sample snippet is here https://gist.github.com/mtoshi/cd74f57631805fb1b2290137f58dac9f
If you use a middleware it probably use the client_secret_basic to make a request you only need to change the configuration of that something similar to this. I use nextauth middleware.
client: {
token_endpoint_auth_method: 'client_secret_post'
}
Currently I have an access token api with username, password and grant_type as password in my request in rails using doorkeeper. But I need to make client_id and secret as mandatory fields in the request. How can I do that. Can anyone please help to make this.
In my doorkeeper.rb config file,
resource_owner_from_credentials do |routes|
#client = OAuth2::Client.new(request.params[:client_id], request.params[:client_secret], site: "http://localhost:3000/")
#auth_url = client.auth_code.authorize_url(:redirect_uri => "urn:ietf:wg:oauth:2.0:oob")
request.params[:user] = {:email => request.params[:username], :password => request.params[:password]}
request.env["devise.allow_params_authentication"] = true
request.env["warden"].authenticate!(:scope => :user)
end
I want to authenticate using user credentials and also want to make client_id and secret a required field. I want to show a message if the client_id and secret is missing.
Inside the block, you can check the presence of params[:client_id] and params[:client_secret], and do the necessary check to make sure that they are valid :)
resource_owner_from_credentials do |routes|
raise Doorkeeper::Errors::DoorkeeperError if params[:client_id].blank? || params[:client_secret].blank?
dk_app = Doorkeeper::Application.find_by(uid: params[:client_id])
raise Doorkeeper::Errors::DoorkeeperError if dk_app.blank? || dk_app.secret != params[:client_secret]
## here do some checking that the client_id and secret are valid
request.params[:user] = {:email => request.params[:username], :password => request.params[:password]}
request.env["devise.allow_params_authentication"] = true
request.env["warden"].authenticate!(:scope => :user)
end
if you need to change the error message to a custom one you can refer to this issue
You can add this code to your doorkeeper.rb config file,
# Doorkeeper patch: Always require a client on resource owner password flow
Doorkeeper::OAuth::PasswordAccessTokenRequest.class_eval do
private
def validate_client
!!client
end
end
It makes sure that the client application is always required for the password flow. Then the client_id and the client_secret are validated internally by Doorkeeper. If they are invalid the default error message from Doorkeeper for that case is provided.
Monkey patching is always ugly, but since Doorkeeper doesn't really allow to customize natively this behaviour I think it's a valid solution for now.
I am currently in Step 3 of the processing on getting an oauth token/secret from an user trying to login via Twitter. https://dev.twitter.com/docs/auth/implementing-sign-twitter
Step 3 tells me to send this request to the API, but I am stuck as to how to do so. I currently have BOTH the oauth_token and oauth_verifier, but how do I send this POST request to get the oauth_token, oauth_token_secret pair?
Is there a standard Oauth Ruby gem I can use to send this POST request? I see examples online where I pass an #accessToken object, but i do not have such an object available. I just have the oauth_token and oauth_verifier (as strings). Given these 2 things, how do I convert them to an oauth_token and oauth_token_secret?
POST /oauth/access_token HTTP/1.1
User-Agent: themattharris' HTTP Client
Host: api.twitter.com
Accept: */*
Authorization: OAuth oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w",
oauth_nonce="a9900fe68e2573b27a37f10fbad6a755",
oauth_signature="39cipBtIOHEEnybAR4sATQTpl2I%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318467427",
oauth_token="NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0",
oauth_version="1.0"
Content-Length: 57
Content-Type: application/x-www-form-urlencoded
oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY
Try something like the following rails controller actions, using the twitter and oauth gems:
def redirect
consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, {
:site => "https://api.twitter.com",
:scheme => :header
})
request_token = consumer.get_request_token(:oauth_callback => CALLBACK_URL)
session[:twitter_request_token] = request_token
redirect_to request_token.authorize_url #=> "https://api.twitter.com/oauth/authorize?oauth_token=XYZ"
end
def callback
request_token = session[:twitter_request_token]
access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
client = Twitter::REST::Client.new(
:consumer_key => CONSUMER_KEY,
:consumer_secret => CONSUMER_SECRET,
:access_token => access_token.token,
:access_token_secret => access_token.secret
)
twitter_user = client.user
redirect_to root_url # or do something with the twitter_user
end
See also: http://barkingiguana.com/2009/10/13/twitter-oauth-authentication-using-ruby/
yes there is the Omniauth gem for authentication with Twitter. The documentation is straight forward.
I personally use Omniauth integrated with Devise and the Twitter gem to access Twitter - works very well.
Hope this helps,
Eugen
The common procedure is the following:
You shell to register your app on twitter development page.
Then set the proper Name, Description, and Website values up for your application.
App Name
App Description
http://your_app_domain.zone:3000/
Change Application Type is your app, by default it has read only access type.
Setup the callback URL for yuor application:
http://your_app_domain.zone:3000/auth/twitter/callback
Store the all keys, and secrets that are shewn on the OAuth tool twitter page:
Consumer key:
Consumer secret:
Access token:
Access token secret:
Setup route on your site with devise, or devise-like gem with the specified twitter keys, and secrets to enable authentication engine. The route list now shall include /auth/twitter path.
By going to http://your_app_domain.zone:3000/auth/twitter you will be redirected to twitter site, and dropped back to your site with passed oauth_token
But
You simple receive those keys, and secrets, and apply then in your app, avoiding the 6, and 7 points:
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end
I saw many questions about the same problem, but I just cannot find a way around so I'm asking a question to be sure I did not miss a thing.
On twitter, I set the callback_url to 'https://my_app_url/'.
# consumer
#consumer = OAuth::Consumer.new('consumer_id',
'secret_key',
:site => 'https://api.twitter.com')
# request_token
#request_token = #consumer.get_request_token(:oauth_callback => "https://my_app_url/?state=#{state}")
# then get the user to log in via
login_url = #request_token.authorize_url
# I have a params[:oauth_token] and params[:oauth_verifier] in return
# I don't know what the oauth token is for
# I then should be able to get an access token
# all oauth calls are done in an object so #request_token has been conserved.
#access_token = #request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
# then it happens.
# => 401 Unauthorized
I have no idea why the 401 is being raised. Is there a step I have been missing?
I would appreciate any help.
get_request_token gives you temporary OAuth tokens to authenticate you during the OAuth authentication flow. The OAuth verifier is something that proves that the user authorized the application. You have to give this last to get_access_token in order to get the final OAuth tokens that will be used for all your authenticated requests.
For further informations, consider reading the official Twitter documentation dealing with this : https://dev.twitter.com/docs/auth/using-oauth (https://dev.twitter.com/docs/auth/implementing-sign-twitter for the description of the process)
after i added an application in twitter when i request for an authentication in twitter (/auth/twitter) i get these error message
http://localhost:3000/auth/failure?message=invalid_credentials
Routing Error
No route matches "/auth/failure"
how can i add a valid credential or is there any ssl certificate that must be included in requesting for twitter auth??
my facebook authentication just works fine after i added a parameter ssl certificate that looks like this
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook,'xxx', 'xx', { :scope => 'publish_stream,offline_access,email',:client_options => { :ssl =>{ :ca_path => "/etc/ssl/certs" } }}
provider :twitter, 'xx','xxx' #,{ :client_options => { :ssl =>{ :ca_path => "/etc/ssl/certs" } }}
end
i've same problem with you, it happen because oauth_token in twitter login only valid on once request. May be your application trying to refresh when authentication to twitter.
when i've problem like you, my apps trying to refresh the webpage with
window.opener.location = "#{request.fullpath}";
Until now i'couldn't find how to popup a window when login using twitter. I'm using omniauth and rails 3.0.3. Thanks
This is all you need
site url http://localhost:3000/