Ragarding ruby on rails sessions - session

In my rails application, I'm using a devise gem for user sign in and sign out.
After a successful sign in, I want to store data in the user's session. How can I do this?

This may help you.
To verify if a user is signed in, use the following helper:
user_signed_in?
For the current signed-in user, this helper is available:
current_user
You can access the session for this scope:
user_session
As you mentioned need to add data to session code goes here
adding this to my config/initializers/devise.rb file
Warden::Manager.after_authentication do |user,auth,opts|
auth.raw_session['warden.user.user.email'] = user.email
auth.raw_session['warden.user.user.username'] = user.username
end

Related

Authentication for Sinatra REST API app

I'm building an API with Sinatra (using Angular for the client side and want others to have access to API) and have it also be an OAuth provider. I am wondering what the best route to take (work off existing gems or roll own solution off Warden or something).
Have used devise and doorkeeper for authentication and oauth before with Rails, wondering what best solution for Sinatra is.
Ideally I don't want the views or be able to extend/mod the actions of an existing solution, as I'm interacting with it purely as an API.
I just recently did the same thing using the following answer from S/O
What is a very simple authentication scheme for Sinatra/Rack
It implies a user model, but instead of using that, I just set a user and admin password in my config file. Then I had a login form that just took a password. When the user enters that password, I checked it against the one in settings and set the session['user'] to :admin or :user according to whichever it matched (or nil if none). Then on each of my routes, I called auth: :user or auth: :admin accordingly.
APIs normally accept your login request and send you an authentication token which you need to pass back in each call. This is very similar to cookie based sessions where your browser automatically passes back the cookie which is acquired on initial website visit.
From what I've seen in Sinatra's docs, you could make a session-based authentication system like this:
enable :session
disable :show_exceptions
use Rack::Session::Pool,
key: 'session_id'
post '/login' do
user = User.login_success(params)
halt 401 if user.nil?
session[:user] = user
200
end
get '/fun' do
user = session[:user]
halt 401 if user.nil?
halt 403 if !user.has_permission_for '/fun'
"fun was had"
end
Now all you need to do in your client is to pass back the cookie token returned in response to initial visit when requesting an API function. This can be done with any web client library that supports cookie stores (such as libcurl) or by inserting the session cookie into the request header manually. Rack::Minitest functionality also supports cookies, so you can test your API with minitest.
See Sinatra API Authentication.
Quick summary:
Sinatra has no built-in auth.
It's best to build auth yourself (see the link).
There are gems available, but you probably won't need them for something as simple as an API.

RubyCAS with a persistent single ticket & Sinatra

