How to configure route for oauth callback - ruby-on-rails

I'm using the gem OAuth2 to communicate with Google services. I don't understand how to implement a callback, which receives the response with the OAuth code to get an access token. When I set a breakpoint in the callback method, it never seems to get called.
Here is my code:
Routes:
match '/oauth2/callback' => 'reports#callback'
Actual redirected url:
http://localhost/oauth2/callback?code=111111
ReportsController:
def new
client = OAuth2::Client.new(ENV['GA_CLIENT_ID'], ENV['GA_SECRET_KEY'], {
:authorize_url => 'https://accounts.google.com/o/oauth2/auth',
:token_url => 'https://accounts.google.com/o/oauth2/token'
})
redirect_to client.auth_code.authorize_url({
:scope => 'https://www.googleapis.com/auth/analytics.readonly',
:redirect_uri => 'http://localhost/oauth2/callback',
:access_type => 'offline'
})
end
def callback
oauth_code = params[:code]
# Create access token with oauth_code
end

Google server is trying to access this url http://localhost/oauth2/callback?code=111111 which is not valid.
You need a domain name to use a service like OAuth, because the google server must be able to find your computer over the internet.
To be able to do this from your development machine you should:
Set a name on a known DNS server: The easiest way to do this is through a Dynamic DNS server like dyndns or no-ip
If you are behind a router using NAT, you might need to redirect the requests to your modem on port 80 to your computer: If you don't do this, your modem will receive a package on port 80 from google and will say "not for me", discarding it. You can do this in your modem menu - look for port forwarding or NAT section on it.

The redirect_url passed to google must match exactly the callback url as seen from client's browser. No problem to use localhost in the url (fotanus sentence about DNS and NAT is wrong). If you are running your container in a different port (e.g. 8080) you have to:
Specify the url in google cloud: http://localhost:8080/oauth2/callback
Specify that same return url in the client request.

Related

Problem with using Rails / ADFS integration gem omiauth-wsfed

I have been trying to set my Ruby Rails App to be remotely accessed by a partner of mine which uses ADFS 2.0 for providing SSO possibilities. I have been using omniauth-wsfed gem but failed.
I have set omniauth.rb as below:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :wsfed,
:issuer_name => "http://fs.sib.com.br/adfs/services/trust",
:issuer => "https://fs.sib.com.br/adfs/ls/",
:realm => "https://qa.wit.com",
:reply => "https://qa.wit.com/students/auth/wsfed/callback",
:saml_version => "2.0",
:id_claim => "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
:idp_cert_fingerprint => "--94061be1aba531da005d5f22bf6796b7cd69b3---"
end
Error log is:
ERROR -- omniauth: (wsfed) Authentication failure! invalid_authn_token: OmniAuth::Strategies::WSFed::ValidationError, AuthN token (wresult) missing in callback.
Does anybody suspect what is wrong ?
I am assuming you have configured your omniauth.rb properly where:
Issuer Name: This should be in the format of the adfs sever domain followed by /adfs/services/trust
Issuer: This is where your login requests will be sent, normally it will be the path /adfs/ls on the ADFS server.
Realm: This should match the domain that you provide in your federation metadata document
Reply: This is where you want the response from ADFS to be returned to in your application. This is normally the path /auth/wsfed/callback when using Omniauth.
SAML Version: The version of SAML tokens. Defaults to 2
ID Claim: This is the name of the claim field that ADFS will return that should be used as the unique identifier.
IDP Cert Fingerprint: Your Windows Administrator should be able to tell you this, but if not a way to find it is to put in any string, do a test login to ADFS — this will fail when doing the callback as the certificate doesn’t match, however if you inspect the response in the Chrome Web Inspector you will be able to see the X509 Certificate in the response. You can then use OpenSSL tools, or this online tool to get the fingerprint of the certificate.
Also Setting up callback routes like below
match '/auth/:provider/callback' => 'sessions#create', via: [:get, :post]
match '/auth/failure' => 'sessions#failure', via: [:get]
The **controller#action** can differ depending on how your application is structured.
You can handle the callback in the same way you would any Omniauth provider.
def create
auth = request.env["omniauth.auth"]
auth.uid # Gets the UID value of the user that has just signed in
# Create a session, redirect etc
end
you can refer below repo for further reference.
https://blog.craig.io/using-microsoft-adfs-with-ruby-on-rails-and-omniauth-a26237c64f8d
https://github.com/kbeckman/omniauth-wsfed
Hope it helps.

How to use $remote_addr with rails and nginx secure_link

