Currently I am working on rails 4 project, and now I have to link / connect another application (not sso but for accessing API's) say example.com. (Note: example.com uses 3-legged oauth security architecture)
After searching found that I have to implement omniouth strategy.
For this I have refereed this link. As per Strategy-Contribution-Guide I am able to complete setup and request Phase, You can find my sample code here.
require 'multi_json'
require 'omniauth/strategies/oauth2'
require 'uri'
module OmniAuth
module Strategies
class MyAppStrategy < OmniAuth::Strategies::OAuth2
option :name, 'my_app_strategy'
option :client_options, {
site: site_url,
authorize_url: authorize_url,
request_url: request_url,
token_url: token_url,
token_method: :post,
header: { Accept: accept_header }
}
option :headers, { Accept: accept_header }
option :provider_ignores_state, true
def consumer
binding.pry
::OAuth::Consumer.new(options.client_id, options.client_secret, options.client_options)
end
def request_phase # rubocop:disable MethodLength
binding.pry
request_token = consumer.get_request_token({:oauth_callback => callback_url}, options.request_params)
session["oauth"] ||= {}
session["oauth"][name.to_s] = {"callback_confirmed" => request_token.callback_confirmed?, "request_token" => request_token.token, "request_secret" => request_token.secret}
if request_token.callback_confirmed?
redirect request_token.authorize_url(options[:authorize_params])
else
redirect request_token.authorize_url(options[:authorize_params].merge(:oauth_callback => callback_url))
end
rescue ::Timeout::Error => e
fail!(:timeout, e)
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
end
def callback_phase # rubocop:disable MethodLength
fail(OmniAuth::NoSessionError, "Session Expired") if session["oauth"].nil?
request_token = ::OAuth::RequestToken.new(consumer, session["oauth"][name.to_s].delete("request_token"), session["oauth"][name.to_s].delete("request_secret"))
opts = {}
if session["oauth"][name.to_s]["callback_confirmed"]
opts[:oauth_verifier] = request["oauth_verifier"]
else
opts[:oauth_callback] = 'http://localhost:3000/auth/callback' #callback_url
end
@access_token = request_token.get_access_token(opts)
super
rescue ::Timeout::Error => e
fail!(:timeout, e)
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
rescue ::OAuth::Unauthorized => e
fail!(:invalid_credentials, e)
rescue ::OmniAuth::NoSessionError => e
fail!(:session_expired, e)
end
def custom_build_access_token
binding.pry
verifier = request["oauth_verifier"]
client.auth_code.get_token(verifier, get_token_options(callback_url), deep_symbolize(options.auth_token_params))
end
alias_method :build_access_token, :custom_build_access_token
def raw_info
binding.pry
@raw_info ||= access_token.get('users/me').parsed || {}
end
private
def callback_url
options[:redirect_uri] || (full_host + script_name + callback_path)
end
def get_token_options(redirect_uri)
{ :redirect_uri => redirect_uri }.merge(token_params.to_hash(:symbolize_keys => true))
end
end
end
end
I am able redirect to example.com, also after login I am able to return to my callback_phase (you will ask how did you know, so answer is I have added binding.pry
in callback_phase method for checking the flow).
But after executing the strategy I am getting following error
ERROR -- omniauth: (my_app_strategy) Authentication failure! invalid_credentials: OAuth2::Error.
After debugging found that I am getting this error for the super
call (from callback_phase method).
First I though may be there are some credentials issue but I am able fetch access token using following (which is executing before the super
call)
@access_token = request_token.get_access_token(opts)
Also for more information I am getting error for build_access_token
which is the oauth2 method
You can refer this link for more info (just search the build_access_token on the page).
EDIT - 1
After debugging found that getting this issue from the request method. (While making the faraday request). Here is the code snippet
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
yield(req) if block_given?
end
Here is my faraday request
#<struct Faraday::Request method=:post, path="example.com/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"http://localhost:3000/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>
In response I am getting following error message
HTTP Status 400 - Inadequate OAuth consumer credentials.
So can any one help to fix this issue?
Is there any other way to store the access token so that I can utilize this for communication purpose. Thanks
First of all, I wan to make clear how Oauth2 works:
Oauth2, the protocol says:
You redirect the user to the provider sign in endpoint adding some required parameters (Ejm: PROVIDER/public/oauth?redirect_uri=MYWEB/oauthDemo& response_type=code&client_id=ABCDE). Sometimes there is also a scope/permission/resource parameter that indicates whats your purpose.
-> Then the users signs in and is redirected to your endpoint MYWEB/public/oauth with a code
Now you have to request the access token doing a POST to the providers endpoint. Example:
POST PROVIDER?code=d5Q3HC7EGNH36SE3N& client_id=d4HQNPFIXFD255H& client_secret=1a98b7cb92407cbd8961cd8db778de53& redirect_uri=https://example.com/oauthDemo& grant_type=authorization_code
Now you have the access_token and you can use it to get information or decode it using JWT.
Having this clear, and seeing that your call seems corect:
#<struct Faraday::Request method=:post, path="PROVIDER/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"MYWEB/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>
As the response is "HTTP Status 400 - Inadequate OAuth consumer credentials.", I think maybe you:
a. Your client is not well configured on the Provider. Usually you use to have a basic configuration on the provider site so he can recognise you. So maybe is not well configured.
b. There is a resource/permission/scope parameter missing or wrong configured on the first step (in the redirection to the provider). So when you ask for the token there is a problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With