How to fix SSL error thrown by omniauth LinkedIn - ruby-on-rails

I'm trying to set up an authentication via LinkedIn in the rails 5.2 application, for the same I'm referring to the documentation given by devise but I am getting the following error:
ERROR -- omniauth: (linkedin) Authentication failure! Connection reset by peer: Faraday::SSLError, Connection reset by peer
I have added these using the following gems for the configuration
devise ~> 4.8.0
omniauth-linkedin-oauth2 ~> 1.0.0
omniauth ~> 2.0.4
I even tried running on the active domain in the production server which contains the valid SSL certificate but still, the same error is thrown.

Some informations about LinkedIn for you:
LinkedIn no longer supports the JavaScript SDK. The recommended approach is to use OAuth 2.0 and LinkedIn's Auth APIs.
And:
LinkedIn does not support TLS 1.0. Support for TLS 1.1 has been marked for deprecation starting 02/01/2020. Please use TLS 1.2 when calling LinkedIn APIs. All API requests to api.linkedin.com must be made over HTTPS. Calls made over HTTP will fail.
Step 1: Add Jquery for Javascript library, run command:
$ yarn add jquery
Then, set content of config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
Step 2: Create ssl connection by add thin gem
gem 'thin'
$ bundle install
Edit config/application.rb and add:
config.force_ssl = true
In project command line, type:
$ openssl genrsa 2048 > host.key
$ chmod 400 host.key
$ openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert
After these commands will create two file: host.key and host.cert. Then run:
$ thin start --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
It will run project in default address: https://0.0.0.0:3000. If you want to run on https://localhost:3000, just type:
$ thin start -a localhost --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
Step 3: Create Linkedin oauth2 app.
Go to link: https://www.linkedin.com/developers/
Click the button Create app, then fill the informations to App name, LinkedIn Page (have to finish it by a custom page), App logo, Terms tick box. Then click to Create app to register your app.
At Settings tab, set the domain of your app, I run with localhost so I will set https://localhost:3000.
At Auth tab, save the Client ID and Client Secret to config/application.yml (remember to run commands $ bundle exec figaro install before this) like these:
LINKEDIN_APP_ID: 86g3...sfjm
LINKEDIN_APP_SECRET: OKnb...jzSL
Then edit, type and save to part Authorized redirect URLs for your app:
https://localhost:3000/auth/linkedin/callback
Check available scopes to use in this page! Mine is r_emailaddress r_liteprofile.
At Products tab, select Sign In with LinkedIn, status will change to Review in progress. All is ok if this status disappear after refresh F5 by a while!
Step 4: Set all codes like mine. With simple config/routes.rb:
Rails.application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
get '/auth/linkedin/callback', to: "linkedin#callback"
post '/auth/linkedin/url', to: "linkedin#popup"
post '/auth/linkedin/token', to: "linkedin#get_token"
post '/auth/linkedin/info', to: "linkedin#get_info"
post '/auth/linkedin/out', to: "linkedin#stop"
root to: "linkedin#home"
end
Create app/controllers/linkedin_controller.rb with content:
class LinkedinController < ApplicationController
# Lib to get token
require "uri"
require "net/http"
# Data variables set/get
attr_accessor :client_id, :client_secret, :redirect_uri, :scope, :raise_error
# Class variable with 2#
##token = ""
# Return view linkedins/home page
def home
render 'linkedins/home'
end
# Call back from popup login window of LinkedIn site
def callback
Rails.logger.debug "Callback called! Params:"
Rails.logger.debug params
#code = params[:code]
#state = params[:state]
#redirect = '/auth/linkedin/callback'
# Get token
url = URI("https://www.linkedin.com/oauth/v2/accessToken")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
host_uri = ENV['HOST']+#redirect
request.body = "grant_type=authorization_code&code=#{#code}&client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&redirect_uri=#{host_uri}"
response = https.request(request)
Rails.logger.debug "response.read_body:"
# Rails.logger.debug response.read_body
r = JSON.parse(response.read_body)
Rails.logger.debug r["access_token"]
##token = r["access_token"]
render 'linkedins/callback'
end
# Config init values
def start
#client_id = ENV['LINKEDIN_APP_ID']
#client_secret = ENV['LINKEDIN_APP_SECRET']
#raise_error = 'true'
#redirect = '/auth/linkedin/callback'
#redirect_uri = ENV['HOST']+#redirect
#scope = 'r_emailaddress r_liteprofile'
#state = generate_csrf_token
end
# Return popup url for sign in by LinkedIn, method = POST
def popup
self.start
#url = "https://www.linkedin.com/uas/oauth2/authorization?client_id=#{#client_id}&raise_errors=#{#raise_error}&redirect_uri=#{#redirect_uri}&response_type=code&scope=#{#scope}&state=#{#state}"
# return #url
render json: { status: 'Success', message: 'Load url for popup finished!', link: #url},status: :ok
end
# Get token of current account Linkedin logged
def get_token
Rails.logger.debug 'From get_token, ##token cache:'
Rails.logger.debug ##token
render json: { status: 'Success', message: 'Load token finished!', token: ##token},status: :ok
end
# Get basic info
def get_info
Rails.logger.debug 'From get_info!'
# Create custom api linking
fields = ['id', 'firstName', 'lastName', 'profilePicture']
link = "https://api.linkedin.com/v2/me?projection=(#{ fields.join(',') })"
url = URI(link)
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer #{##token}"
response = https.request(request)
Rails.logger.debug "From get_info, variable response:"
Rails.logger.debug response
r = JSON.parse(response.read_body)
# r = JSON.parse(response.body)
first_name = r['firstName']['localized']['en_US'].to_s
last_name = r['lastName']['localized']['en_US'].to_s
full_name = first_name + " " + last_name
render json: { status: 'Success',message: 'Load link basic info finished!', name: full_name},status: :ok
end
# For logout LinkedIn, by method revoke
def stop
link = 'https://www.linkedin.com/oauth/v2/revoke'
url = URI(link)
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request.body = "client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&token=#{##token}"
response = https.request(request)
Rails.logger.debug "Test logout linkedin!"
render json: { status: 'Success',message: 'Log out finished!'},status: :ok
end
# Genereate random state
def generate_csrf_token
SecureRandom.base64(32)
end
end
Note to install these gems, and we don't need any oauth2 linkedin libs:
gem 'uri'
gem 'net-http'
$ bundle install
We will exit popup LinkedIn login by this callback view app/views/linkedins/callback.html.erb:
<script>
// Close this popup show from LinkedIn window open
close();
</script>
Create this main view app/views/linkedins/home.html.erb:
<p>Linkedin Login Home page</p>
<button id="linkedin-login" type="button">Login</button>
<p id="linkedin-informations">Token here!</p>
<button id="linkedin-logout" type="button">Logout</button>
<p id="linkedin-results">Results here!</p>
<script>
$('#linkedin-login').on('click', function(e){
// e.preventDefault()
var url_popup = ""
var ltoken = ""
var lurl = ""
$.post('/auth/linkedin/url', function(json) {
console.log(json)
url_popup = json.link
if (url_popup != "") {
console.log('Successful to load url popup!')
const w = 600
const h = 600
const top = (screen.height - h) / 4, left = (screen.width - w) / 2
var child = window.open(url_popup, "popupWindow", `width=${w}, height=${h}, top=${top}, left=${left}, scrollbars=yes`)
function checkChild() {
if (child.closed) {
clearInterval(timer);
$.post('/auth/linkedin/token', function(json) {
console.log('Load token link successful!')
$('#linkedin-informations').html('Token is comming ...')
ltoken = json.token
console.log(json.token)
$('#linkedin-informations').html(json.token)
})
$.post('/auth/linkedin/info', function(json) {
console.log('Load info link successful!')
$('#linkedin-results').html('Information is comming ...')
console.log(json)
$('#linkedin-results').html(`Your login account: ${json.name}`)
})
}
}
var timer = setInterval(checkChild, 500);
}
})
})
$('#linkedin-logout').on('click', function(e){
e.preventDefault()
$.post('/auth/linkedin/out', function(json) {
console.log('Log out successful!')
$('#linkedin-results').html(`You logged out!`)
})
})
</script>
Successful screen:

Related

Facebook has detected App isn't using a secure connection followed by domain URL error

My app was working just fine and I could easily log in with facebook, but today, facebook login suddenly gave me this error message:
"Facebook has detected Superhero Maker isn't using a secure connection to transfer information. Until Superhero Maker updates its security settings, you won't be able to use Facebook to log into it". After I closed the error message, I got this error message as well: "The domain of this URL isn't included in the app's domains. To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings."
I did not make any changes to my code, so I'm not sure what's going on. Also, the FACEBOOK_ID and FACEBOOK_SECRET match the app ID and app Secret from Facebook.
routes.rb
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#facebookAuth'
get 'auth/failure', to: redirect('/')
end
omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_SECRET']
end
.env
FACEBOOK_ID=[facebook_id_here]
FACEBOOK_SECRET=[facebook_secret_here]
sessions_controller.rb
class SessionsController < ApplicationController
def facebookAuth
access_token = request.env["omniauth.auth"]
#user = User.from_omniauth(access_token)
#user.facebook_token = access_token.credentials.token
refresh_token = access_token.credentials.refresh_token
#user.facebook_refresh_token = refresh_token if refresh_token.present?
if #user.save
log_in(#user)
redirect_to user_path(#user)
else
redirect_to login_path
end
end
end
Facebook Login Setting
screenshot
Option 1:
You should disable HTTP Enforce and Valid OAuth uri must be http:// not https://
Option 2:
You can create a local ssl certificate and run with rails s -b 'ssl://localhost:3000?key=localhost.key&cert=localhost.crt'
Check these steps to login facebook without Devise gem, for Rails 6.1.3.2!
Step 1: Create ssl connection by add thin gem
gem 'thin'
$ bundle install
Edit config/application.rb and add:
config.force_ssl = true
Run Terminal and type:
$ openssl genrsa 2048 > host.key
$ chmod 400 host.key
$ openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert
After these commands will create two file: host.key and host.cert. Copy them to ssl folder of your project location then run:
$ thin start --ssl --ssl-key-file=./ssl/host.key --ssl-cert-file=./ssl/host.cert
It will run project in default address: https://0.0.0.0:3000. If you want to run on https://localhost:3000, just type:
$ thin start -a localhost --ssl --ssl-key-file=./ssl/host.key --ssl-cert-file=./ssl/host.cert
Step 2: Add Jquery for Javascript library, run command:
$ yarn add jquery
Then, set content of config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
Step 3: Add related gems and contents to omniauth
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-rails_csrf_protection'
$ bundle install
Set .env at root folder:
FACEBOOK_APP_ID=166...30
FACEBOOK_APP_SECRET=165a14...77e1d
Set init content for config/initializers/omniauth.rb:
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET']
end
Edit config/routes.rb:
Rails.application.routes.draw do
get '/auth/:provider/callback', to: 'sessions#facebookAuth'
root to: "pages#home"
end
Create app/controllers/sessions_controller.rb, it's main controller for facebook login processing:
class SessionsController < ApplicationController
def facebookAuth
access_token = request.env["omniauth.auth"]
# #user = User.from_omniauth(access_token)
# #user.facebook_token = access_token.credentials.token
# refresh_token = access_token.credentials.refresh_token
# #user.facebook_refresh_token = refresh_token if refresh_token.present?
# if #user.save
# log_in(#user)
# redirect_to user_path(#user)
# else
# redirect_to login_path
# end
Rails.logger.debug "Get name:"
Rails.logger.debug access_token["info"]["name"]
render json: { status: 'SUCCESS', message: 'Loaded facebook finished!', name: access_token["info"]["name"] }, status: :ok
end
end
Add to app/javascript/packs/application.js:
require("jquery")
window.fbAsyncInit = function() {
FB.init({
appId: '166...30',
version: 'v4.0',
cookie: true // IMPORTANT must enable cookies to allow the server to access the session
});
console.log("fb init");
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
And create simple controller app/controllers/pages_controller.rb:
class PagesController < ApplicationController
def home
end
end
Add this file app/views/pages/home.html.erb to finish this question task:
Welcome to project railstrace !
<div id="fb-root"></div>
<p id="connect">
Connect to FB!
</p>
<p id="results" />
<script type="text/javascript">
$('a').click(function(e) {
e.preventDefault();
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
$('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
// since we have cookies enabled, this request will allow omniauth to parse
// out the auth code from the signed request in the fbsr_XXX cookie
$.getJSON('/auth/facebook/callback', function(json) {
$('#connect').html('Connected! Callback complete.');
$('#results').html(JSON.stringify(json));
});
}
}); // if you want custom scopes, pass them as an extra, final argument to FB.login
});
</script>
If you have any problems don't hesitate to tell me! Thank you!

Getting authorization from iNaturalist for API

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 %>

Why is QuickBooks API not returning a refresh token?

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!

Sinatra doesn't know this ditty?

I am trying to setup the Spotify IOS API but everytime I run this Ruby file and go to http://localhost:1234/swap I get "Sinatra doesn't know this ditty".
Here is my code:
require 'sinatra'
require 'net/http'
require 'net/https'
require 'base64'
require 'json'
require 'encrypted_strings'
# This is an example token swap service written
# as a Ruby/Sinatra service. This is required by
# the iOS SDK to authenticate a user.
#
# The service requires the Sinatra and
# encrypted_strings gems be installed:
#
# $ gem install sinatra encrypted_strings
#
# To run the service, enter your client ID, client
# secret and client callback URL below and run the
# project.
#
# $ ruby spotify_token_swap.rb
#
# IMPORTANT: The example credentials will work for the
# example apps, you should use your own in your real
# environment. as these might change at any time.
#
# Once the service is running, pass the public URI to
# it (such as http://localhost:1234/swap if you run it
# with default settings on your local machine) to the
# token swap method in the iOS SDK:
#
# NSURL *swapServiceURL = [NSURL urlWithString:#"http://localhost:1234/swap"];
#
# -[SPAuth handleAuthCallbackWithTriggeredAuthURL:url
# tokenSwapServiceEndpointAtURL:swapServiceURL
# callback:callback];
#
print "\e[31m------------------------------------------------------\e[0m\n"
print "\e[31mYou're using example credentials, please replace these\e[0m\n"
print "\e[31mwith your own and remove this silly warning.\e[0m\n"
print "\e[31m------------------------------------------------------\e[0m\n"
print "\7\7"
sleep(2)
CLIENT_ID = ""
CLIENT_SECRET = ""
ENCRYPTION_SECRET = ""
CLIENT_CALLBACK_URL = "dawgone://returnhere"
AUTH_HEADER = "Basic " + Base64.strict_encode64(CLIENT_ID + ":" + CLIENT_SECRET)
SPOTIFY_ACCOUNTS_ENDPOINT = URI.parse("https://accounts.spotify.com")
set :port, 1234 # The port to bind to.
set :bind, '0.0.0.0' # IP address of the interface to listen on (all)
post '/swap' do
# This call takes a single POST parameter, "code", which
# it combines with your client ID, secret and callback
# URL to get an OAuth token from the Spotify Auth Service,
# which it will pass back to the caller in a JSON payload.
auth_code = params[:code]
http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
http.use_ssl = true
request = Net::HTTP::Post.new("/api/token")
request.add_field("Authorization", AUTH_HEADER)
request.form_data = {
"grant_type" => "authorization_code",
"redirect_uri" => CLIENT_CALLBACK_URL,
"code" => auth_code
}
response = http.request(request)
# encrypt the refresh token before forwarding to the client
if response.code.to_i == 200
token_data = JSON.parse(response.body)
refresh_token = token_data["refresh_token"]
encrypted_token = refresh_token.encrypt(:symmetric, :password => ENCRYPTION_SECRET)
token_data["refresh_token"] = encrypted_token
response.body = JSON.dump(token_data)
end
status response.code.to_i
return response.body
end
post '/refresh' do
# Request a new access token using the POST:ed refresh token
http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
http.use_ssl = true
request = Net::HTTP::Post.new("/api/token")
request.add_field("Authorization", AUTH_HEADER)
encrypted_token = params[:refresh_token]
refresh_token = encrypted_token.decrypt(:symmetric, :password => ENCRYPTION_SECRET)
request.form_data = {
"grant_type" => "refresh_token",
"refresh_token" => refresh_token
}
response = http.request(request)
status response.code.to_i
return response.body
end
This is because swap is a POST endpoint. When you pull up a URL in your browser you are doing an HTTP GET.
If you want to see that the sinatra service is running and you can at least talk to it you could try hitting it with curl from the command line with the right POST parameters.

RoR login into page and post data to remote url

here's my problem:
I need to post data from RoR server to remote PHP server, to a specific url, but before that I need to authenticate.. any help is much appreciated..
What I have done so far..
#sample data
postparams ={'id'=>1, 'name'=>'Test', 'phone'=>'123123123'}
#url - is in form http://domain.com/some/somemore
#user - contains username
#pass - contains password
require "uri"
require "net/http"
uri = URI(url)
req = Net::HTTP::Post.new(uri.path)
req.set_form_data(postparams)
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
case res
when Net::HTTPSuccess, Net::HTTPRedirection
#all ok
else
res.value
end
Obviously I get 403.. because I'm not authorized? How do I authorize?
I also tried my luck with mechanize gem (below - using the same "sample" data\vars)
#when not logged in it renders login form
login_form = agent.get(url).forms.first
login_form.username = user
login_form.password = pass
# submit login form
agent.submit(login_form, login_form.buttons.first)
#not sure how to submit to url..
#note that accessing url will not render the from
#(I can't access it as I did with login form) - I simply need to post postparams
#to this url... and get the response code..
I think the mechanize gem is your best choice.
Here is an example showing how to post a file to flicker using mechanize.
Maybe you could easily adapt to your needs:
require 'rubygems'
require 'mechanize'
abort "#{$0} login passwd filename" if (ARGV.size != 3)
a = Mechanize.new { |agent|
# Flickr refreshes after login
agent.follow_meta_refresh = true
}
a.get('http://flickr.com/') do |home_page|
signin_page = a.click(home_page.link_with(:text => /Sign In/))
my_page = signin_page.form_with(:name => 'login_form') do |form|
form.login = ARGV[0]
form.passwd = ARGV[1]
end.submit
# Click the upload link
upload_page = a.click(my_page.link_with(:text => /Upload/))
# We want the basic upload page.
upload_page = a.click(upload_page.link_with(:text => /basic Uploader/))
# Upload the file
upload_page.form_with(:method => 'POST') do |upload_form|
upload_form.file_uploads.first.file_name = ARGV[2]
end.submit
end
I strongly suggest the use of ruby rest-client gem.

Resources