Sinatra with Rails reset session every request - ruby-on-rails

I have a rails web application and I need to create API for mobile clients. I choose a Sinatra web framework for this. But I have a problem with my Sinatra app, after every request all data session lost.
My API looks like this(lib/api/core.rb):
module Api
class Core < Sinatra::Base
set :session_secret, 'secret'
enable :sessions
get '/foo' do
content_type :json
session['foo'] = 'some value'
end
get '/bar' do
content_type :json
session['foo']#everytime is nil
end
end
end
In my route.rb I wrote this:
constraints :subdomain => 'api' do
mount Api::Core => '/'
end
I use Rails 3.2.8, Sinatra 1.3.3
And my questions is how can I store data between requests(it's need me for authentication) ?

Your APIs should be stateless. Authentication is usually done with tokens that are sent along with every request. See RailsCast #352 Securing an API for more info.

Related

Callbacks in Ruby on Rails

Using the 'oauth2' gem and a Heroku server, I have managed to create a client object and redirect the user to the login site:
client = OAuth2::Client.new(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
:authorize_url => "/oauth/authorize",
:token_url => "/oauth/token",
:site => "https://connect.xxxxxxxxxx.com")
redirect_to(client.auth_code.authorize_url(:redirect_uri => 'https://xxxxx.herokuapp.com/callback'))
The browser afterwards redirects itself to the callback link as intended, something like:
https://xxxxx.herokuapp.com/callback?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I need to access the authorization code to then send a post request for the access token and refresh token, but being totally new to Ruby and Ruby on Rails, I am not sure how to get the callback and parse the code out. All of the dozen tutorials/documentation I've researched just mention that the authorization code should be 'magically obtained,' but I'm not sure how exactly that works explicitly. I tried creating a 'callback' controller and view to no avail - is there something missing in the routes files possibly? Help is much appreciated!
Your CallbackController will start to look like this maybe:
class CallbackController < ApplicationController
def index
access_token = client.auth_code.get_token(params[:code], redirect_uri: 'https://xxxxx.herokuapp.com/callback')
# Now you have an OAuth2::AccessToken object that you can either use to:
# - make direct requests to the API
# - or access access_token.token, access_token.refresh_token, access_token.expires_at, access_token.expires_in and store those
# somewhere for later use
# http://www.rubydoc.info/github/intridea/oauth2/OAuth2/AccessToken
end
private
def client
#client ||= OAuth2::Client.new(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authorize_url: "/oauth/authorize",
token_url: "/oauth/token",
site: "https://connect.xxxxxxxxxx.com"
)
end
end

Rails - Add custom headers to response based on API version

What I am trying to do is pretty simple. There are multiple versions of a Rails REST API. So, there are routes like:
http://www.example.com/v1/user.json
http://www.example.com/v2/user.json
http://www.example.com/v3/user.json
What I want to do is add custom http headers to the response based on the API version endpoint that is requested.
In my config/application.rb file, I tried:
config.action_dispatch.default_headers.merge!('my_header_1' => 'my_value_1', 'my_header_2' => 'my_value_2')
I have also tried this in my config/routes.rb file:
scope path: "v1", controller: :test do
get "action_1" => :action_1
get "action_2" => :action_2
Rails.application.config.action_dispatch.default_headers.merge!('my_header_1' => 'my_value_1', 'my_header_2' => 'my_value_2')
end
But both of these snippets append custom headers to the response irrespective of the API version endpoint.
I think I can write a middleware that checks the request url and appends the response headers based on that but it sounds a bit hackish.
Is there a better way to achieve this? Preferably via config or some central piece of code?
What about using a before_action on your controllers? I imagine each API version has its own controllers? That way you could do something like:
class API::V1::BaseController < ApplicationController
before_action :set_headers
protected
def set_headers
response.headers['X-Foo'] = 'V1'
end
end

Async_Sinatra in Rails: async actions can't write to shared session

I have a Sinatra class in a Rails project. It uses eventmachine and async_sinatra to make asynchronous calls to external sites. I'd like to write to a session object (ideally, the same one that Rails is using), but so far I can only:
write to a separate session object from Rails' (by default, Sinatra names its session something different from Rails)
write to the same session for synchronous calls only
When I make asynchronous calls, sessions written in the async_sinatra code don't get pushed out to the client machine. I suspect one of two things is happening:
The header has already been sent to the client and the local variable storing the session (in Sinatra) will be flushed out at the end of the action. The client would never see a request from the server to save this data to a cookie.
The header is being sent to the client, but Rails immediate sends another, instructing the client to write to the cookie what Rails has stored in its session variable, overwriting what Sinatra wrote.
Either way, I'd like to just get simple session functionality in both Sinatra and Rails. An explanation of what I'm doing wrong would also be nice :)
A full working copy of the code is on github, but I believe the problem is specifically in this code:
class ExternalCall < Sinatra::Base
use ActionDispatch::Session::CookieStore
register Sinatra::Async
get '/sinatra/local' do
session[:demo] = "sinatra can write to Rails' session"
end
aget '/sinatra/goog' do
session[:async_call]="async sinatra calls cannot write to Rails' session"
make_async_req :get, "http://www.google.com/" do |http_callback|
if http_callback
session[:em_callback] = "this also isn't saving for me"
else
headers 'Status' => '422'
end
async_schedule { redirect '/' }
end
end
helpers do
def make_async_req(method, host, opts={}, &block)
opts[:head] = { 'Accept' => 'text/html', 'Connection' => 'keep-alive' }
http = EM::HttpRequest.new(host)
http = http.send(method, {:head => opts[:head], :body => {}, :query => {}})
http.callback &block
end
end
end
EDIT 7/15:
Changed code on Github to include Async-Rack. Async-sinatra can write to sessions when they are not shared with Rails. Compare the master and segmented_sessions branches for behavior difference. (Or on the master branch, change use ActionDispatch::Session::CookieStore to enable :sessions)
This is because async_sinatra uses throw :async by default, effectively skipping the session middleware logic for storing stuff. You could override async_response like that:
helpers do
def async_response
[-1, {}, []]
end
end

