I'm trying to use Faraday with Rails 4 for Google API Oauth in below code:
class GoogleApisController < ApplicationController
def oauth2callback
require 'faraday'
render layout: false
conn = Faraday.new(:url => 'https://accounts.google.com',:ssl => {:verify => false}) do |faraday|
faraday.request :url_encoded
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
#result = conn.post '/o/oauth2/token', {'code' => params[:code],
'client_id' => "some_id",
'client_secret' => "some_secret",
'redirect_uri' => "some_redirect_uri",
'grant_type' => 'authorization_code'}
end
end
But when I do #result.body.inspect it doesn't return anything.
So #result return nil and doesn't seem to process at all.
However when I try the above codes in liveshell it seems to work.
Is there a reason why this is happening?
If you try to render before you instantiate the object you are trying to render, the template won't actually have anything in it. Moving the render statement to the bottom of the method will take care of the issue.
Related
Is there any reference available to connect to Facebook using Oauth2 gem in Rails 4?
I am getting above error while calling get_token method of Oauth2.
def login_facebook
redirect_to client.auth_code.authorize_url(
:redirect_uri => 'http://localhost:3000/oauth/facebook_callback'
)
end
def facebook_callback
token = client.auth_code.get_token(
params[:code],
:token_url => "/oauth/token",
#:token_url => "/oauth/access_token",
# :parse => :query,
:redirect_uri => 'http://localhost:3000/oauth/facebook_callback'
)
puts "**************"
puts token.inspect
puts "**************"
end
private
def client
OAuth2::Client.new(APPLICATION_ID, APPLICATION_SECRET, :site => FB_GRAPH_URL)
end
The default url for requesting the access token has been changed from /oauth/access (which is what FB uses) to /oauth/access_token. Now, you need to override this setting as:
OAuth2::Client.new(FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, {
:token_url => '/oauth/access_token',
:redirect_uri => 'http://localhost:3000/oauth/facebook_callback'
})
You may want to use Koala gem for easy communication with facebook.
I'm working on an app that interacts with SoundCloud and I'm having an issue when I try to save the exchange_token that I'm getting back from the server (among other things) and I really could use some assistance.
According to the error I'm getting:
undefined method `merge!' for nil:NilClass
The problem apparently lies with line 10 in my sclouds_controller.rb file (included below):
soundcloud_client.exchange_token(:code => params[:code])
Which is calling a method in the SoundCloud gem that I'm using. Here's the line in the SoundCloud gem that the error originates from:
params.merge!(client_params)
That can be found on line 23 of the following method (taken from the client.rb file in the SoundCloud gem):
def exchange_token(options={})
store_options(options)
raise ArgumentError, 'client_id and client_secret is required to retrieve an access_token' if client_id.nil? || client_secret.nil?
client_params = {:client_id => client_id, :client_secret => client_secret}
params = if options_for_refresh_flow_present?
{
:grant_type => 'refresh_token',
:refresh_token => refresh_token,
}
elsif options_for_credentials_flow_present?
{
:grant_type => 'password',
:username => #options[:username],
:password => #options[:password],
}
elsif options_for_code_flow_present?
{
:grant_type => 'authorization_code',
:redirect_uri => #options[:redirect_uri],
:code => #options[:code],
}
end
params.merge!(client_params)
response = handle_response(false) {
self.class.post("https://#{api_host}#{TOKEN_PATH}", :query => params)
}
#options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
#options[:expires_at] = Time.now + response.expires_in if response.expires_in
#options[:on_exchange_token].call(*[(self if #options[:on_exchange_token].arity == 1)].compact)
response
end
However, if I throw a 'raise' in my sclouds_controller.rb file like this:
def connected
if params[:error].nil?
raise
soundcloud_client.exchange_token(:code => params[:code])
Then, in the console, manually paste in the following line:
soundcloud_client.exchange_token(:code => params[:code])
I get back the following response (which, to me, appears to be successful):
$ #<SoundCloud::HashResponseWrapper access_token="xxxxx" expires_in=21599 refresh_token="xxxxx" scope="*">
Any idea why this is happening? I'm trying to learn what I'm doing wrong, especially since I'm not sure if I'm going about this in the right way. Here's some of my code for a little more context. Thanks in advance!
sclouds_controller.rb:
class ScloudsController < ApplicationController
before_action :authenticate_user!, only: [:connect, :connected]
def connect
redirect_to soundcloud_client.authorize_url(:display => "popup")
end
def connected
if params[:error].nil?
soundcloud_client.exchange_token(:code => params[:code])
unless user_signed_in?
flash[:alert] = 's'
redirect_to :login
end
current_user.update_attributes!({
:soundcloud_access_token => soundcloud_client.access_token,
:soundcloud_refresh_token => soundcloud_client.refresh_token,
:soundcloud_expires_at => soundcloud_client.expires_at
})
end
redirect_to soundcloud_client.redirect_uri
end
def disconnect
login_as nil
redirect_to root_path
end
private
def soundcloud_client
return #soundcloud_client if #soundcloud_client
#soundcloud_client = User.soundcloud_client(:redirect_uri => 'http://localhost:3000/sclouds/connected/')
end
end
user.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :soundcloud_access_token, :soundcloud_refresh_token, :soundcloud_expires_at
has_one :scloud
#SOUNDCLOUD_CLIENT_ID = 'xxxxx'
#SOUNDCLOUD_CLIENT_SECRET = 'xxxxx'
#REDIRECT_URI = 'xxxxx'
def self.soundcloud_client(options={})
options = {
:client_id => #SOUNDCLOUD_CLIENT_ID,
:client_secret => #SOUNDCLOUD_CLIENT_SECRET,
:redirect_uri => #REDIRECT_URI
}.merge(options)
Soundcloud.new(options)
end
def soundcloud_client(options={})
client = self.class.soundcloud_client(options)
options= {
:access_token => soundcloud_access_token,
:refresh_token => soundcloud_refresh_token,
:expires_at => soundcloud_expires_at
}.merge(options)
client.on_exchange_token do
self.update_attributes!({
:soundcloud_access_token => client.access_token,
:soundcloud_refresh_token => client.refresh_token,
:soundcloud_expires_at => client.expires_at
})
end
client
end
end
options= {
:access_token => soundcloud_access_token,
:refresh_token => soundcloud_refresh_token,
:expires_at => soundcloud_expires_at
}.merge(options)
How does this work?
You're trying to set the options local var (which is a duplicate of the options argument in your method), and then you're trying to merge options? Looks like a self-referential loop to me...
Method
Secondly, you mention the error is caused by this line:
exchange_token(:code => params[:code])
I can't see this method in your documentation? The merge! method is obviously being caused inside this method somewhere - we need to know where it's being called, so we can fix it!
I can see one mention of merge, which I've queried above
Update
Thanks for posting the code!
I think #mischa is right - but I'll keep this code to show you what I'd look at..
I can only give an opinion, as I've never used this gem before -There are two calls to merge! in the gem code:
params.merge!(client_params)
#options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
I'd look at this:
Params
The params hash is populated locally like this:
params = if options_for_refresh_flow_present?
{
:grant_type => 'refresh_token',
:refresh_token => refresh_token,
}
elsif options_for_credentials_flow_present?
{
:grant_type => 'password',
:username => #options[:username],
:password => #options[:password],
}
elsif options_for_code_flow_present?
{
:grant_type => 'authorization_code',
:redirect_uri => #options[:redirect_uri],
:code => #options[:code],
}
end
This could be the cause of the problem, as it is populated with elsif (not else). This means you have to make sure you're passing the right codes to the system
#options
#options is referenced but not declared
I imagine it's declared in another part of the gem code, meaning you need to make sure it's there. This will likely be set when the gem initializes (I.E when you set up the authentication between SC & your app)
If you've not got the #options set up correctly, it will probably mean you're not calling the gem correctly, hence the error.
Try mischa's fix, and see if that cures the issue for you.
Initializer
Something to note - convention for including gems in your system is to use an initializer
If you set up an initializer which creates a new constant called SOUNDCLOUD, you can then reference that throughout your app (curing any errors you have here)
I can write some code for you on this if you want
#RickPeck got very close I think, as the problem really lies in this code excerpt:
params = if options_for_refresh_flow_present?
{
:grant_type => 'refresh_token',
:refresh_token => refresh_token,
}
elsif options_for_credentials_flow_present?
{
:grant_type => 'password',
:username => #options[:username],
:password => #options[:password],
}
elsif options_for_code_flow_present?
{
:grant_type => 'authorization_code',
:redirect_uri => #options[:redirect_uri],
:code => #options[:code],
}
end
#options is populated in the line store_options(options). So the problem is that neither options_for_refresh_flow_present?, options_for_credentials_flow_present?, nor options_for_code_flow_present? return true.
The relevant option for your code is code flow:
def options_for_code_flow_present?
!!(#options[:code] && #options[:redirect_uri])
end
which expects options to have both :code and :redirect_uri. In your code you pass only :code. Add a :redirect_uri and you should be good to go.
What #Mischa suggests will probably fix that for you, as your :redirect_uri was nil when you set it...
I have here a model helper named GmailGcalendar
I have multiple actions in 2 models namely gmail.rb and pivotaltracker.rb
They do the same functions, but the only difference is the connection url.
In my lib helper:
def connection_url
if API::Pivotaltracker
'https://www.pivotaltracker.com'
else
'https://accounts.google.com'
end
end
def my_connections
connection_name ||= Faraday.new(:url => "#{connection_url}" , ssl: {verify: false}) do |faraday|
faraday.request :url_encoded # form-encode POST params
faraday.response :logger # log requests to STDOUT
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
end
puts "####"
puts connection_url
end
And in my gmail.rb
def connection
my_connections
end
As well as my pivotaltracker.rb
def connection
my_connections
end
Now they have different connection url.
Pivotal goes to https://www.pivotaltracker.com
Gmail goes to https://accounts.google.com
But I seem to can't make the condition work in connection_url action.
Any workarounds will be appreciated.
EDIT:
I am using the connection_url here: ( in the Faraday block )
def my_connections
connection_name ||= Faraday.new(:url => "#{connection_url}" , ssl: {verify: false}) do |faraday|
faraday.request :url_encoded # form-encode POST params
faraday.response :logger # log requests to STDOUT
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
end
puts "####"
puts connection_url
end
Your can rewrite your lib helper as class:
class Connection
def initialize(url)
#url = url
end
def setup
connection_name ||= Faraday.new(:url => "#url" , ssl: {verify: false}) do |faraday|
faraday.request :url_encoded # form-encode POST params
faraday.response :logger # log requests to STDOUT
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
end
end
end
Now in the model you can initialize this class with the url you want, for example:
def connection
Connection.new("https://accounts.google.com").setup
end
I didn't test this code, but it should work.
I've been struggling trying to get my API working with CORS. I followed the instructions from:
http://kellishaver.tumblr.com/post/40758797489/cors-headers-with-devise
Added this to my application.rb:
config.middleware.use "Cors"
config.middleware.insert_before Warden::Manager, "Cors"
Added this to /lib/cors.rb:
class Cors
def initialize(app)
#app = app
end
def call(env)
status, headers, response = #app.call(env)
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'DELETE, GET, HEAD, OPTIONS, POST, PUT'
headers['Access-Control-Max-Age'] = "1728001"
[status, headers, response]
end
end
As I'm trying to solve the same problem and allow my API to be called from Chrome (or anywhere else). Problem is that all my calls work except for my custom API Devise calls. My Devise API calls get a weird 404 error for "OPTIONS" when I look at it in Chrome dev tools.
My Devise API looks like this...
class Api::V1::SessionsController < Devise::SessionsController
skip_before_filter :verify_authenticity_token,
:if => Proc.new { |c| c.request.format == 'application/json' }
respond_to :json
def create
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
render :status => 200,
:json => { :success => true,
:info => "Logged in",
:data => { :auth_token => current_user.authentication_token },
:id => current_user.id}
end
...
Any ideas on why my custom Devise API calls aren't working but my others are?
Thanks!
Got it working. Just switched to the rack-cors gem instead of the code above and followed the blog example here:
https://gist.github.com/AdrianTeh/3561310
I'm redirecting to https like so:
redirect_to :protocol => 'https://', :status => :moved_permanently
However, the parameters don't go through like this. I can pass specific parameters through like this:
redirect_to :protocol => 'https://', :status => :moved_permanently, :param1 => params[:param1], :param2 => params[:param2]
How would I make it so that it just passes through every parameter on the url instead of having to explicitly declare each parameter?
Figured it out:
redirect_to({:protocol => 'https://'}.merge(params), :flash => flash)
This will keep all URL params through the redirect.
With Rails 4.2 and above, passing the whole params hash will result in adding ?controller=foo&action=bar to the querystring. Instead, you should do this:
redirect_to protocol: 'https', params: request.query_parameters
If you only need this at the controller level, you can use:
MyController < ApplicationController
force_ssl
end
You can use :only or :except if you only need this on a certain action. See documentation:
http://api.rubyonrails.org/classes/ActionController/ForceSSL/ClassMethods.html
Alternatively, if you just want your whole app to use ssl (assuming rails 3.1 or greater):
# config/application.rb
module MyApp
class Application < Rails::Application
config.force_ssl = true
end
end
You could just pass params as an argument like this:
redirect_to :protocol => 'http://', :status => :moved_permanently, :params => params