HTTParty get request ignores query params - ruby-on-rails

I'm trying to make a GET request with a quersytring, however, HTTParty is ignoring my params. For example, I'm doing something like this:
headers = {"User-Agent"=>"whatever", "Authorization"=>"whatever"}
options = {"sport" => "mlb" }
HTTParty.get("https://www.erikberg.com/events.json", query: options, headers: headers)
The response should be empty since yesterday there wasn't any MLB game going on. I checked with curl and worked properly. However, when I use this gem I'm getting NBA games (ignoring the param sport). I asked to the owner of the API and he told me I'm getting two redirects (302, 301).
Any clue of what is failing here? Is my request really using https?
Best regards!

Related

graphql_devise for authentication in Rails/Graphql/Apollo/React. "field requires authentication" error

I have a project where I am setting up a Rails API with Graphql and React/Apollo. I've purpled all the google links looking for the best authentication solution, but it seems I haven't been able to find a clear answer.
I settled on using the graphql_devise gem, which leverages devise & devise_token_auth. I would have preferred to find a JWT solution, but just couldn't. (If you have any obvious suggestions please tell me!)
First I mounted a separate auth route, but ran into problems with multiple endpoints when I was setting up ApolloClient. I couldn't figure out how to direct auth related requests to my auth endpoint, while letting the rest go through to my graphql one. (If figuring this out is the easiest solution, please tell me!) Instead, I mounted the auth routes in my own schema, as instructed by the docs:
class MyApiSchema < GraphQL::Schema
use GraphqlDevise::SchemaPlugin.new(
query: Types::QueryType,
mutation: Types::MutationType,
resource_loaders: [
GraphqlDevise::ResourceLoader.new('User', only: [:login, :logout])
]
)
mutation(Types::MutationType)
query(Types::QueryType)
And edited the execute line in graphql_controller.rb:
result = MyApiSchema.execute(query, variables: variables, context: graphql_context(:user), operation_name: operation_name)
At this point, running a test query in postman is successful. I can access the graphql route with a userLogin mutation without any headers set, and get a successful response with client, uid & token. Authentication of my other queries also works—success with headers, denied without.
But when I try to perform the same queries using useQuery in react, it doesn't work. In the Apollo Client Developer Tools plugin in Chrome, it doesn't work either, returning only [Object object]. By looking at the request in my Network tab, I can see that this is the result of the same error: "photo field requires authentication".
When I pry into the rails server, I can see that the headers are being received. I can even authenticate the user manually in my graphql_controller before the execute method is called, so I don't think that it is a CORS issue. I have set up the rack-cors gem in Rails to expose the required headers.
When I drill into the code, it seems that the graphql_devise method set_current_resource is failing to return my user. This seems to stem from the devise method set_user_by_token, but I have not been able to figure out why it is failing.
If anyone has any experience with implementing this gem & stack, I would very much appreciate your input! If you have a better way of approaching authentication, I'd love to know what your strategy is. If you can help me solve this ... field requires authentication error, I will love you forever.
Apologies if I've provided too much/too little info, or if my question is too vague. Please let me know if there's something specific I should know to ask/show in my code. Hope you can help! Thanks.
I've managed to find the issue, and I thought I'd explain.
Having traced the problem to the set_user_by_token method in the devise_token_auth gem, I bundle open devise_token_auth'd, and put a byebug inside. This revealed that #token.token was not being extracted correctly from the headers.
The problem was that my header access-token was somehow being converted to accessToken, so when devise tried to set the token info from the request headers using this key, it returned nil.
I do not know why or where this conversion takes place. I suspect it originates from Apollo/React rather than Rails, since the headers on my postman query were not altered. In react, when I set the headers they are set with access-token, as below, but it seems that at some point in the life of my request they are changed.
How I have set the headers in React:
const client = new ApolloClient({
cache,
link: new HttpLink({
uri: 'http://localhost:3000/graphql',
headers: {
accessToken: localStorage.getItem('access-token'),
client: localStorage.getItem('client'),
uid: localStorage.getItem('uid')
},
}),
});
The keys that devise_token_auth uses to select the headers from the request can be changed in the initializers/devise_token_auth.rb file.
I edited them as follows:
config.headers_names = {:'access-token' => 'accessToken',
:'client' => 'client',
:'expiry' => 'expiry',
:'uid' => 'uid',
:'token-type' => 'token-type' }
This means that my front-end is now working smoothly, and I can perform queries/mutations with authentication. However, now in my postman queries I have to change the header to accessToken if I want them to work.
It would be great to know exactly at what point my header from React is changed to camelCase, but for now, it's working. Hope this helps if anyone goes through the same frustrations!

Sending form params as body for Guzzle POST request