Problems with OAuth 2 Gem

I have a rails project using https://github.com/intridea/oauth2. In my rails app I have the following code:
ApplicationController.rb
def facebook_client
OAuth2::Client.new(FacebookOauthCredentials::APP_ID, FacebookOauthCredentials::APP_SECRET, :site => 'https://graph.facebook.com')
end
FacebookController.rb
def facebook_session_create(poster, featured_item)
redirect_to facebook_client.web_server.authorize_url(:scope => 'publish_stream', :redirect_uri => "http://localhost:3000/facebook/facebook_callback")
end
def facebook_callback
if(params[:code])
begin
access_token = facebook_client.web_server.get_access_token(params[:code], :redirect_uri => "http://localhost:3000/facebook/facebook_callback")
access_token.post('/me/feed', "testing #{rand(1000)}")
rescue OAuth2::HTTPError => e
render :text => e.response.body
end
end
end
Every time I run this code I get this response:
{"error":{"type":"OAuthException","message":"Error validating verification code."}}
However, I use the sinatra app supplied in the OAuth2 gem's readme file, it works fine.
def client
OAuth2::Client.new(FacebookOauthCredentials::APP_ID, FacebookOauthCredentials::APP_SECRET, :site => 'https://graph.facebook.com')
end
get '/auth/facebook' do
redirect client.web_server.authorize_url(
:redirect_uri => redirect_uri,
:scope => 'publish_stream'
)
end
get '/auth/facebook/callback' do
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => redirect_uri)
begin
user = JSON.parse(access_token.post('/me/feed', :message => "testing # {rand(10000)}"))
rescue Exception => e
return e.response.body
end
user.inspect
end
def redirect_uri
uri = URI.parse(request.url)
uri.path = '/auth/facebook/callback'
uri.query = nil
uri.to_s
end
I have tried reproducing the steps using irb, but I an http 400 error. I'm not sure if it's for the same reason as the rails app, or if it's because I'm doing a hybrid of console and web browser operation. Any suggestions would be greatly appreciated.
I found the answer to my problem on this page Facebook graph API - OAuth Token
I ran into the exact same problem but
it turned out the issue is not the
encoding of the redirect_uri
parameter, or that I had a trailing
slash or question mark it's simply
that I passed in two different
redirect urls (had not read the
specification at that time).
The redirect_uri is only used as a
redirect once (the first time) to
redirect back to the relying party
with the "code" token. The 2nd time,
the redirect_uri is passed back to the
auth server but this time it's not
used as you'd expect (to redirect)
rather it's used by the authentication
server to verify the code. The server
responds with the access_token.
You'll notice facebook documentation
(which is terrible) says fetch
"Exchange it for an access token by
fetching ....
"
In summary, I didn't have to encode or
do anything special to the Uri, just
pass in the same redirect_uri twice,
and fetch the 2nd page to get the
access_token inside.
I didn't copy my original code correctly and the redirect uri I was passing to get the code was different than the uri I was passing to get the access token. Facebook's API documentation is terrible :(
I had this problem/error but finally found a different solution (by comparing my Dev app settings which was working) to my prod app settings (which was generating this error).
I went to the advanced settings page for my app in the Facebook Developer app:
https://developers.facebook.com/apps/YourAppID/advanced
Then I found the "Encrypted Access Token:" setting and turn it to "Enabled."

Posting xml to a rest api in rails

I need to post some xml info to a restful api can anyone give me a clue of how to do this? I'm using rails.
In rails, using ActiveResource, you do it like this:
class PersonResource < ActiveResource::Base
self.site = "http://api.people.com:3000/"
self.proxy = "http://user:password#proxy.people.com:8080"
end
ryan = Person.new(:first => 'Ryan', :last => 'Daigle')
# the next line posts this object serialized to xml to the configured url
ryan.save # => true
http://api.rubyonrails.org/classes/ActiveResource/Base.html
If the site you are posting to has a custom API (not active resource) you must use Net:HTTP

Resources