Devise with Sinatra - ruby

Does anyone had installed Devise gem with Sinatra?
Devise is based on Warden and so it should work on Sinatra, I couldn't find any related info about how to implement it.

Devise is really just a Rails-centric wrapper with nice helpers for warden, which is the underlying Rack authentication framework. So if you're using Sinatra in conjunction with Rails, you can use Devise in your Rails app, and use warden directly in your Sinatra app, and they will see the same user session data.
So no, you can't use Devise directly within your Sinatra app, but if you're building a modular app with some pieces in Rails, and other pieces in Sinatra, you can use Devise/Warden among the components.

Devise is designed for Rails only. You can't use it with Sinatra.
You can check out:
https://github.com/maxjustus/sinatra-authentication
http://www.gittr.com/index.php/archive/sinatra-basic-authentication-selectively-applied/
https://gist.github.com/243611

There is also https://github.com/jsmestad/sinatra_warden available.

I was able to get it working. There were a few main aspects:
Get Devise working with Rails (Devise is a Rails app, won't work without it)
Setup the mapping (route) on Rack level to support both Rails and Sinatra
Share the sessions between Rails and Sinatra
Setup Warden and make it available to Sinatra
Here is most relevant part of code from /config.ru:
#
# ...
# Rest with Rails
map "/" do
run MyApp::Application
end
# Anything urls starting with /slim will go to Sinatra
map "/slim" do
# make sure :key and :secret be in-sync with initializers/secret_store.rb initializers/secret_token.rb
use Rack::Session::Cookie, :key => '<< see, initializers/secret_store.rb >>', :secret => '<< copy from initializers/secret_token.rb >>'
# Point Warden to the Sinatra App
use Warden::Manager do |manager|
manager.failure_app = AppMain
manager.default_scope = Devise.default_scope
end
# Borrowed from https://gist.github.com/217362
Warden::Manager.before_failure do |env, opts|
env['REQUEST_METHOD'] = "POST"
end
run AppMain
end
See, http://labnote.beedesk.com/sinatra-warden-rails-devise for a complete solution.

Related

Mount a Rack App Explicitly Inside a Sinatra::App

I need to mount another Rack app from within my Sinatra app.
So instead of this:
run Rack::URLMap.new('/' => MyApp, `/other` => OtherApp)
I want to...
class MyApp < Sinatra::Base
# ...mount the other rack app in here.
end
Yes, I understand this might not be the beaten path... but that's precisely why I'm asking. Trust me, I have good reasons.

Difference in cookie content using Sinatra session & Rack::Session::EncryptedCookie

I'm learning Sinatra framework & developing a login system. I came across two ways of using cookies.
A simple Sinatra inbuilt way:
enable :sessions
set :session_secret, 'random-key'
This approach produces following cookie content while logged in (used session.inspect to get the output):
{"session_id"=>"6be0b9a31831604ba51114d265ba952482e0b2da6ced6c54e15ebe7f212858ca",
"tracking"=>{"HTTP_USER_AGENT"=>"b8c1e8f89eeaea0b825bed0d811f0c7678e98c74",
"HTTP_ACCEPT_ENCODING"=>"a0bfc876d68fe7aea700da5ea8925abac6f2f794",
"HTTP_ACCEPT_LANGUAGE"=>"dd065ed263c67d799f943ab6c39b55c5e008cbb5"},
"csrf"=>"b480324f510e4f391d15cee8236a8fb74a5aaa5ce2f9ad38e4dbb025a823b16e",
"name"=>"john"}
Another approach is using an encrypted cookie :
require 'sinatra'
require 'encrypted_cookie'
use Rack::Session::EncryptedCookie, :secret => "random-key"
But this approach produces following cookie content while logged in (used session.inspect here too):
{:name=>"john"}
Why enable :sessions is creating such a big cookie with all that information & why is it required (especially those HTTP_... parts?) Because Rack::Session::EncryptedCookie isn't generating any of it.
Do you think that using enable :sessions should be preferred because it has csrf token & session id? Or do you think that Rack::Session::EncryptedCookie is enough since it is encrypted?
I have following versions of gems installed :
encrypted_cookie (0.0.4)
rack (1.5.2)
rack_csrf (2.4.0)
sinatra (1.4.3)
thin (1.5.1)
Please tell me if you need more information...
Because Sinatra will use rack-protection middleware when you enable :sessions. It makes cookie bigger but more secure.
Relevant snippet:
def setup_default_middleware(builder)
builder.use ExtendedRack
builder.use ShowExceptions if show_exceptions?
builder.use Rack::MethodOverride if method_override?
builder.use Rack::Head
setup_logging builder
setup_sessions builder
setup_protection builder
end
def setup_sessions(builder)
return unless sessions?
options = {}
options[:secret] = session_secret if session_secret?
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
builder.use session_store, options
end
def setup_protection(builder)
return unless protection?
options = Hash === protection ? protection.dup : {}
options = {
img_src: "'self' data:",
font_src: "'self'"
}.merge options
protect_session = options.fetch(:session) { sessions? }
options[:without_session] = !protect_session
options[:reaction] ||= :drop_session
builder.use Rack::Protection, options
end
sessions? returns true if you enable :sessions
session_store is Rack::Session::Cookie by default
The difference between Rack::Session::EncryptedCookie
That is, if you want to use Rack::Session::EncryptedCookie with rack-production, it can be easily done by:
enable :sessions
set :session_store, Rack::Session::EncryptedCookie
FYI, since encrypted_cookie is lack of some features (secret rotation, custom serializer, etc) and no longer under maintenance, I made another one to replace it.
Hope it helps.
Because Rack::Session::EncryptedCookie requires that your secret be at least 16 bits long. In the README, they recommend using OpenSSL for generating the secret, like so:
ruby -ropenssl -e "puts OpenSSL::Random.random_bytes(16).inspect"
If you open your inspector, you'll see a cookie named 'rack.session', and its contents obfuscated.
As I know, when using Rack::Session::Cookie in Sinatra, and write session_secret as an environment variable, the session which has created won't destroy after the project deploy. I think this is a risk in Single Page Application.

Strange issue in Sinatra

ok so this is very strange (well is to me), everything in my master branch works fine, I then created a new branch called twitter to conduct some twitter feed implementation. I have done this and was working yesterday on my linux machine.. I have pulled the branch today in a windows environment but when i load the app i now get the regular Sinatra 404 Sinatra doesn’t know this ditty.
This is my profile.rb file
require 'bundler/setup'
Bundler.require(:default)
require 'rubygems'
require 'sinatra'
require './config/config.rb' if File.exists?('./config/config.rb')
require 'sinatra/jsonp'
require 'twitter'
require 'sinatra/static_assets'
class Profile < Sinatra::Base
helpers Sinatra::Jsonp
enable :json_pretty
register Sinatra::StaticAssets
##twitter_client = Twitter::Client.new(
:consumer_key => ENV["CONSUMER_KEY"],
:consumer_secret => ENV["CONSUMER_SECRET"],
:oauth_token => ENV["OAUTH_TOKEN"],
:oauth_token_secret => ENV["OAUTH_SECRET"],
)
get '/' do
erb :index
end
get '/feed' do
jsonp ##twitter_client.user_timeline('richl14').map(&:attrs)
end
end
Config.ru
require './profile'
run Profile
Does anyone have any ideas of what i need to be looking at to solve this? Can anyone speak from experience with this?
Thanks
When you use the classic Sinatra style you use require 'sinatra' and then add routes to the top level. These routes get added to the Sinatra::Application. When you directly run this file, e.g. with ruby my_app.rb, Sinatra runs a built in web server, which will serve the Sinatra::Application app.
When you use the modular style, you use require 'sinatra/base', and then add routes to your Sinatra::Base subclass. In this case directly executing the file doesn’t start the built in server.
In your case you are using the modular style, but have used require 'sinatra'. You create your Profile app, but when you run the file directly Sinatra launches the built in server and serves the Sinatra::Application app. Since you haven’t added any routes to this (they’ve all been added to Profile) it runs but all requests return 404.
One way to get your app to launch you is to use rackup. This will launch the Profile app that you have explicitly set in your config.ru. (Explicitly starting your webserver will also work, e.g. using thin start).
Another possibility would be to add a line like this to the end of your Profile class:
run! if app_file == $0
This tells Sinatra to start the build in server running the Profile app if the file is the same as the Ruby file being executed, in a similar way to how the classic style app is launched. If you use this method you should change require 'sinatra' to require 'sinatra/base' otherwise you will get two servers launched, one after the other (in fact you should probably make that change anyway).
See the Sinatra docs for more info about the difference between classic and modular style.

