How to set SameSite=None for my Devise session cookie? - ruby-on-rails

I'm developing a Chrome Extension that works in conjunction with a rails backend. When users are logged into the backend site, I want to customize the extension UI according to their account. Normal stuff.
Because this is a browser extension, the "document" / URL that requests to the backend will always be changing, so SameSite=None needs to be set.
How can I customize the Devise cookie settings? Everything I found so far online shows me how to set cookie information globally via a rails config:
YourApp::Application.config.session_store :cookie_store, { key: '_xxxx_session', secure: secure_option }
But I think technically I don't need this for every cookie, just the session cookie. I guess I could be wrong though...
Thoughts?

Related

Using cross-site cookies to post to Rails API from Chrome extension

I built a Chrome extension that saves web content to my Rails app. Originally I was able to rely on the existing Rails/Devise user session to ensure content was being saved to the right user, as long as the CORS settings were opened up on my API controller (see code below). As long as the user was logged in, AJAX calls to my site from the Chrome extension were being authenticated correctly, no matter what site the extension was being used on.
However, in early 2020 Chrome introduced changes to how they they handle cross-site requests (see here, here, and here). Specifically, a cookie's SameSite attribute would now default to 'Lax' instead of 'None', and so to use a cross-site cookie, the cookie setting would need to be explicitly set to SameSite=None; Secure.
Rails' own user session cookie does not have the SameSite=None; Secure settings, and so using the Rails session to authenticate my Chrome extension's request was no longer an option.
My fix was to generate my own API authentication cookie whenever the user logged into the app, which did have the necessary SameSite=None; Secure applied. I was able to authenticate API calls from my Chrome extension using this cookie, and all was well.
And then in early September 2020 it suddenly stopped working. Rails no longer reads the cross-site cookie from Chrome extension requests. There's no error or warning, the value is just null.
API Controller:
# This gets called when user logs into app:
def set_cross_site_cookie
# NOTE: Won't work in dev because secure = true
cookies[:foo_cookie] = {
value: 'bar',
expires: 1.year.from_now,
same_site: :none, # Required in order to access from Chrome extension on different site
secure: true # Required in order to access from Chrome extension on different site
}
cookie = cookies[:foo_cookie]
render json: {cookie: cookie}
end
# This SHOULD work when called from our Chrome extension:
def get_cross_site_cookie
# Add headers to allow CORS requests
# SEE: http://stackoverflow.com/questions/298745/how-do-i-send-a-cross-domain-post-request-via-javascript
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Request-Method'] = %w{GET POST OPTIONS}.join(",")
cookie = cookies[:foo_cookie]
render json: {cookie: cookie}
end
Rails 5, Rack 2.1
(NOTE: In order to set Rails cookies with option same_site: none you apparently need need to be on a rack version that's higher than 2.1.0 - SEE: https://github.com/rails/rails/pull/28297#issuecomment-600566751)
Anybody know what happened?
I still don't know why the cross-site cookies suddenly stopped working, but here's how I hacked around it:
The workaround was to use the Chrome extension cookie API to read my Rails API authentication cookies into Chrome's local storage. Since we can enable access to cookies of any specific site in the extension manifest, it actually doesn't matter whether they're cross-site cookies or not.
Once the API cookie would get read into storage, we could then pass it along as an auth token with every request, basically using it as a pseudo-cookie.
So the full flow is that the user clicks the extension button, the extension reads in the API authentication cookies based on the explicit cookie permissions it has for that domain, and if the cookie is missing or outdated, it forces the user to log in. If the cookie is valid, it is passed as an auth token in the params or headers of every API call.
SIDENOTE ON PRE-FLIGHT OPTIONS REQUESTS:
You may also have to deal with the OPTIONS pre-flight requests that will be sent with certain cross-site AJAX (I think it's only an issue with content-type JSON POSTS but don't quote me), as they'll trigger an ActionController::RoutingError (No route matches [OPTIONS]) error in Rails. The recommended answer was to use the rack-cors gem, which indeed solved the issue.
SEE:
Why is an OPTIONS request sent and can I disable it - Stack Overflow ***
How to respond to OPTIONS HTTP Method in rails-api - Stack Overflow
Rails Responds with 404 on CORS Preflight Options Request - Stack Overflow
Perform HTTP OPTIONS request from Rails 5 for CORS pre-flight or otherwise - Stack Overflow

Re-athenticated with the stolen cookies in Laravel Sanctum

I setup a SPA authentication with Laravel Sanctum, it works fine. I login successful with an user. In Chrome Devtools, Application > Storage > Cookies, I copy and save the values of laravel_session and XSRF-TOKEN to a text file, then logout and delete all cookies and refresh browser, here I logged out.
Then I re-open Devtools, restore the values of laravel_session and XSRF-TOKEN manually, refresh browser, now my status is logged in.
Is this normal? Is this the way that cookie based session authentication work?
Thank you.
I was running into the same issue. My problem was that I called Auth::logout() instead of Auth::guard('web')->logout(); inside my AuthController in Laravel.
By using Auth::guard('web')->logout(); the cookies seem to get revoked by the server and can't be used for authentication any more.
By the way, I found the answer here: https://stackoverflow.com/a/63449251/10095327

How to read cookies in rails app set by some different application

I need to read cookies set by some different application into my rails application. both the application is running under common SSO (authentication) and when the user successfully authenticated he first redirected to the first application (Node/Express app) which write some data into browser cookies and then when the user clicks on some button which loads my rails application. it will create a rails session. I need to set some variables reading from the cookie set by node/express application.
Assuming your SSO cookie is named "sso_cookie", to read the cookie: cookies[:sso_cookie]. To update the cookie: cookies[:sso_cookie] = 'new-value'.
See ActionDispatch::Cookies.

Devise session does not persist on different URL

I have a rails application and I am using devise for authentication.
When I get logged in successfully on URL http://localhost:5000/,
I try to access http://127.0.0.1:5000/ in the same browser.
I expect to be logged in as soon as I access it on http://127.0.0.1:5000/ but application remains logged out. Whats going on I really cannot understand as I am trying to access both URLs in the same browser?
UPDATE:
my config/initializers/session_store.rb
Rails.application.config.session_store :cache_store, key: '_app'
The fact that you are logged in is store in the session which is stored in a cookie. For security reasons, the browser sends cookies only to the URLs from which the cookie was set.
From the browser's point of view, localhost and 127.0.0.1 are totally different URLs. Therefore the login information stored in the cookie on localhost is not sent to the server running at 127.0.0.1 and therefore the server running at 127.0.0.1 has no information about an existing session on localhost.
UPDATE:
Using the cache_store to store the session doesn't change anything because the information what session in the cache store belongs to the user is still stored in the cookie.
Imaging that your server needs to store all generated sessions somewhere. And if a user comes backs the server needs to know which session belongs to the user. A simplified solution to this problem might be to assign a random number to each session and give the user this number (stored in the cookie). When the user returns the cookie is returned too and that allows the server to load the session by that number.
And a cookie is bound to a domain. This is a security feature of the browser. If it didn't work that way all sessions would be sent to all domains: Google would know if you were logged in to Facebook, every website would know that you have a cookie from your bank...

Rails and Devise: set Secure flag when requested via HTTPS, don't set Secure flag when not requested via HTTPS

I manage a Rails 4.2 application which runs dual stack: SSL and Non-SSL. I'd like to set the Secure flag for cookies when the resource is requested via HTTPS and I want to leave out the flag when the resource is requested via plain HTTP.
Is there a way to achieve this in Rails (session cookie, cookies sent manually in the Code)? And especially when using Devise with rememberable enabled.
I know this is a late response, but I'm currently looking into the same thing and it seems https://github.com/mobalean/devise_ssl_session_verifiable should automate this for you, although it uses a different approach (regular session cookie over http + https, but an additional secure cookie in https, so that someone hijacking your session cannot access your https-only resources.

Resources