I want to write a script which calls a REST API using a URL like:
http://localhost:3000/api/v1/user/xyz
If I open this URL in the browser, it asks for a user email and password. Once I've authenticated, it returns a JSON response with a user id. I don't know how I should I authenticate through the script. Can someone give me a suggestion?
This is what I started with:
require 'rubygems'
require 'net/http'
require 'json'
api_url = "http://localhost:3000/api/v1//user/xyz"
response = Net::HTTP.get_response(URI.parse(api_url))
data = JSON.parse(response.body)
fields = data.keys
puts fileds
puts data.keys
This is the authentication for the API:
before_filter :authorize, :except => [:keys]
private
def authorize
authenticate_or_request_with_http_basic do |username, password|
user = User.find_by_email(username)
if user && user.valid_password?(password)
sign_in :user, user
end
end
end
How I can pass email and password to authenticate this API so I can get "id" as return?
rest_client gem works great for me. As far as what the API returns that should be in the documentation. You can always try it in IRB and see what the response contains.
HTTP Basic authentication is pretty simple to add to your request, though I don't think you can use the get_response convenience method.
See: http://ruby-doc.org/stdlib-2.0/libdoc/net/http/rdoc/Net/HTTP.html#label-Basic+Authentication
Example from the documentation:
uri = URI('http://example.com/index.html?key=value')
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass' # username, password go here.
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
puts res.body
Related
I'm trying to access all the contacts from Google Contacts for invite purposes from my site.
So I'm using OAuth2 but when I first request I get a 401 error with access_token. :(
Keep in mind I'm working local!!
Here's my code.
def authenticate
# initiate authentication w/ gmail
# create url with url-encoded params to initiate connection with contacts api
# next - The URL of the page that Google should redirect the user to after authentication.
# scope - Indicates that the application is requesting a token to access contacts feeds.
# secure - Indicates whether the client is requesting a secure token.
# session - Indicates whether the token returned can be exchanged for a multi-use (session) token.
next_param = "http://localhost:3000/import/authorise"
scope_param = "https://www.google.com/m8/feeds/"
#session_param = "1"
state="profile"
client_id="535946964206.apps.googleusercontent.com"
root_url = "https://accounts.google.com/o/oauth2/auth"
response_type="token"
#TO FIND MORE ABOUT THESE PARAMETERS AND THERE MEANING SEE GOOGLE API DOCS.
query_string = "?scope=#{scope_param}&state=#{state}&redirect_uri=#{next_param}&response_type=#{response_type}&client_id=#{client_id}"
#render :text=> root_url + query_string and return
# redirect_to "https://accounts.google.com/o/oauth2/auth?
#scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&
#state=%2Fprofile&
#redirect_uri=http://localhost:3000/import/authorise&
#response_type=token&
#client_id=535946964206.apps.googleusercontent.com
#"
redirect_to root_url + query_string
end
this is for authentication now i get response with access token
http://localhost:3000/import/authorise#state=profile&access_token=ya29.1.AADtN_WPohnlKYBH16kFrqEcYUTZ558WDICpqCqiz1mwBKESFr5PTiJFA5vsyoc&token_type=Bearer&expires_in=3600
Now next it will redirected to here,this is where i get above error
# YOU WILL BE REDIRECTED TO THIS ACTION AFTER COMPLETION OF AUTHENTICATION PROCESS WITH TEMPORARY SINGLE USE AUTH TOKEN.
def authorise
#FIRST SINGEL USE TOKEN WILL BE RECEIVED HERE..
token = params[:token]
#PREPAIRING FOR SECOND REQUEST WITH AUTH TOKEN IN HEADER.. WHICH WILL BE EXCHANED FOR PERMANENT AUTH TOKEN.
uri = URI.parse("https://www.google.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
path = '/accounts/AuthSubSessionToken'
headers = {'Authorization' => "AuthSub token=#{token}"}
render :text=> http.get(path, headers) and return
#GET REQUEST ON URI WITH SPECIFIED PATH...
resp, data = http.get(path, headers)
#ender :text=> data and return
resp.code="200"
#SPLIT OUT TOKEN FROM RESPONSE DATA.
if resp.code == "200"
token = ''
data.split.each do |str|
if not(str =~ /Token=/).nil?
token = str.gsub(/Token=/, '')
end
end
return redirect_to(:action => 'import', :token => token)
else
redirect_to root_url , :notice => "fail"
end
end
here at above function when it requests resp, data = http.get(path, headers) the responce resp.code is 401 . i dont know what's wrong i'm following https://gist.github.com/miketucker/852849 example which uses AuthSub which is now not in use so i'm adapting oauth2 .
i already have made app at clode console and given return url and other info.
any help or point me to other working example,thanks you!!!
thank you.
I'm trying to grab Twilio recordings with the following GET request to their API in my Rails controller:
def myrecordings
account_sid = 'AC32a3c49700934481addd5ce1659f04d2'
auth_token = 'asd89f677asd897asdfd9' #this is made up for now
client = Twilio::REST::Client.new(account_sid, auth_token)
#recordings = client.account.recordings.list()
render :json => #recordings
end
As a result I'm getting a 500 response error with the message "Authenticate"
My application trace is
app/controllers/campaigns_controller.rb:13:in `myrecordings' [the #recordings line]
To the best of my knowledge I'm following what are in the Twilio docs here:
http://www.twilio.com/docs/api/rest/recording
But clearly I'm missing something. The account_sid and auth_token are correct, but is there a security issue?
require 'rubygems'
require 'twilio-ruby' # Download twilio-ruby from twilio.com/docs/libraries
# Get your Account Sid and Auth Token from twilio.com/user/account
account_sid = 'AC32a3c49700934481addd5ce1659f04d2'
auth_token = '{{ auth_token }}'
#client = Twilio::REST::Client.new account_sid, auth_token
# Loop over recordings and print out a property for each one
#client.account.recordings.list.each do |recording|
puts recording.sid
end
From the docs, this should work too, assuming you change the sid and token to the right ones
How would I write a method to be used in rspec testing to access pages that require a username and password for HTTP Digest Authentication. For example, this test...
it "edit" do
http_login
post :edit, id: #post
assigns[:post].should eq(#post)
end
needs http_login method to be something like this...
def http_login
user = {"username" =>
Digest::MD5.hexdigest(["username","Application","password"].join(":"))}
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Digest.encode_credentials(?,?,?,?)
end
My question is what do I put in the four arguments for the encode credentials. The arguments are going to be http_method, credentials, password, password_is_ha1 but I'm unsure how to write http_method and credentials to implement in the tests.
Solution here: https://gist.github.com/1282275
recopied here for posterity
# Adds support for http digest authentication in Rails 3
# Inspired by: http://lightyearsoftware.com/2009/04/testing-http-digest-authentication-in-rails/
# Place this code in test/test_helper.rb
# In your test, call authenticate_with_http_digest prior to calling get, post, put or delete
# Tested with Rails 3.0.7
class ActionController::TestCase
require 'digest/md5'
def authenticate_with_http_digest(user = API_USERNAME, password = API_PASSWORD, realm = API_REALM)
ActionController::Base.class_eval { include ActionController::Testing }
#controller.instance_eval %Q(
alias real_process_with_new_base_test process_with_new_base_test
def process_with_new_base_test(request, response)
credentials = {
:uri => request.url,
:realm => "#{realm}",
:username => "#{user}",
:nonce => ActionController::HttpAuthentication::Digest.nonce(request.env['action_dispatch.secret_token']),
:opaque => ActionController::HttpAuthentication::Digest.opaque(request.env['action_dispatch.secret_token'])
}
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Digest.encode_credentials(request.request_method, credentials, "#{password}", false)
real_process_with_new_base_test(request, response)
end
)
end
end
Here's a solution for RSpec 3.1 and Rails 4 HTTP Digest Auth testing: https://gist.github.com/murbanski/6b971a3edc91b562acaf
So I logged in with Google and it hits my callback url with a code parameter.
Here is how I initiate my client, I'm using oauth2 gem.
def oauth_client(channel_name)
file = YAML.load_file("#{Rails.root}/config/oauth_credentials.yml")
client_id = file['oauth_credentials'][channel_name]['client_id']
client_secret = file['oauth_credentials'][channel_name]['secret']
site = file['oauth_credentials'][channel_name]['site']
OAuth2::Client.new(client_id, client_secret, site: site, authorize_url: "/o/oauth2/auth", connection_opts: { params: { scope: "https://www.googleapis.com/auth/adsense https://www.googleapis.com/auth/analytics.readonly" } })
end
def oauth_url_for(channel_name)
client = oauth_client(channel_name)
client.auth_code.authorize_url(:redirect_uri => oauth_callback_url(channel: channel_name))
end
Here's my controller
class Oauth2Controller < ApplicationController
include ApplicationHelper
def callback
token = oauth_client(params[:channel]).auth_code.get_token(params[:code], :redirect_uri => oauth_callback_url(channel: params[:channel]))
current_user.connections.create!(channel: params[:channel], token: token)
render text: request.inspect
end
end
Unfortunately I can't get_token due to a response from google saying The page you requested is invalid.
It doesn't look like you're defining the token_url value in your initialization of the client, or later. It is "o/oauth2/token". By default "/oauth/token" is used, so that's likely the cause of your "page you requested is invalid" error.
Source:
http://rubydoc.info/gems/oauth2/0.8.0/OAuth2/Client#initialize-instance_method
I have a RESTful Rails application with a resource called "Foo". I'm trying to use REST Client to do a put:
resource = RestClient::Resource.new 'http://localhost:3000/foos/1', :user => 'me', :password => 'secret'
resource.put :name => 'somethingwitty', :content_type => 'application/xml'
But my app raises:
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'
It seems like my app isn't getting the message that this is an XML request and that the AuthenticityToken should be ignored. Maybe I'm not using REST Client correctly. Any ideas on why I'm getting the exception?
Try putting an :only => [:update, :delete, :create] on the protect_from_forgery line in your application controller.
More info: http://ryandaigle.com/articles/2007/9/24/what-s-new-in-edge-rails-better-cross-site-request-forging-prevention
Use something like:
resource.put '<foo><name>somethingwitty</name></foo>', :content_type => 'application/xml'
I think you need to make two changes;
(a) Use the rails routing to tag this as an XML request
(b) Use HTTP Basic Authentication to authenticate the request.
This means changing your URL above to include the username, password like this
me:secret#localhost:3000/foos/1.xml
also note .xml bit
I guess that somewhere on your server-side you have code that authenticates in-bound requests via a before filter. This needs to work something like this ...
#
# If you haven't authenticated already then you are either
# reqirected to the logon screen (for HTML formats) or
# the browser prompts you. You are always allowed to pass
# the username/password in the URL
#
def login_required
#current_user = valid_session?
unless #current_user
if params["format"]
#
# If you specify a format you must authenticate now
#
do_basic_authentication
else
display_logon_screen
end
end
end
#
# Ask Rails for the login and password then authenticate as if this
# were a new login.
#
def do_basic_authentication
user = authenticate_with_http_basic do |login, password|
User.authenticate(login, password)
end
if user
current_user(#current_user = user)
else
request_http_basic_authentication
end
end
That's from our own app and is triggered by a before_filter in ApplicationController.
Also, I don't think you need the :content_type => 'application/xml'. What I normally do is just call post or put directly like this ..
response = RestClient.post URI.encode(url), :record => args
where the url contains the basic authentication and the ".xml"
Happy coding
Chris
Since your application is a Rails app, it might be easier to use ActiveResource for the client.
Something like:
require 'active_resource'
class Foo < ActiveResource::Base
self.site = 'http://localhost:3000/'
end
foo = Foo.new(:name => 'somethingwitty')
foo.save
You can read up on how to do the authentication on the rdoc site.