Multiple Sinatra apps using rack-mount

I have a question regarding using rack-mount with Sinatra. I've got two classic-style Sinatra apps. Let's call one App defined in app.rb and the other API defined in api.rb.
I would like it so that api.rb handles all routes beginning with '/api' and app.rb handles all other requests including the root ('/').
How would I set this up with rack-mount? Or is there a better solution than that?
I think you'll prefer Rack::URLMap - it will probably look something like this:
run Rack::URLMap.new("/" => App.new,
"/api" => Api.new)
That should go in your config.ru file.
I had a similar issue and I am not very familiar with Rack. I could not figure out what to do based on the answers above. My final solution was to have the following in config.ru.
This works perfectly for me.
# Main Ramaze site
map "/" do
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
require ::File.expand_path('../app', __FILE__)
Ramaze.start(:root => __DIR__, :started => true)
run Ramaze
end
# Sinatra & Grape API
map "/api" do
use Rack::Static, :urls => ["/stylesheets", "/images", "/javascripts"], :root => "public"
use Rack::Session::Cookie
run Rack::Cascade.new([
MySinatraApp::Application,
MySinatraApp::API])
end
In config.ru you could also take advantage of Sinatra's middleware feature. If you have several Sinatra apps, each with its own routes, and want to run them simultaneously, you can arrange them in the order you want them found, e.g.
# config.ru
...
use MyAppA
use MyAppB
use MyAppC
run MyAppD
I had the same problem once and so I came up with this template: sinatra-rspec-bundler-template which is layed out for multiple apps.
It may have more features than you need but it should help you when you need something "a bit more" complex.

Automatically add rack middleware with a gem

I have a gem that provides some rack middleware, the only way I can get it to work is to place this in my application.rb
config.middleware.use "TBBC::Editor::Middleware"
How can I make it so that this middleware is automatically used when my gem is used in an apps Gemfile?
If you intend your gem to be used with Rails 3, you could provide a Railtie. You can then automatically load it in case Rails is used.
Assuming your gem's name is tbbc, place this in lib/tbbc/railtie.rb:
module TBBC
class Railtie < Rails::Railtie
initializer "tbbc.insert_middleware" do |app|
app.config.middleware.use "TBBC::Editor::Middleware"
end
end
end
In lib/tbbc.rb:
require "tbbc/railtie" if defined? Rails
You can't automatically add the middleware to generic Rack apps. For non-Rails applications, this will be something the user has to do.

Resources