Alright, I'm a huge RubyCAS noob, and this is driving me crazy.
I have installed the rubycas-client gem, and have followed along with the official Sinatra setup at this repo. The way this setup is done, every time I request a page, I receive a one-time ticket that's appended to the URL as a query like so:
http://localhost:9393/?ticket=ST-1373928850... etc.
If I refresh the page, I get a Sinatra error saying the ticket has already been used up!
I've two questions, then.
Is the ticket-per-reload standard behavior?
How do I save my CAS login for a session and still retain single sign-out?
What I've done:
I have gone and tried to implement :sessions in Sinatra, but this causes single-sign-out to fail.
I have gone and done my best to follow the steps in the rubycas-client GitHub Repo (replacing ActiveRecord session storage with Sinatra's :session helper).
The RubyCAS documentation for Sinatra is fairly poor, so I'm looking for a definitive answer to this.
It is doing the correct thing when you try and reload the page with the same ticket. That ticket has already been validated. When you get the validation response you need to then set your own applications cookie or other session option.
I usually add a method that will add a session attribute to the user's cookie like:
session["cas"]["username"] = <user from cas validation response>
Then in future requests the Sinatra application can protect whatever routes you want with a helper method like:
cas = RestClient::Resource.new "#{cas_url}/login", :timeout => 5
checked = cas.get
return true if checked.code == 200
In my configure block for Sinatra I do this:
use Rack::Session::Cookie, :key => "example.com",:secret => "veryrandomhex"
I hope this helps, have any questions let me know.
UPDATE BELOW
While discussing this problem, we've uncovered that RubyCas says to not use a regular cookie session for your ruby application in production, while using CAS. What you'll want to do is:
A. Make sure your cookie expires at the same time or sooner than the CAS cookie
And/Or
B. Make sure your cookie is per browser session, then revalidate the CAS user on next browser session.
For Rack cookie you would specify this extra config for when the cookie is set to expire: :expire_after => 1440, (where 1440 is in minutes)
In case of the ruby CAS there are two kinds of session :
(1). The application session.
(2). The Single sign on (SSO) session.
you can use sinatra-session gem for managing the application session and just use session_end! helper method to destroy the application session. For destroying the SSO session unset the session[:cas_ticket] parameter in log out route.
example:
In case of the Sinatra:
get '/logout' do
session_end! # provided by sinatra-session gem
session[:cas_ticket] = nil # session variable set by CAS server
end
here we are explicitly setting the session[:cas_ticket] to nil, however you can use session.clear in logout route to destroy the current session data .

Sessions & User Authentication in Padrino

I'm very new to Padrino (I come from a PHP background), and ruby web frameworks in general, and have been trying to figure out how to implement a simple user authentication and session management system, but have not really found much documentation on the subject. I know that padrino comes with a pre-built "Admin" package that includes user login/authentication, ect, however I'd rather roll my own from scratch, rather than trying to customize their solution to fit my own needs.
So my question is, in Padrino how would I go about implementing a simple session-based authentication system for logging in users by setting session data once a user/pass combo has been validated against the database, retrieving that session data to check if the user is logged in when a request is made to protect certain pages/resources, use the session data to get the user's ID/role/ect, and then destroy that session when user logs out. As a PHP programmer I'm used to using the $_SESSION superglobal for this purpose, is there something akin to this in padrino/ruby? I noticed there is a enable :sessions in app.rb, is :sessions the pardrino equivalent?
Yup,
session[:cart] = cart_id
Cart.find(session[:cart].to_i) if session[:cart].present?
For authentication purposes you can avoid padrino-admin and builtin auth using a more more simple way:
# in app.rb
use Rack::Auth::Basic, 'Restricted Area' do |username, password|
user == 'admin' and password == 'pwd'
end
If you need to control a bit more your sessions/cookies you can use:
set :sessions,
:key => '__awesome_key',
:secret => 'awesome_password',
:expire_after => 1.year

Ruby On Rails authentication with Devise and (warden) callbacks

I have a rails app that is using Devise, with a User model, no scope.
I've also added activeadmin gem in to the app, Active Admin is a gem used for adding an admin dashboard in your application. It uses Devise for logging in users and creates a separate admin_user model for the admins.
I am allowing anonymous, non-logged in users to create shopping carts, creating a session[:cart_id]. If a user logs in I want associate the user with the cart, something like
Cart.find(session[:cart_id]).user = current_user
I was planning to use Wardens callbacks wardens callbacks to impliment this, something like so :
Warden::Manager.after_set_user :scope => :user do |user, auth, opts|
Cart.find(session[:cart_id]).user = user
end
However I get an error if I do that:
<% unless user_signed_in? %> throws an error :admin_user user is not logged in
Anyone got any ideas what is going on?
I've looked at related questions, but no help:
How to access session from Warden/Devise after_authentication callback in Rails
Where should warden callbacks be placed in a rails app?
The AdminUser model that Active Admin uses also executes this callback. So, maybe, an if can solve your problem:
Warden::Manager.after_set_user :scope => :user do |user, auth, opts|
Cart.find(session[:cart_id]).user = user if user.class == User
end
Actually it turned out the issue was solved by setting the default scope in warden,in the devise initializer file.
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
Since the active admin routes were added above the devise routes for my user, the adminuser became the default user.

Sinatra App with multiple users

What is the easiest way to allow for multiple users in a Sinatra web app. I've previously used an authorization class that allows for one username and password, but what if I want to allow users to sign up for a simple web app and allow them all their own login credentials?
Thank you so much!
If HTTP basic auth is sufficient I'd recommend defining two methods like this:
helpers do
def protected!
unless authorized?
response["WWW-Authenticate"] = 'Basic realm="Protected Area"'
throw(:halt, [401, "Not authorized\n"])
end
end
def authorized?
#auth ||= Rack::Auth::Basic::Request.new(request.env)
if #auth.provided? && #auth.basic? && #auth.credentials
username,password = #auth.credentials
# verify credentials are correct
end
end
end
Call protected! from any action that should be protected (or use a before block to protect everything). I leave the credential verification to you since I don't know how you're storing user account information.
The sinatra-authentication gem looks like an easy and powerful solution for adding users, authentication and permissions to sinatra apps.

Resources