44

I have a Rails-API app. More or less "out of the box" but I want to add back cookie-based session store. Here is what I've done:

app/controllers/application_controller.rb

+ include ::ActionController::Cookies

config/application.rb

+ config.middleware.insert_after ActiveRecord::QueryCache, ActionDispatch::Cookies
+ config.middleware.insert_after ActionDispatch::Cookies, ActionDispatch::Session::CookieStore

created config/initializers/secret_token.rb

+ Namespace::Application.config.secret_token = 'token'

created config/initializers/session_store.rb

+ Namespace::Application.config.session_store :cookie_store, :key => '_namespace_key'

When I inspect the session in a controller it results:

<Rack::Session::Abstract::SessionHash:0x3fdadc5daa24 not yet loaded>

However it does appear that data is being written to and used.

But, in my browser the cookie itself is being named as '_session_id' instead of '_namespace_key'

I thought I added back every piece required for cookie based session storage, but I appear to be missing something else. Any ideas?

seanhussey
  • 379
  • 3
  • 10
bcardarella
  • 4,667
  • 4
  • 29
  • 45

5 Answers5

55

If you're on Rails 5, and want to preserve config.api_only = true you could extend the middleware to add the sessions layer, adding this code after class Application < Rails::Application in config/application.rb

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_namespace_key'

This could come in handy when you want to have a rails api-only enabled app but have to manage user sessions with an administration panel like ActiveAdmin or Rails_Admin.

jstnno
  • 1,035
  • 1
  • 11
  • 13
  • 1
    To enable ActiveAdmin on a Rails 5 API app, you have to perform a few other steps in addition to the ones above. Specifically, you have to create 2 base controllers, one for ActiveAdmin which inherits from `ActionController::Base` and another for your API controllers which inherits from `ActionController::API`. You also need a few more pieces of middleware to get the dashboard views rendering. I wrote [a guide](http://www.carlosramireziii.com/how-to-add-active-admin-to-a-rails-5-api-application.html?utm_source=stackoverflow) with all the steps. Hope that helps! – Carlos Ramirez III Jan 29 '17 at 16:26
  • 2
    It may be obvious, but the `config.session_store` is no longer useful in this case; only this `config.middleware.use` stuff is required now. In addition, the options like `key: '_namespace_key'` should also be moved to the `config.middleware.use` statements. – Franklin Yu Jun 25 '17 at 20:58
  • 2
    According to this [Rails issue](https://github.com/rails/rails/issues/24239#issuecomment-199409912), you may also need to `include AbstractController::Helpers` and `include ActionController::Cookies` in the controller besides enabling the middleware. – Epigene Jan 25 '18 at 09:16
13

You need to remove these middleware declarations from your application.rb file and add this:

config.api_only = false

This will enable session management the way you want if there is a configured session_store somewhere in your initialisers (which you have). This isn't clearly documented, but that's what you're supposed to do.

Example here.

Maurício Linhares
  • 39,901
  • 14
  • 121
  • 158
  • 29
    While this may fix the problem, it doesn't really make sense to do. I use rails-api to keep my stack slim and trim. Setting `config.api_only = false` essentially says to just use the regular, full Rails stack. ...in which case, there's no point in using rails-api. – turboladen Oct 02 '14 at 18:54
  • 3
    @turboladen is right--this defeats the purpose of using rails-api. If you only want to include specific middleware, with specific options, there's a very clean way to do that--the same way rails-api itself does it [here](https://github.com/rails-api/rails-api/blob/v0.4.0/lib/rails-api/application/default_rails_four_middleware_stack.rb#L13) – Isaac Betesh Apr 13 '15 at 17:01
  • 1
    Holy crap! Praise the lord for this question and answer! After hours of tracing code, I was about ready to strangle myself, trying to figure out why the #$%^! it wasn't using my session_options! ;) I'm going to add an alternate option to this answer in case people don't want to disable `api_only`. – mltsy Feb 14 '17 at 22:19
  • 1
    Okay, my edit was rejected, I guess (?) but see @IsaacBetesh's answer below to get the idea. I also added a PR for Rails Guides to explain the special case of trying to add session middleware back in to a Rails api_only app: https://github.com/rails/rails/pull/28009 – mltsy Feb 16 '17 at 18:23
10

This worked for me in Rails 6.0, application.rb:

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore
config.middleware.insert_after(ActionDispatch::Cookies, ActionDispatch::Session::CookieStore)

If you want to set custom key (yes, it has to be set twice):

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_your_app'
config.middleware.insert_after(ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, key: '_your_app')

And lastly, if you want to add expiration date - it's done here:

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_your_app', expire_after: 20.years 
config.middleware.insert_after(ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, key: '_your_app')

Would have loved linking to documentation, but there's none.

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
  • Plus, it's important to declare this separately on each environment file. Especially if you're using omniauth (depends on the domain which is set on `session_store`). – brcebn Jul 21 '20 at 08:15
9

This line is ignored because you are not using the full Rails stack:

::Rails.application.config.session_store :cookie_store,
  :key => '_namespace_key'

So instead, your session is using the default session key set here. However, you can pass these arguments directly by replacing:

config.middleware.insert_after 
  ActionDispatch::Cookies, ActionDispatch::Session::CookieStore

with:

config.middleware.insert_after
  ActionDispatch::Cookies, ActionDispatch::Session::CookieStore,
  :key => '_namespace_key'

Here's a full list of options you can pass (with a rough idea of their defaults, since some may be overridden by modules in Rails).

Isaac Betesh
  • 2,935
  • 28
  • 37
1

This thread worked the magic for me. I was on Rails "7.0.4" and Ruby "3.1.2"

config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options

Below is the User.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :jwt_authenticatable, :registerable,
  :recoverable, :rememberable, :validatable, :confirmable, :lockable, 
  :timeoutable, :trackable, jwt_revocation_strategy: JwtDenylist
end
brytebee
  • 41
  • 1
  • 7