I've been trying to convince Rails of the existence of a page at localhost:3000/user_forms/redir to absolutely no avail. The page appears when I try rake routes- as
user_forms_redir GET /user_forms/redir(.:format) user_forms#redir
It is definitely in the routes.rb file:
Rails.application.routes.draw do
get "user_forms/redir", to: "user_forms#redir"
And there exists a file at /views/user_forms/redir.html.haml thus:
%h1 Checking Google Auth, please wait...
%p
If this page continually hangs, please contact
= mail_to "ex#ex.co.uk"
I even have code in my user_forms_controller.rb file:
def g_check_auth
if(#authorization_code.nil?)
redirect_to user_forms_redir_url
end
end
# GET /user_forms/redir
def redir
#user_form = UserForm.new(set_user_form)
credentials = Google::Auth::UserRefreshCredentials.new(
client_id: "xxx",
client_secret: "xxx",
scope: [
"https://www.googleapis.com/auth/drive",
"https://spreadsheets.google.com/feeds/",
],
redirect_uri: user_form_url(#user_form))
#auth_url = credentials.authorization_uri
redirect_to #auth_url.to_s
end
The program even goes as far as to have user_forms_redir_url as a valid path. Yet, upon following that path I get a lovely 404 message.
Related
I'm trying to use iNaturalist's API via Ruby on Rails. I'm new to Ruby and iNaturalist's documentation is pretty sparse. As a first step, I need to figure out how to get authorization from their site.
iNaturalist provides the sample code below. I set up a project with iNaturalist and tried running the sample code in Rails Console with my credentials. #{url} in the following line is replaced with a url that the user is supposed to go to in order to log in to iNat:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
I went to the resulting url and logged in:
https://www.inaturalist.org/oauth/authorize?client_id=[my client id]&redirect_uri=https://ruby_on_rails--timrobinson41199691.codeanyapp.com/login/&response_type=code
iNaturalist responds with "The redirect uri included is not valid."
If I leave off &response_type=code, it responds with "The authorization server does not support this response type."
My website is on codeanywhere.com. The url of the main page is "https://ruby_on_rails--timrobinson41199691.codeanyapp.com/". Part of the problem is that I don't understand what kind of page I'm supposed to create for redirect_uri, since I'm still kind of new at this.
require 'rubygems'
require 'rest_client'
require 'json'
site = "https://www.inaturalist.org"
app_id = 'YOUR APP ID'
app_secret = 'YOUR APP SECRET'
redirect_uri = 'YOUR APP REDIRECT URI' # you can set this to some URL you control for testing
# REQUEST AN AUTHORIZATION CODE
# Your web app should redirect the user to this url. They should see a screen
# offering them the choice to authorize your app. If they aggree, they will be
# redirected to your redirect_uri with a "code" parameter
url = "#{site}/oauth/authorize?client_id=#{app_id}&redirect_uri=#{redirect_uri}&response_type=code"
# REQUEST AN AUTH TOKEN
# Once your app has that code parameter, you can exchange it for an access token:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
print "Code: "
auth_code = gets.strip
puts
payload = {
:client_id => app_id,
:client_secret => app_secret,
:code => auth_code,
:redirect_uri => redirect_uri,
:grant_type => "authorization_code"
}
puts "POST #{site}/oauth/token, payload: #{payload.inspect}"
puts response = RestClient.post("#{site}/oauth/token", payload)
puts
# response will be a chunk of JSON looking like
# {
# "access_token":"xxx",
# "token_type":"bearer",
# "expires_in":null,
# "refresh_token":null,
# "scope":"write"
# }
# Store the token (access_token) in your web app. You can now use it to make authorized
# requests on behalf of the user, like retrieving profile data:
token = JSON.parse(response)["access_token"]
headers = {"Authorization" => "Bearer #{token}"}
puts "GET /users/edit.json, headers: #{headers.inspect}"
puts RestClient.get("#{site}/users/edit.json", headers)
puts
After the user logs in to iNat, he should be redirected back to my website with the authorization code provided in the data. In routes.rb, my login route is set as:
post '/login', to: 'organisms#login'
I've tried using get, as well.
iNat is returned the error mentioned above and not redirecting back to my site.
OAuth can be a bit daunting at first. And that guide really just shows the equivalent of using cURL to test your API.
In an actual application redirect_uri is whatever endpoint in your application that handles the response when the provider redirects back from authorization.
So lets setup a minimal real rails app.
1. Register your app
Register a new application or edit your existing app.
Use http://localhost:3000/oauth/inaturalist/callback for the callback url (adjust the host as needed).
Keep the window open as you will need the client_id and secret in a moment.
2. Setup your routes
# /config/routes.rb
Rails.application.routes.draw do
# just make sure you have a root path defined.
root to: 'pages#home'
namespace :oauth do
namespace :inaturalist, controller: :callbacks do
# This is just a simple redirect route
get '/', action: :passthru, as: :authorize
# This is the route that handles the actual callback
get 'callback'
end
end
end
You can actually do this without the redirect route and just plant a link to the https://www.inaturalist.org/oauth/authorize... url in your view. But having it isolates your application against the craziness that is OAuth and its how OmniAuth does it.
3. Add your credentials to the Rails app.
In Rails 5 use the encrypted credentials to store your client_id and secret.
Run $ bin/rails credentials:edit from your shell.
inaturalist:
client_id: <from the inaturalist site>
secret: <from the inaturalist site>
In earlier versions use ENV vars instead.
4. Install the oauth2 gem
# Place this in your gemfile outside any groups
gem 'oauth2', '~> 1.4', '>= 1.4.1'
Then run bundle install.
4. Setup the controller
# app/controllers/oauth/inaturalist/callbacks_controller.rb
require 'oauth2'
module Oauth
module Inaturalist
class CallbacksController < ::ActionController::Base
# GET /oauth/inaturalist
def passthru
redirect_to client.auth_code.authorize_url
end
# This endpoint is where the provider redirects the user back to
# after authorization.
# GET /oauth/inaturalist/callback
def callback
# Fetch an auth token from the access code
token = client.auth_code.get_token(params[:code])
# Perform an authenticated request to get the users data
api_response = token.get("/users/edit.json")
#user_data = JSON.parse(api_response.body)
# This is just an example of how you can use the user data from
# the provider
#user = {
uid: #user_data["id"],
nickname: #user_data["nickname"]
}
session[:user_id] = #user[:uid]
session[:token] = token.to_hash
redirect_to root_path, success: "Hello #{#user[:nickname]}"
end
private
# Change this if you are not using Rails 5 credentials.
def client
OAuth2::Client.new(
credentials.fetch(:client_id),
credentials.fetch(:secret),
site: "https://www.inaturalist.org",
redirect_uri: oauth_inaturalist_callback_url
)
end
def credentials
Rails.application.credentials.fetch(:inaturalist)
end
end
end
end
token here is actually a new OAuth2::AccessToken instance that can be called to call endpoints with the fetched credentials.
This example stores the token in the session. You can retrieve it in subsequent requests with:
token = OAuth2::AccessToken.from_hash( session[:token] )
The docs kind of mention trading the oauth access token for an api token for api.inaturalist.org. But the details are kind of sparse.
5 Add a link to sign in:
<%= link_to 'Sign in to iNaturalist.org', oauth_inaturalist_authorize_path %>
I'm trying to setup up an oauth2 webapp (front end written in react, backend in rails). On the front end I'm able to get authenticate and ket my access code, after that through my callback function on redirected to my back server, where I'm trying to exchange my front-end code for a token, below is my code on the back end.
I am initializing a new auth_client and it is being updated properly (client_secrets and code). The problem is when I'm requesting my exchange token, it is giving me a
*** Signet::AuthorizationError Exception: Authorization failed. Server message:
{
"error" : "redirect_uri_mismatch"
}
I don't know how to solve that, the redirect address is being loaded from the client_secret, I've confirmed it, I tried to update it again using auth_ ient.update!, same problems. My routes do exist, tried them, I was using localhost before, but was getting same error, through web search was able to find recommendations of the use of lvh.me. I also tried to send it to a different controller (route) http://localhost:3000/api/v1/users which also have Post, but same error....
I don't know what else to try, I would really appreciate if someone could give me some direction, this is for a capstone project due on Wednesday, and I everything else depends on it...
Thank you in advance for any help ....
require 'google/api_client/client_secrets'
class Api::V1::AuthController < ApplicationController
def create
client_secrets= Google::APIClient::ClientSecrets.load("client_secrets.json")
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'profile https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send',
:redirect_uri => 'http://lvh.me:3000/api/v1/auth'
)
auth_client.code = params["code"]
result = auth_client.fetch_access_token! <-----Breaks here------->
end
end
My routes...
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :users
post '/auth',to:'auth#create'
get '/current_user', to: 'auth#show'
end
end
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
My Authorized redirect URIs on Google API Dashboard ...
http://lvh.me:3000/api/v1/auth
http://localhost:3000/api/v1/auth
http://localhost:3000/api/v1/users
http://lvh.me:3000/api/v1/users
My client_secrets.json ...
client_secrets.json
{
"web": {
"client_id":
"<MY_CLIENT_ID",
"project_id": "storied-pixel-191717",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "<MY_CLIENT_SECRET>",
"redirect_uris": ["http://lvh.me:3000/api/v1/auth", "http://localhost:3000/api/v1/auth","http://localhost:3000/api/v1/users","http://lvh.me:3000/api/v1/users"],
"scope":
"profile https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send",
}
}
Ok... I was able to get it to work, only had to replace my redirect_uri with a 'postmessage'. That did the trick for me.
I'm trying to setup a quickbooks integration with our rails app and I'm getting very weird results. So far, I've created an Active Admin page that lets me call out to Quickbooks' API to get an Oauth2 token:
action_item :reset_token do
session[:state] = SecureRandom.uuid
quickbook_params = {
response_type: 'code',
state: session[:state],
scope: 'com.intuit.quickbooks.accounting'
}
link_to "Reset Token", client.authorization_uri(quickbook_params)
end
page_action :oauth2_redirect do
# test if the response has the state that we set to prevent a man-in-the-middle attack
if session[:state] == params[:state]
client.authorization_code = params[:code]
resp = client.access_token!
ENV["QBO_API_REALM_ID"] = params[:realmId]
ENV["QBO_API_REFRESH_TOKEN"] = resp.refresh_token
ENV["QBO_API_ACCESS_TOKEN"] = resp.access_token
end
redirect_to admin_quickbooks_path
end
def client
Rack::OAuth2::Client.new(
identifier: ENV['QBO_API_IDENTIFIER'],
secret: ENV['QBO_API_SECRET'],
redirect_uri: Rails.application.routes.url_helpers.root_url + ENV['QBO_API_REDIRECT_URI'],
authorization_endpoint: ENV["QBO_API_AUTHORIZATION_ENDPOINT"],
token_endpoint: ENV["QBO_API_TOKEN_ENDPOINT"]
)
end
The above "works" in that I get an access token (but no refresh token). the realm_id also matches the company id that I'm expecting so it seems to be recognizing the authorization attempt at least.
But, I'm not getting back a refresh_token and I keep getting 500s when I try to use the access_token to retrieve a customer:
$ curl -H "Authorization: bearer $auth_token" "https://sandbox-quickbooks.api.intuit.com/v3/company/$realm_id/customer/1"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><FaultInfo xmlns="http://www.intuit.com/sb/cdm/baseexceptionmodel/xsd"><Message>Internal Server Error</Message><ErrorCode>500</ErrorCode><Cause>SERVER</Cause></FaultInfo>
Which is the same error I see in my logs when I try to run a query from the rails app:
action_item :run_query do
link_to "Run Query", admin_quickbooks_query_path
end
page_action :query do
QboApi.log = true # TODO: clean up so that we aren't always logging the Quickbooks API
if ENV['QBO_API_ACCESS_TOKEN']
qbo_api = QboApi.new(
access_token: ENV['QBO_API_ACCESS_TOKEN'],
realm_id: ENV['QBO_API_REALM_ID']
)
session[:qb_customer] = qbo_api.get :customer, 1 rescue "-- rescued error --"
end
redirect_to admin_quickbooks_path
end
Any help would be greatly appreciated.
Thanks!
I'm using stripe connect on my website, and when a user in production tries to connect his stripe account to my web site, I have the following error in the stripe callback :
{
"error": "invalid_redirect_uri",
"error_description": "Invalid redirect URI 'http://www.mywebsite.com/stripe_connections/callback'. Ensure this uri exactly matches one of the uris specified in your application settings",
"state": "4 »
}
whereas my redirecti URIS in my stripe application setting is https://www.mywebsite.com/stripe_connections/callback
here is my controller :
require 'oauth2'
class StripeConnectionsController < ApplicationController
skip_after_action :verify_authorized
def new
stripe_auth_url = "https://connect.stripe.com/oauth"
client = OAuth2::Client.new(ENV['STRIPE_CONNECT_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], :site => stripe_auth_url)
#stripe_url = client.auth_code.authorize_url(:redirect_uri => "#{request.protocol}#{request.host_with_port}/stripe_connections/callback", :scope => 'read_write', state: params[:brief_id])
end
def callback
#brief = Brief.find(params[:state])
stripe_auth_url = "https://connect.stripe.com/oauth"
#user = current_user
client = OAuth2::Client.new(ENV['STRIPE_CONNECT_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], :site => stripe_auth_url)
access_token = client.auth_code.get_token(params[:code], :redirect_uri => '#{request.protocol}#{request.host_with_port}/oauth2/callback')
stripe_connection = StripeConnection.find_or_create_by(user_id: #user.id)
stripe_connection.update_attributes(access_token: access_token.token,
refresh_token: access_token.refresh_token,
livemode: access_token.params['livemode'],
stripe_user_id: access_token.params['stripe_user_id'],
publishable_key: access_token.params['stripe_publishable_key']
)
#user.profile.projects.where(state: 'pending').update_all(state: "on_sale")
end
end
I'm using heroku and paying the SSL add-ons already.
I don't know why stripe is returning http instead of https. Does anyone have an idea? thx.
ps: this has already worked before in production and works in the beta version of the website
Do you have a button that the user clicks to connect to stripe? I just removed the redirect_uri parameter.
You have to remove the redirect_uri of client.auth_code.authorize_url() in the new method and also the redirect_uri in the callback method and put the right protocol in the dashboard stripe.
I'm trying to test logging in via http basic but am continually getting errors, I've uploaded the code here
I have http_basic_authenticate_with name: "name", password: "password"
in my application controller
My step definition for logging in is;
Given /^I login as admin$/ do
authorize "name", "password"
end
but it doesn't work and I get the error
expected there to be content "Posts" in "HTTP Basic: Access denied.\n"
(RSpec::Expectations::ExpectationNotMetError)
can someone tell me how to get cucumber/capybara to log in?
I got it to work with one of two different approaches; take your pick:
### Following works ONLY if performed first before even going to a page!!!
if page.driver.respond_to?(:basic_auth)
puts 'Responds to basic_auth'
page.driver.basic_auth(username, password)
elsif page.driver.respond_to?(:basic_authorize)
puts 'Responds to basic_authorize'
page.driver.basic_authorize(username, password)
elsif page.driver.respond_to?(:browser) && page.driver.browser.respond_to?(:basic_authorize)
puts 'Responds to browser_basic_authorize'
page.driver.browser.basic_authorize(username, password)
else
raise "I don't know how to log in!"
end
My tests responded to browser_basic_authorize
or
encoded_login = ["#{username}:#{password}"].pack("m*")
page.driver.header 'Authorization', "Basic #{encoded_login}"
both of which I found at several places in my search.
Use the selenium web driver and visit the website with page.driver.visit("https://username:password#example.com")