I'm a bit confused about Guzzle's deprecation of sending body parameters, and throwing an exception if $body is an array. The recommended way is seemingly to replace body parameters with form_params.
I'm posting to the eventbrite API here
https://www.eventbrite.com.au/developer/v3/quickstart/
and when doing the following:
$response = $this->guzzleClient->request('POST', $item->eventbriteEndpoint, ['form_params' => $item->parameters]);
It doesn't work (and actually responds like it's received a GET request)
After much trial and error, I discovered I need to json_encode the parameters into a json string and add them into the body like so
$response = $this->guzzleClient->request('POST', $item->eventbriteEndpoint, ['body' => json_encode($item->parameters));
So what is going on here, is form_params the correct replacement for body, and why? Is json_encoding body the correct way to do it, and if so why is form_params mentioned everywhere and this not common knowledge? Something is wrong here, is it
My understanding of form_params and body parameters in a guzzle POST request (most likely)
Guzzle doesn't handle POST requests correctly (unlikely)
The Eventbrite API is not receiving POST requests correctly.
I find guzzle to be incredibly opaque tbh, xdebugging down the rabbit hole usually asks more questions than it answers.
I've actually just discovered something even more bizarre on the Eventbrite end - a POST request to create a new event uses form_params, and updating an event requires the params as json in the body. What.

Interpolation with httparty request in rails

I want to make a http request (using httparty gem) to facebook graph to check if a token which comes form the client is valid or not, but I suppose that interpolation doesn't work with httparty
response = HTTParty.get("https://graph.facebook.com/me?access_token=#{params[:access_token]}")
render json: response
but this gives me the response
{"error":{"message":"Bad signature","type":"OAuthException","code":1}}
Am I missing something?
I suppose that interpolation doesn't work with httparty
Interpolation is a Ruby syntax feature; your string gets interpolated before it's ever passed to HTTParty.
You could have debugged this by inspecting the URL:
uri = "https://graph.facebook.com/me?access_token=#{params[:access_token]}"
Rails.logger.info(uri)
response = HTTParty.get(uri)
Regardless, when using query parameters with HTTParty, you shouldn't use interpolation anyway. You should pass them with the :query option, which will ensure that the parameters are encoded correctly:
response = HTTParty.get( "https://graph.facebook.com/me",
query: { access_token: params[:access_token] })
However, I'm not certain this will actually fix your problem. There's a good chance the problem is really somewhere else in your OAuth flow--there's a lot of room for error when working with OAuth.

JSON parameters not available in request hash (Rails, omniauth-google-oauth2 gem)

Overview
I want to implement the Google OAuth2 server side (hybrid) with the Omniauth Google OAuth2 Gem (0.2.6). However, the code parameter I send to my app does not get added to the request.params hash. Thus, OmniAuth throws an error, as it can't find the code.
Details
After retrieving the auth code from Google I send it to the server (by AJAX):
// Send the code to the server
$.ajax({
type: 'POST',
url: '/auth/google_oauth2/callback',
contentType: 'application/json',
success: function(result) {
// Handle or verify the server response.
},
processData: false,
data: JSON.stringify(password_result)
});
This throws the error:
"error" : "invalid_request",
"error_description" : "Missing required parameter: code"
After going through the stack, I figured out the following:
As long as I have 'application/json' set as content Type, Rack parses the params correctly and the env object contains the parsed parameters:
"action_dispatch.request.request_parameters"=>{"code"=>"<sent_in_code>"}
However, the request.params hash remains empty. Since OmniAuth checks for request.params['code'], this is the source of the error.
request.POST is empty, which from looking at the source code of Rack is the underlying cause for the empty request.params hash.
When sending the code in standard format as data:"code="+authResult['code'], the parameter is available in the request.params hash. (I get a strange undefined route error then, but this is a different issue.)
Questions
Now, even though I can avoid the issue by not using JSON, I'm still very intereted in the answers to the following questions:
Why is the code parameter not available in request.POST/request.params, even though it gets parsed correctly?
Is there a way to fix this, so I can still send the auth code in JSON to my app?
I've spent two afternoons trying to get the answers myself, but haven't really gotten to a good conclusion so far.
omniauth-google-oauth2 tries to get the auth code from the Rack::Request.params hash. However, Rack apparently does not have JSON parsing built-in. Params calls POST which then calls form_data? which only looks for application/x-www-form-urlencoded or multipart/form-data. It then also tries parseable_data?, which does not parse JSON either. Seems like Rack does not support JSON out of the box, also look at this answer.
The "action_dispatch.request.request_parameters"=>{"code"=>"<sent_in_code>"} hash works because this is done by rails ActionDispatch::Request, which subclasses Rack::Request. However because The omniauth gem is included in your app as a Rack middleware, it does not know of the ActionDispatch request object.
The question remains why this example uses JSON.
Possible solutions:
money-Patch JSON Support into Rack - not recommended
just use application/x-www-form-urlencoded - recommended

Make Rails parse JSON from XDR Cross-Domain requests with content-type: nil

I'm building a client side application which cross-domain posts JSON, using CORS, to a Rails 3.2 application on a different server. Unfortunately, we need to support IE9, which means that we're falling back to using XDomainRequest (XDR) to send our cross-domain requests in IE.
The major limitation of XDR is that it doesn't support setting headers like 'Content-type', which means the Rails server doesn't know that it's receiving JSON, so doesn't parse it.
I got around this briefly by using jQuery.param to send the data as url encoded form data. However, some of the data we need to send is nested GeoJSON, which gets garbled by jQuery.param, so I have to send the POST body as JSON.
In config/initializers/wrap_parameters.rb I've set:
ActiveSupport.on_load(:action_controller)
wrap_parameters format: [:json, nil]
end
… so ActionController nests parameters inside model_name when the content-type is nil.
However, I still need Rails to try to JSON.parse() the data from requests where "Content-type" => nil. I could just manually do this in the controllers which need it (which isn't very clean), but I was hoping I would be able to make Rails do it for all {content-type: nil} requests.
I've been looking through the Rails source, and what I've found doesn't make me very hopeful. This method seems to be hard coded to check for :json, and parse if so, meaning I can't modify this from config:
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/params_parser.rb#L43
Can anyone come up with a clever solution?

Resources