I have a rails application that makes calls to another server via net::http to retrieve documents.
I have set up Nginx with secure_link.
The nginx config has
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr mySecretCode";
On the client side (which is in fact my rails server) I have to create the secure url something like:
time = (Time.now + 5.minute).to_i
hmac = Digest::MD5.base64digest("#{time}/#{file_path}#{IP_ADDRESS} mySecretCode").tr("+/","-_").gsub("==",'')
return "#{DOCUMENT_BASE_URL}/#{file_path}?md5=#{hmac}&expires=#{time}"
What I want to know is the best way to get the value above for IP_ADDRESS
There are multiple answers in SO on how to get the ip address but alot of them do not seem as reliable as actually making a request to a web service that returns the ip address of the request as this is what the nginx secure link will see (we don't want some sort of localhost address).
I put the following method on my staging server:
def get_client_ip
data=Hash.new
begin
data[:ip_address]=request.ip
data[:error]=nil
rescue Exception =>ex
data[:error]=ex.message
end
render :json=>data
end
I then called the method from the requesting server:
response = Net::HTTP.get_response(URI("myserver.com/web_service/get_client_ip"))
if response.class==Net::HTTPOK
response_hash=JSON.parse response.body
ip=response_hash["ip_address"] unless response_hash[:error]
else
#deal with error
end
After getting the ip address successfully I just cached it and did not keep on calling the web service method.

Metasploit: send_request_cgi returns nil for HTTPS connections

I am currently trying to write an auxiliary module for Metasploit. The module basically tries multiple default credentials to get access to the router's management page. The authentication is done via web, i.e. HTTP POST.
Currently, the module works as expected for plain HTTP connections, i.e. unsecured connections, however every connection attempt via HTTPS (port 443), returns nil. Below is the function used within the Metasploit class to retrieve the login page:
def get_login_page(ip)
begin
response = send_request_cgi(
'uri' => '/',
'method' => 'GET'
)
# Some models of ZyXEL ZyWALL return a 200 OK response
# and use javascript to redirect to the rpAuth.html page.
if response && response.body =~ /changeURL\('rpAuth.html'\)/
vprint_status "#{ip}- Redirecting to rpAuth.html page..."
response = send_request_cgi(
'uri' => '/rpAuth.html',
'method' => 'GET'
)
end
rescue ::Rex::ConnectionError
vprint_error "#{ip} - Failed to connect to Web management console."
end
return response
end
When trying to connect via HTTPS, the first send_request_cgi call returns nil. No exception are caught or thrown. I have tried with 3 different hosts to make sure the issue was not with a specific endpoint. All my 3 attempts failed to return a response. At every attempt, I set the RPORT option to 443;
RHOSTS 0.0.0.0 yes The target address range or CIDR identifier
RPORT 443 yes The target port
Note that I have replaced the real IP with 0.0.0.0. Using a web browser, I can actually connect to the router via HTTPS with no issue (other than having to add an exception since the certificate is untrusted) and am presented the login page. With Wireshark, I tried to look at the generated traffic. I can clearly see that nothing is sent by the router. I notice the 3-way handshake being completed and the HTTP GET request being made:
GET / HTTP/1.1
Host: 0.0.0.0
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
There are 3-4 ACK after and then a FIN/PUSH sent by the server.
Based on this page on Metasploit's GitHub, I was under the impression that connections to HTTPS websites were handled by the underlying framework. I have not seen any articles/tutorial/source that leads me to believe otherwise. The doc about the send_request_cgi does not specify any specific requirement to establish a HTTPS connection. Other posts did not had the exact same issue I'm having. At this point I suspect either the OS, the framework or me forgetting to enable something. Other modules I have looked at either only targets HTTP websites - which I doubt - or do not have any special handling for HTTPS connections.
Any help determining the cause would be greatly appreciated.
Version of Metasploit:
Framework: 4.9.3-2014060501
Console : 4.9.3-2014060501.15168
Version of OS:
SMP Debian 3.14.5-1kali1 (2014-06-07)
As per this post on SecurityStreet, the solution was to set SSL to true in the DefaultOptions in the initialize function:
def initialize
super(
...
'DefaultOptions' =>
{
...
'SSL' => true
}
)
...
end
Connections to routers using HTTPS worked afterwards.

Access Google Contacts API on Ruby

I'm struggling to access the Google Contacts API.
First I tried the google-api-ruby-client gem but it turned out that it does not support the Contacts API.
Next shot was the google_contacts_api gem but I struggle to get a oauth_access_token_for_user with the oAuth2 gem. When following the oAuth2 instructions I don't know what to put in authorization_code_value and Basic some_password.
I tried the following:
require 'oauth2'
client = OAuth2::Client.new(ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], :site => 'http://localhost:9292')
=> #<OAuth2::Client:0x007fcf88938758 #id="blabla.apps.googleusercontent.com", #secret="blabla", #site="http://localhost:9292", #options={:authorize_url=>"/oauth/authorize", :token_url=>"/oauth/token", :token_method=>:post, :connection_opts=>{}, :connection_build=>nil, :max_redirects=>5, :raise_errors=>true}>
client.auth_code.authorize_url(:redirect_uri => 'http://localhost:9292')
=> "http://localhost:9292/oauth/authorize?client_id=blabla.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A9292&response_type=code"
token = client.auth_code.get_token('authorization_code_value', :redirect_uri => 'http://localhost:9292', :headers => {'Authorization' => 'Basic some_password'})
=> Faraday::ConnectionFailed: Connection refused - connect(2) for "localhost" port 9292
I would appreciate if someone could give me detailed step by step instructions how to access the API.
Make sure your app is set up properly and that you've enabled the Contacts API in the Google Developers Console. Then try this:
CLIENT_ID = '?????.apps.googleusercontent.com'
CLIENT_SECRET = 'your_secret'
REDIRECT_URI = 'your_redirect_uri'
client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET,
site: 'https://accounts.google.com',
token_url: '/o/oauth2/token',
authorize_url: '/o/oauth2/auth')
url = client.auth_code.authorize_url(scope: "https://www.google.com/m8/feeds",
redirect_uri: REDIRECT_URI)
Visit url in your browser and log in to Google. The url you are redirected to afterwards will contain the token in the parameter code. It will look like this (this next line is not code you run):
actual_redirect_url = "#{REDIRECT_URI}?code=#{code}"
Parse the code from the redirect url, then
token = client.auth_code.get_token(code, :redirect_uri => REDIRECT_URI)
Edit
Someone asked in the comments how to pass the token to the google_contacts_api library. (I wrote the library, so I should know!)
token is an OAuth2::AccessToken object in this example. All you have to do is pass it to the constructor:
user = GoogleContactsApi::User.new(token)
To be extra clear, the constructor accepts the token object, not a string.
It looks like you're authenticating against localhost (should only be referring to localhost in the context of redirecting after authentication). You should be authenticating against Google's OAuth server somewhere in there.
See: https://github.com/google/google-api-ruby-client/blob/master/lib/google/api_client.rb#L165

