Rails finds an encrypted cookie that doesn't exist

This is kinda bizarre. Anybody seen this?

So here I am, working on my localhost:3000 project, running rails server as normal. (Rails 7.1.1, Ruby 3.2.2)

I have code in my controller’s before_action that gets a token like this:

token = cookies.encrypted[:device_token]

I see in my safari’s “storage” dev panel that there is no device_token cookie there. However, when I submit the form that’s supposed to create that cookie, Rails finds this token somehow in that submit’s before_action. That’s before the cookie was ever created by the action. Then, because I have a check that won’t allow you to create this cookie under certain conditions, Rails correctly redirects back to previous page, but the action where the redirect went finds this non-existent cookie again!

If at this moment I refresh the page manually, Rails will not find the cookie anymore. I can repeat this loop and Rails will keep finding it on that form submission, and not finding it after manual refresh. (I am using turbo for page transitions).

Where is this cookie coming from? I swear the browser is not showing it, and I’m never creating it. However, I don’t remember specifically getting rid of that cookie before. Since last time it was there, I closed Safari and updated Sonoma, restarted the Mac. But it was only a couple of days. So it should’ve been there still? :thinking: Bug in Safari that it’s kinda half-there?

I tried in Firefox, and cannot reproduce this problem. Definitely not my code. Any ideas, perhaps a known Safari issue?

Update: I set prepend_before_action { binding.irb }, and inspecting cookies there, Safari is definitely sending a cookie that it claims not to have.

Another update: this stopped happening in Safari when I cleared all cookies. So apparently Safari holds onto some kind of phantom cookies and sends them over in some situations, even if the developer panel is not showing them. :grimacing:

1 Like

I think it’s unlikely anything other than your Rails application would be able to create an encrypted cookie that your application can successfully decrypt.

That typed, I searched on GitHub Code Search and wondered: are you using the Shimmer gem? https://github.com/nerdgeschoss/shimmer/blob/edc00f642de32221eaa20ed2765f3427a878893c/lib/shimmer/auth/authenticating.rb#L22

And if that’s not the culprit, I’ll offer that I think this is difficult to debug without examples of your code, because I imagine then that things in your application are running in a different order than you expect.

Thanks for engaging on such a weird issue. Brave! :smiley:

You’re certainly right that my application originally created this cookie. The bug seems to be on Safari side: it claims not to have the cookie, but actually still retains it.

That’s a good hunch, and I had the same one, but I debugged through every single before filter in all the superclasses and modules, and even at the top of the stack, before anything else kicks in, the cookie is already provided in the request itself.

Agreed. But I can pretty confidently say at this point that this is a Safari (dev-tools?) issue. Evidenced also by it not happening in other browsers, and by the issue getting fixed once I cleared Safari cookies, despite dev tools claiming this cookie wasn’t there to begin with. I was hoping that somebody may be familiar with this Safari bug, and confirm it, or link to a bug report.

P.S. The device_token is our own cookie name, so the shimmer gem is just a coincidence. We don’t use it.

Happ to try to help :blush:

Yep, it’s strange. I only have experience with Safari dropping cookies, never secretly retaining them.

After a while, it may be fixed (cannot confirm 100%), and if it was, then it was probably broken for 2 reasons:

  1. I’m on localhost:3000.
  2. I made the cookie same_site: :strict.

I imagine the bug may be stemming from a possible discrepancy in Safari between how it displays vs. actually stores + sends strict cookies in the context of localhost:3000.

My working theory for now: just don’t use same_site: :strict on your cookies. Rails uses :lax by default, and I should’ve just stayed with that.