I have RESTful API written on RoR 3.
I have to make my application not to send "Set-Cookie header" (clients are authorizing using auth_token parameter).
I have tried to use session :off and reset_session but it does not make any sense.
I am using devise as authentication framework.
Here is my ApplicationController
class ApplicationController < ActionController::Base
before_filter :reset_session #, :unless => :session_required?
session :off #, :unless => :session_required?
skip_before_filter :verify_authenticity_token
before_filter :access_control_headers!
def options
render :text => ""
end
private
def access_control_headers!
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Headers"] = "Content-type"
end
def session_required?
!(params[:format] == 'xml' or params[:format] == 'json')
end
end
Use the built in option.
env['rack.session.options'][:skip] = true
or the equivalent
request.session_options[:skip] = true
You can find the documentation for it here https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L213
As is mentioned in a comment on John's answer, clearing the session will not prevent the session cookie from being sent. If you wish to totally remove the cookie from being sent, you have to use Rack middleware.
class CookieFilter
def initialize(app)
#app = app
end
def call(env)
status, headers, body = #app.call(env)
# use only one of the next two lines
# this will remove ALL cookies from the response
headers.delete 'Set-Cookie'
# this will remove just your session cookie
Rack::Utils.delete_cookie_header!(headers, '_app-name_session')
[status, headers, body]
end
end
Use it by creating an initializer with the following body:
Rails.application.config.middleware.insert_before ::ActionDispatch::Cookies, ::CookieFilter
To prevent the cookie filter to end up in application stack traces, which can be utterly confusing at times, you may want to silence it in the backtrace (Assuming you put it in lib/cookie_filter.rb):
Rails.backtrace_cleaner.add_silencer { |line| line.start_with? "lib/cookie_filter.rb" }
I'm not sure when they added it to Devise, but there appears to be a configuration that will let you disable the sending of the session cookie when using a auth_token:
# By default Devise will store the user in session. You can skip storage for
# :http_auth and :token_auth by adding those symbols to the array below.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing :skip => :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth, :token_auth]
It does work well. The only issue I had was that I still needed to be able to make an initial request to my token_controller in order to generate/retrieve the token. I.e. POST /api/v1/tokens.json, which unfortunately would cause a session cookie to be returned for that request.
So I ended up implementing the CookieFilter intializer that Ryan Ahearn wrote above anyway.
Also, since my app has both a web front-end as well as a JSON api, I only wanted to filter the cookies for the JSON api. So I modified the CookieFilter class to first check the requests belonged to the api:
if env['PATH_INFO'].match(/^\/api/)
Rack::Utils.delete_cookie_header!(headers, '_myapp_session')
end
Not sure if there's a better way of doing that...
Another solution:
In the controller you want to avoid cookies, add this:
after_filter :skip_set_cookies_header
def skip_set_cookies_header
request.session_options = {}
end
If you have a set of api controllers, set this in an api_controller class and let your other controllers inherit the api_controller.
This skips setting Set-Cookie header since the session opts is empty.
The default CookieSessionStore doesn't send a "Set-Cookie" header unless something is added to the session. Is something in your stack writing to the session? (it's probably Devise)
session :off has been deprecated:
def session(*args)
ActiveSupport::Deprecation.warn(
"Disabling sessions for a single controller has been deprecated. " +
"Sessions are now lazy loaded. So if you don't access them, " +
"consider them off. You can still modify the session cookie " +
"options with request.session_options.", caller)
end
If something in your stack is setting session info, you can clear it using session.clear like so:
after_filter :clear_session
def clear_session
session.clear
end
Which will prevent the Set-Cookie header from being sent
Further to John's answer, if you are using CSRF protection you would need to turn that off for web service requests. You can add the following as a protected method in your application controller:
def protect_against_forgery?
unless request.format.xml? or request.format.json?
super
end
end
This way HTML requests still use CSRF (or not - depends on config.action_controller.allow_forgery_protection = true/false in the environment).
I myself truly missed being able to declaratively turn off sessions (using session :off)
... thus I brought it "back" - use it just like in plain-old-rails (<= 2.2) :
than of course this might require some additional Devise specific hacking of your own, since session_off might cause session == nil in a controller, and most rails extensions since 2.3 simply assume a lazy session that shall not be nil ever.
https://github.com/kares/session_off
Imo the best approach is to simply remove the cookie session store middleware.
To do so, add this to your application.rb (or to a specific environment if needed):
# No session store
config.middleware.delete ActionDispatch::Session::CookieStore
Try this instead
after_filter :skip_set_cookies_header
def skip_set_cookies_header
session.instance_variable_set('#loaded', false)
end
Or even better, always remove Set-Cookie header when session data did not change
before_filter :session_as_comparable_array # first before_filter
after_filter :skip_set_cookies_header # last after_filter
def session_as_comparable_array(obj = session)
#session_as_comparable_array = case obj
when Hash
obj.keys.sort_by(&:to_s).collect{ |k| [k, session_as_comparable_array(obj[k])] }
when Array
obj.sort_by(&:to_s).collect{ |k| session_as_comparable_array(k) }
else
obj
end
end
def skip_set_cookies_header
session.instance_variable_set('#loaded', false) if (#session_as_comparable_array == session_as_comparable_array)
end
# frozen_string_literal: true
module Api
module Web
module Base
class WebApiApplicationController < ApplicationController
include DeviseTokenAuth::Concerns::SetUserByToken
include Api::Concerns::ErrorsConcern
devise_token_auth_group :user, contains: %i[api_web_v1_user]
respond_to :json
serialization_scope :current_user
before_action :METHOD_NAME
private
def METHOD_NAME
request.session_options[:skip] = true
end
end
end
end
end
It's working for me.
Related
I am new to Rails and experience a strange issue I don't understand.
I use ActiveRecord as a session store and need to add session id as a property of JSON responses for all the requests. I use Devise as well if it have some impact on the situation. The problem is that if a request is made by a user without cookies (or at least without session id in the cookie) the session.id is empty or - attention, please - not the same value that is set in the response cookie.
For debugging, I add this code as an after_filter to ApplicationController:
puts session.id
puts request.session_options[:id]
Both values are the same. They match the value in the cookie if it is present. Otherwise, if session id is not present in the cookie, the cookie set after that request has different value.
My opinion is that session_id gets new value after it is actually saved to the database, where it have to be unique. DB migration:
def change
create_table :sessions do |t|
t.string :session_id, :null => false
t.text :data
t.timestamps
end
add_index :sessions, :session_id, :unique => true
add_index :sessions, :updated_at
end
My question: How can I get the actual session.id value of a new session before the first response is rendered?
UPD:
I just created a new Rails app that uses ActiveRecord session store without Devise, and I can get session.id that is going to be set in cookie just before response with this code id application controller:
class ApplicationController < ActionController::Base
after_filter :show_session
def show_session
puts session.id
end
end
But in my existing app with Devise I get a value that really looks like a session id, but that doesn't match the value set in the cookie via Set-Cookie response header and the value actually saved to sessions table in database. Looks like Devise have a conflict with ActiveRecord session store in some way. Need to go deeper to figure it out.
UPD 2
Looks like I found the problem roots. As I said, I use Devise for authorization with Omniauth. According to the documentation, sign_in method resets session id for security reasons. But after that reset session.id returns the old value, that had been automatically set. I use this code as an Omniauth callback:
def facebook_access_token
sign_in #user
puts session.id
end
And in console I get session id different from the one set in the Set-Cookie response header. If I comment "sign_in" line, these values match. New question: how can I get the new session id value after it is been reset inside of sign_in method? Is it an internal Warden/Devise implementation or something?
Renewing is still important and you should not disable it
Also the new session id is generated after the execution of the controller, therefore after you have a chance to set the response to be sent to the client.
The solution is to manually trigger the renewing of the session id
In your ApplicationController add the method:
protected
def commit_session_now!
return unless session.options[:renew]
object = session.options.instance_variable_get('#by')
env = session.options.instance_variable_get('#env')
session_id = object.send(:destroy_session, env, session.id || object.generate_sid, session.options)
session_data = session.to_hash.delete_if { |k,v| v.nil? }
object.send(:set_session, env, session_id, session_data, session.options)
session.options[:renew] = false
session.options[:id] = session_id
end
Then in your controller you just call this method before getting the session id for your response
def my_action
...
commit_session_now!
render json: {session_id: session.id}, status: :ok
end
The code in commit_session_now! comes from Rack::Session::Abstract::ID#commit_session https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L327
The problem I experienced was caused by default Warden configuration. It renewed session id, but somehow the new id was not accessible via session.id.
The only way I found to stop this behavior was putting this code into config/initializers/devise.rb:
Warden::Manager.after_set_user do |user,auth,opts|
auth.env["rack.session.options"][:renew] = false
end
Probably this method is not really good for security reasons, but I have no other ideas in a week of searching and reading sources.
Without knowing the details of your application, my suggestion would be to use a before_filter in your ApplicationController:
class ApplicationController < ActionController::Base
before_filter :use_session_id
protected
def use_session_id
# Do something with session.id
# This will get called before any rendering happens
end
end
If the protect_from_forgery option is mentioned in application_controller, then I can log in and perform any GET requests, but on very first POST request Rails resets the session, which logs me out.
I turned the protect_from_forgery option off temporarily, but would like to use it with Angular.js. Is there some way to do that?
I think reading CSRF-value from DOM is not a good solution, it's just a workaround.
Here is a document form angularJS official website http://docs.angularjs.org/api/ng.$http :
Since only JavaScript that runs on your domain could read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.
To take advantage of this (CSRF Protection), your server needs to set a token in a JavaScript readable session
cookie called XSRF-TOKEN on first HTTP GET request. On subsequent
non-GET requests the server can verify that the cookie matches
X-XSRF-TOKEN HTTP header
Here is my solution based on those instructions:
First, set the cookie:
# app/controllers/application_controller.rb
# Turn on request forgery protection
protect_from_forgery
after_action :set_csrf_cookie
def set_csrf_cookie
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
Then, we should verify the token on every non-GET request.
Since Rails has already built with the similar method, we can just simply override it to append our logic:
# app/controllers/application_controller.rb
protected
# In Rails 4.2 and above
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
# In Rails 4.1 and below
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
If you're using the default Rails CSRF protection (<%= csrf_meta_tags %>), you can configure your Angular module like this:
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
Or, if you're not using CoffeeScript (what!?):
myAngularApp.config([
"$httpProvider", function($httpProvider) {
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
If you prefer, you can send the header only on non-GET requests with something like the following:
myAngularApp.config ["$httpProvider", ($httpProvider) ->
csrfToken = $('meta[name=csrf-token]').attr('content')
$httpProvider.defaults.headers.post['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.put['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.patch['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.delete['X-CSRF-Token'] = csrfToken
]
Also, be sure to check out HungYuHei's answer, which covers all the bases on the server rather than the client.
The angular_rails_csrf gem automatically adds support for the pattern described in HungYuHei's answer to all your controllers:
# Gemfile
gem 'angular_rails_csrf'
The answer that merges all previous answers and it relies that you are using Devise authentication gem.
First of all, add the gem:
gem 'angular_rails_csrf'
Next, add rescue_from block into application_controller.rb:
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken do |exception|
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
render text: 'Invalid authenticity token', status: :unprocessable_entity
end
And the finally, add the interceptor module to you angular app.
# coffee script
app.factory 'csrfInterceptor', ['$q', '$injector', ($q, $injector) ->
responseError: (rejection) ->
if rejection.status == 422 && rejection.data == 'Invalid authenticity token'
deferred = $q.defer()
successCallback = (resp) ->
deferred.resolve(resp)
errorCallback = (resp) ->
deferred.reject(resp)
$http = $http || $injector.get('$http')
$http(rejection.config).then(successCallback, errorCallback)
return deferred.promise
$q.reject(rejection)
]
app.config ($httpProvider) ->
$httpProvider.interceptors.unshift('csrfInterceptor')
I saw the other answers and thought they were great and well thought out. I got my rails app working though with what I thought was a simpler solution so I thought I'd share. My rails app came with this defaulted in it,
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
I read the comments and it seemed like that is what I want to use angular and avoid the csrf error. I changed it to this,
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session
end
And now it works! I don't see any reason why this shouldn't work, but I'd love to hear some insight from other posters.
I've used the content from HungYuHei's answer in my application. I found that I was dealing with a few additional issues however, some because of my use of Devise for authentication, and some because of the default that I got with my application:
protect_from_forgery with: :exception
I note the related stack overflow question and the answers there, and I wrote a much more verbose blog post that summarises the various considerations. The portions of that solution that are relevant here are, in the application controller:
protect_from_forgery with: :exception
after_filter :set_csrf_cookie_for_ng
def set_csrf_cookie_for_ng
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
rescue_from ActionController::InvalidAuthenticityToken do |exception|
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
render :error => 'Invalid authenticity token', {:status => :unprocessable_entity}
end
protected
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
I found a very quick hack to this. All I had to do is the following:
a. In my view, I initialize a $scope variable which contains the token, let's say before the form, or even better at controller initialization:
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. In my AngularJS controller, before saving my new entry, I add the token to the hash:
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.push(entry)
$scope.newEntry = {}
Nothing more needs to be done.
angular
.module('corsInterceptor', ['ngCookies'])
.factory(
'corsInterceptor',
function ($cookies) {
return {
request: function(config) {
config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN');
return config;
}
};
}
);
It's working on angularjs side!
Active Resource can make use of HTTP authentication set at the class level. For instance:
class Resource < ActiveResource::Base
end
Resource.user = 'user'
Resource.password = 'password'
or
Resource.site = "http://user:password#site.com/"
But what if I use different HTTP authentication based on which user is logged in? If I change Resource.user and Resource.password, is that going to cause a race condition where requests from one thread suddenly start using the authentication of a user whose requests are running simultaneously in a different thread? Or is this a non-issue (as long as I reset the authentication between requests) because rails servers are not multithreaded?
Even if there's no thread safety problem, it still seems risky that if I fail to reset them, the previous user's credentials will be used automatically by future requests.
Update: After being frustrated with ActiveResource, I wrote my own REST library:
https://github.com/DeepWebTechnologies/well_rested
Monkey patch the host, user and password methods of ActiveResource::Base class:
class ActiveResource::Base
# store the attribute value in a thread local variable
class << self
%w(host user password).each do |attr|
define_method(attr) do
Thread.current["active_resource.#{attr}"]
end
define_method("#{attr}=") do |val|
Thread.current["active_resource.#{attr}"] = val
end
end
end
end
Now set the credentials in every request
class ApplicationController < ActionController::Base
around_filter :set_api_credentials
private
# set the credentials in every request
def set_api_credentials
ActiveResource::Base.host,
ActiveResource::Base.user,
ActiveResource::Base.password = current_user_credentials
yield
ensure
ActiveResource::Base.host =
ActiveResource::Base.user =
ActiveResource::Base.password = nil
end
DEFAULT_HOST, DEFAULT_USER, DEFAULT_PASSWORD= [
"http://www.foo.com", "user1", "user78102" ]
def current_user_credentials
current_user.present? ?
[ current_user.host, current_user.login, current_user.password] :
[ DEFAULT_HOST, DEFAULT_USER, DEFAULT_PASSWORD]
end
end
As of Active Resource 4.1.0, those settings are thread local, so this example would not cause a race condition anymore.
This is the relevant commit: https://github.com/rails/activeresource/commit/538588ddba9ffc9bf356790e9186dc7e6adad12f
I am trying to create a session explicitly like this UserSession.create(#user, true) but the session is not getting created, current_user is nil.
But when I do this, I get < #UserSession: {:unauthorized_record=>""}>us = UserSession.create(#user, true)
RAILS_DEFAULT_LOGGER.info(us.inspect) #=> UserSession: {:unauthorized_record=>""}
I had a look at Authlogic::Session::UnauthorizedRecord here it says
Be careful with this, because Authlogic is assuming that you have already confirmed that the user is who he says he is. For example, this is the method used to persist the session internally. Authlogic finds the user with the persistence token. At this point we know the user is who he says he is, so Authlogic just creates a session with the record. This is particularly useful for 3rd party authentication methods, such as OpenID. Let that method verify the identity, once it’s verified, pass the object and create a session.
which is exactly what I am trying to do (i am authenticating using omniauth and creating session using authlogic).
How do I fix this, so that I can get a valid session in current_user ?
I had a similar issue caused by the persistence_token being nil on the user. Reset it before creating the UserSession. So...
#user.reset_persistence_token!
UserSession.create(#user, true)
I'm not sure about the .create(object, bool) method signature, but the following works using authlogic.
class Api::ApiBaseController < ApplicationController
protected
def verify_token
return false if params[:token].blank?
#session = UserSession.new(User.find_by_single_access_token(params[:token]))
#session.save
end
end
If that doesn't work for you -- I think the #user isn't being set correctly.
If you map the active_record_store to the authlogic user_sessions table your session information will be stored in the database, and you will be able to store larger sets of data.
Inside your config folder:
config/initializers/session_store.rb
Comment out App::Application.config.session_store :cookie_store, :key => '_App_session'
Add or uncomment App::Application.config.session_store :active_record_store
Inside of config/application.rb
At the end of the class for you application add:
ActiveRecord::SessionStore::Session.table_name = 'user_sessions'
Restart your app, and any information stored in the user session will be saved in the authlogic user_sessions table.
Goto: http://apidock.com/rails/ActiveRecord/SessionStore
For more information
For now you can replace
UserSession.create #user
to
UserSession.create :email => #user.email, :password => #user.password
not a big deal.
But that caught me other way. I forgot that my user got active? == false when created. I've set it to true and session is created.
I ran into this problem today. In my case it ended up being related to CSRF tokens.
We are creating a user and session in our app in response to an OAuth callback. It appears that if the CSRF token is invalid, which would be the case when coming from a third party, authlogic won't create the user session.
Can't verify CSRF token authenticity
The fix was simple:
class Oauth::UserSessionsController < ApplicationController
skip_before_action :verify_authenticity_token, only: :callback
def new
# code removed...
end
def callback
# code removed...
UserSession.create(#user)
redirect_to root_path
end
end
I'm thinking about writing an automatic spam protection system (maybe I will write a public gem) for rails.
My concept is to include a helper method in application_controller f.e.:
class ApplicationController < ActionController::Base
automatic_captcha_redirect(:min_time => 30.seconds :limit => 50)
...
end
Then I want to include automatical a before_filter in every controller, which checks, if the current request is via post, put or delete-method.
If the user's last post-request is smaller than :min_time, then the request should be redirected to an captcha-input-page (the posted user-data resides in hidden html fields).
# before_filter :check_spam
def check_spam
if !request.get? && session[:last_manipulation_at]
&& session[:last_manipulation_at] >= DateTime.now - 30.seconds
redirect_to captcha_path
# (doesn't know yet how to handle the post data to
# display in hidden fields in the spam-captcha-form)
end
end
And in captcha.haml
=form_tag
-request.params.each do |key, value|
=hidden_field_tag key, value
=captcha_image
=submit_button_tag
If the user submits the right captcha-word, his data will be posted to the right action.
Do you think thats realizable?
Any critics or suggestions? Or an idea how to realize this behaviour?
EDIT:
this should not pass through all the ActiveRecord stack; can't it be implemented as a middleware hook (Rails Rack)?
Yes, would be a good idea - but I'm not very familiar with rails rack :/
what about file uploads? (you can not store it in a hidden file)
Hm... maybe a check if there is a file in the post? (How could that be realized?)
what about Ajax posting?
Maybe sending back http-status codes (f.e. 503 Service temporary unavailable)
why only POST and not also PUT and DELETE?
corrected this in my question
EDIT:
First structure of processing (as non rack-app - I dont know how to write rack apps):
0) Settings in environment.rb
auto_recaptcha[:limit] = 10
auto_recaptcha[:min_time] = 1.minute
1) User posts data
Check last_manipulation and max. amount of allowed manipultations in application_controller.rb
class ApplicationController < ActionController::Base
before_filter :automatic_captcha_redirect
def automatic_captcha_redirect
session[:last_manipulation_at][:manipultation] = [] unless session[:last_manipulation_at][:manipultation]
# Checks if requests are falling under the specifications for showing captcha
if !request.get?
&& session[:last_manipulation_at][:date] > DateTime.now - auto_recaptcha[:min_time]
&& session[:last_manipulation_at][:manipultation].count < auto_recaptcha[:limit]
# If user answered captcha, verify it
if !verify_captcha(params)
#url = request.url
#params = request.params
render "layouts/captcha.haml"
else
# Add successfull manipulation to counter
session[:last_manipulation_at][:manipultation] << DateTime.now
session[:last_manipulation_at][:date] = DateTime.now
end
end
end
end
captcha.haml
-form_tag #url do
-request.params.each do |key, value|
=hidden_field_tag key, value
=captcha_image
=submit_button_tag
2)
...
...
...
last) Post userdata to the right location
post(params) => users_path # path "/users" with method: post
First, i would like to say that this is a very good ideea of a feature.
My qs/remarks:
this should not pass through all the ActiveRecord stack; can't it be implemented as a middleware hook (Rails Rack)?
what about file uploads? (you can not store it in a hidden file)
what about Ajax posting?
why only POST and not also PUT and DELETE?
Anyway, i would be more interested to see the number of posts in last 5 mins, for example, that the date of the last request. I believe it is more relevant.
One way this could be put together:
Middleware/rails metal component that
monitors the requests and adds the
information to the rack session.
Controller helpers for before_filters
on things that might need captchas
View helpers for displaying the
captchas
You could make the captcha rate adjustable through the args passing mechanism of use
#config/environment.rb
config.middleware.use 'CaptchaMiddleware',:period=>5.minutes,:limit=>50,:captcha_url=>'/captcha'
Also, this should not rely on hidden form fields because a determined bot writer could just change the value they are posting to your server code.
Simple middleware example code(slightly better than a stab in the dark, but still)
class CaptchaMiddleware
def initialize app,options
#app = app
#options=options
end
def update_stats!
#session based,on account of laziness
session[:reqs] ||= []
session[:reqs].reject!{ |request| request < Time.now - #options[:period]}
session[:reqs] << Time.now
end
def over_limit?
session[:reqs].length > #options[:limit]
end
def call env
#env = env
if #env["REQUEST_METHOD"]!='GET'
update_stats!
if over_limit?
return [302,{"Location: #{options[:captcha_url]}"},'']
end
end
#app.call env
end
def session
#env["rack.session"]
end
end