Rails: OAuth2 gem returns 400 error when attempting to connect to facebook

I'm attempting to add Facebook connect to our web app, and I'm running into a problem with. Everything works fine locally (I can authenticate through Facebook), but when I push the code to our dev server (which lives in the wild), every time I try to authenticate it returns the following error code:
OAuth2::HTTPError: Received HTTP 400 during request
That's really the only explanation I'm getting. Again, this works on my local machine, and the gems and such match between boxes, so I'm a bit confused. Here's the code I'm executing.
def facebook_connect
#Set the scope we want to pull from Facebook, along with the callback URL
options = {
:redirect_uri => facebook_callback_url,
:scope => "email,publish_stream"
}
#Go out and fetch the url
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_SECRET, {:site => FACEBOOK_API_URL, :access_token_method => :post})
#Redirect to the callback for processing
redirect_to client.web_server.authorize_url(options)
end
def facebook_callback
#Client URL
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_SECRET, {:site => FACEBOOK_API_URL, :access_token_method => :post})
#Parse out the access token
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => facebook_callback_url)
#Get the user
fb_user = JSON.parse(access_token.get('/me'))
#Do some authentication database stuff
end
def facebook_callback_url
uri = URI.parse(request.url)
uri.path = '/users/facebook_callback'
uri.query = nil
uri.to_s
end
I searched Google, but the solutions that show up aren't working. Also, if anyone knows how to parse and display OAuth2 errors, I would appreciate that, as well. Thanks
Assuming that Facebook OATH knows of your server's IP address(they are very strict about it), I would recommend that you use use 'rescue' to catch that exception, get the backtrace and then find where it is being raised and place a bunch of debug statements to check the state of both request and the response, as well as access tokens.
Or you can configure remote debugging with Rubymine or NetBeans which is not an easy task :)
The issue actually ended up being a problem with the "Faraday" gem. Our dev server wasn't set up to handle SSL, which was returning an error code. We patched it using the following answer:
OmniAuth & Facebook: certificate verify failed

Resources