How to mock OAuth2 authentication flow in RSpec? - ruby

I am quite new to using RSpec but would like to start testing the (thus far, quite basic) API that I have created, but I am absolutely stumped as to how I can go about mocking the authorization flow of OAuth2, using the oauth and quickbooks-ruby gems. I have researched into this quite extensively today, and am coming up empty handed on any results. Any resources or suggestions would be hugely appreciated.
I am unsure how to even begin approaching actually writing tests, so I don't have any RSpec stuff written yet, but below is shown the two endpoints that I would like to somehow mock, to give an idea of my aim.
get '/callback' do
redirect_uri = "http://localhost:4567/callback"
resp = qbo_client.auth_code.get_token(params[:code], redirect_uri: redirect_uri)
redirect to("/")
end
get '/auth' do
redirect_uri = 'http://localhost:4567/callback'
code_url = qbo_client.auth_code.authorize_url(redirect_uri: redirect_uri, response_type: "code", state: SecureRandom.hex(12), scope: "com.intuit.quickbooks.accounting")
redirect code_url
end
Basically, for a user to authenticate, they hit the /auth endpoint which redirects to Quickbooks for OAuth authorization, this sends the authorization code to the callback endpoint which is then traded for an access token.
I am unsure how to simulate obtaining and exchanging the authorization code, primarily. My best guess at this point is to use a fixture representing the auth-code, mock the exchange with webmock or some comparable tool, but I am very unsure if this is the right approach.

To test the OAuth2 flow in your API, you will need to simulate the responses from the authorization server and ensure that your code handles them correctly. One way to do this is by using a library like VCR or WebMock to record and replay HTTP interactions between your API and the authorization server.
Here is an example of how you could use VCR to record and replay requests to the Quickbooks API:
Install VCR gem() and configure VCR in your spec_helper.rb file:
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :webmock
end
RSpec.configure do |config|
config.around(:each) do |example|
VCR.use_cassette("#{example.full_description.parameterize.underscore}") do
example.run
end
end
end
Use the VCR.use_cassette block to wrap your API requests in your test:
describe 'GET /auth' do
it 'redirects to Quickbooks for OAuth authorization' do
VCR.use_cassette('quickbooks_oauth_authorize') do
get '/auth'
expect(last_response).to be_redirect
end
end
end
describe 'GET /callback' do
it 'trades the authorization code for an access token' do
VCR.use_cassette('quickbooks_oauth_token') do
get '/callback', code: '123456'
expect(last_response).to be_redirect
end
end
end
When you run your tests, VCR will record the HTTP interactions and save them to a YAML file in the spec/vcr_cassettes directory. On subsequent test runs, VCR will replay the interactions from the cassette file instead of making real HTTP requests to the authorization server. This allows you to test the OAuth2 flow in a predictable and repeatable way.
In the example above, I used quickbooks_oauth_authorize and quickbooks_oauth_token as the names of the cassette files, but you can use any name that makes sense for your test. You can also pass additional options to the VCR.use_cassette block to customize how VCR records and replays the interactions, such as record: :new_episodes to append new interactions to an existing cassette file.
I hope this helps you get started with testing the OAuth2 flow in your API using RSpec and VCR. Good luck!

Related

Get Instagram Access Token using Restful Ruby

I am trying to use the Instagram API to create a rails background worker to query hashtags. I don't need to log in any other user but myself furthermore I don't want to have to use any browsers, just RESTful calls.
I'm trying to automate getting my access token in a Ruby script using the gem "rest-client" (https://github.com/rest-client/rest-client)
I can successfully navigate to the following url in a browser and get the access token from the response url
I have used both this URL:
https://www.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=xxx&response_type=token
BUT When I use the RESTful gem response = RestClient.get(url) the
response.headers['location'] is nil
I have also tried using the Instagram API URL but no luck: https://api.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=xxx&response_type=code
Anyone know how to get the access token completely programmatically in Ruby?
I think I'm missing the step to log in the user (which will be me). Not sure how to do this programatically.
Can I use the instagram API with a code or access token that never changes?
rest-client automatically request to get to redirect host.
https://github.com/rest-client/rest-client/blob/master/lib/restclient/abstract_response.rb
I have never used the instagram API yet, but the code to get "Location" on 302 is shown below.
# client
require 'net/http'
require 'uri'
url = URI.parse('http://localhost:2000')
res = Net::HTTP.start(url.host, url.port) do |http|
http.get('/')
end
puts "#{res.header['Location']} is https://google.com"
# test server
require 'socket'
server = TCPServer.new 2000
loop do
socket = server.accept
while header = socket.gets
break if header.chomp.empty?
puts header.chomp
end
socket.puts "HTTP/1.0 302"
socket.puts "Location: https://google.com"
socket.close
end
I hope that this information is helpful to you.

Rails Integration Testing - How to Simulate bad CSRF token and Expired Session

I just changed exception handling code in my application_controller.rb to correctly capture ActionController::InvalidAuthenticityToken.
I was previously doing a rescue_from Exception that was defined after the recuse_from ActionController::InvalidAuthenticityToken. This was taking priority and my intended rescue_from code was not being executed.
I'd like to write an integration test to verify this behavior. How can I create an object that will allow me to send a bad CSRF token to a post request to simulate this behavior?
I'd also like to have an object that will allow me to simulate an expired session to make a get request. How would I implement these integration tests?
A bad CSRF token can be simulated with:
with_forgery_protection do
post user_session_path, {:authenticity_token => 'foo'}
assert redirected_to_new_user_session_path
end
An expired session can be simulated using the TimeCop gem:
Timecop.travel 2.days.from.now do
get some_authorized_path
assert_redirect_to new_user_session_path
end

How do I write a sinatra API to receive a POST from AngularJS $http

$http({
method: "POST",
url: "http://myapp.herokuapp.com/angular",
data: {message: $scope.newChat}
})
I thought that if I enable it to receive JSON, it would be fine, but I still get a 404 error with an OPTIONS method:
require 'rubygems'
require 'sinatra'
require 'sinatra/json'
require 'json'
post '/angular' do
data = JSON.parse(request.body.read)
json data
end
But it's not working. What do I need to change on either/both the angularJS side and my sinatra server side in order to accept posts and send a response back to the client?
I'm working on the assumption that this is a cross origin request. If this is the case CORS is disabled by default due to security reasons; as it should be.
You will need to enable CORS; in Sinatra I typically use the sinatra-cross_origin extension for this.
sinatra-cross_origin
To enable CORS for the entire service you would use something like follows.
require 'sinatra'
require 'sinatra/cross_origin'
configure do
enable :cross_origin
end
To enable for only specific routes it would look more like follows.
get '/cross_origin' do
cross_origin
"This is available to cross-origin javascripts"
end
Make sure you realize that this opens a big security risk and you are mitigating that risk appropriately.
The following for example will allow any origin to connect, which in the case of an open web service may be what you need.
set :allow_origin, :any
On the other hand you could limit it if possible
set :allow_origin, 'http://example.com'
If you need to open it up as a full web service to any origin then you will want to consider additional security measures such as TLS and OAuth, but that is a bit beyond the scope of this question.

JSON interaction after OAuth2/OmniAuth authentication

Context: I'm trying to interact with Twitter via JSON and no libraries as i'm practicing to interact with a newly released receipt printer(themprinter.com) which has no helper libraries. I need to OAuth with the printer then make the appropriate calls to register my device, print, verify online/offline status etc.
I've successfully authenticated with Twitter via OmniAuth's Twitter Gem. I can pull all the data from the Authentication Hash here - https://github.com/arunagw/omniauth-twitter
...now what? I want to be able to make a JSON call with my OAuth credentials to Twitter and pull my timeline or any other such data. Can anyone provide any sample code that will allow me a starting point to tinker with and work off of?
Twitter provides REST API. Here's how you might create a GET REST request
request = Net::HTTP::Get.new(url, initheader = header)
http_request = Net::HTTP.new(host, port)
response = http_request.start {|http| http.request(request)}
here's an example of the request URL:
https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=twitterapi&count=2

Testing a web API using RSpec and VCR

I'm writing an API wrapper as a gem, and I want to test API responses using RSpec.
The problem with this is that all API requests are made using GET, and contain an API key in the url:
e.g. game/metadata/{api_key}
This presents problems for testing, as I don't want to keep the API key in the git repository history. Is there any way I can do these spec tests, preferably with RSpec/VCR, and not store the API key in version control?
I've tried using environment variables, but VCR still stores the entire request, not just the response body.
VCR has a configuration option specifically for cases like these:
VCR.configure do |c|
c.filter_sensitive_data("<API_KEY>") { MyAPIClient.api_key }
end
See https://www.relishapp.com/myronmarston/vcr/docs/configuration/filter-sensitive-data for a larger example.

Resources