To make SameSite=None
cookies work in a rails application (or any rack-based ruby app)
you need to also FILTER OUT any clients that don't work nicely with this new flag,
namely clients who mistreat the None
value or drop the cookie when incompatible values
are seen.
Last active
July 11, 2024 01:37
-
-
Save bnorton/7dee72023787f367c48b3f5c2d71540f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApplicationControler < ActionController::Base | |
... | |
def set_third_party_cookie(name, value, expires_in: 1.year) | |
cookies.encrypted[name] = { :value => value.to_s, :expires_in => expires_in, :domain => :all, :same_site => SameSite.value(request.headers['User-Agent']), :secure => true, :httponly => true }.compact | |
end | |
... | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
## | |
# Until this commit is merged and released by rack | |
# | |
gem 'rack', git: 'https://github.com/rack/rack.git', ref: 'c859bbf7b53cb59df1837612a8c330dfb4147392' | |
... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SameSite | |
def self.value(agent) | |
send_same_site_none?(agent) ? :none : nil | |
end | |
private | |
## | |
# Based on https://www.chromium.org/updates/same-site/incompatible-clients | |
# | |
# Don’t send `SameSite=None` to known incompatible clients. | |
# | |
def self.send_same_site_none?(useragent) | |
return true if useragent.blank? | |
!same_site_none_incompatible?(useragent) | |
end | |
# Classes of browsers known to be incompatible. | |
# | |
def self.same_site_none_incompatible?(useragent) | |
web_kit_same_site_bug?(useragent) || | |
drops_unrecognized_cookies?(useragent) | |
end | |
def self.web_kit_same_site_bug?(useragent) | |
ios_version?(useragent, major: 12) || | |
(macosx_version?(useragent, major: 10, minor: 14) && | |
(safari?(useragent) || mac_embedded_browser?(useragent))) | |
end | |
def self.drops_unrecognized_cookies?(useragent) | |
return !uc_browser_version_at_least?(useragent, major: 12, minor: 13, build: 2) if uc_browser?(useragent) | |
chromium_based?(useragent) && | |
chromium_version_at_least?(useragent, major: 51) && | |
!chromium_version_at_least?(useragent, major: 67) | |
end | |
def self.ios_version?(useragent, major: nil) | |
/\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\// =~ useragent | |
$1 == major.to_s | |
end | |
def self.macosx_version?(useragent, major:nil, minor:nil) | |
/\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\// =~ useragent | |
$1 == major.to_s && $2 == minor.to_s | |
end | |
def self.safari?(useragent) | |
/Version\/.* Safari\// === useragent && !chromium_based?(useragent) | |
end | |
def self.mac_embedded_browser?(useragent) | |
/^(Mozilla\/[.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) )|AppleWebKit\/[.\d]+ \(KHTML, like Gecko\)$/ === useragent | |
end | |
def self.chromium_based?(useragent) | |
/Chrom(e|ium)/ === useragent | |
end | |
def self.chromium_version_at_least?(useragent, major: nil) | |
/Chrom[^ \/]+\/(\d+)[.\d]* / =~ useragent | |
$1.to_i >= major | |
end | |
def self.uc_browser?(useragent) | |
/UCBrowser\// =~ useragent | |
end | |
def self.uc_browser_version_at_least?(useragent, major: nil, minor: nil, build: nil) | |
/UCBrowser\/(\d+)\.(\d+)\.(\d+)[.\d]* / =~ useragent | |
major_version = $1.to_i | |
minor_version = $2.to_i | |
if major_version != major | |
return major_version > major | |
elsif minor_version != minor | |
return minor_version > minor | |
end | |
$3.to_i >= build | |
end | |
end |
This is why you use real user agent parser.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I agree, the pipe should not be there. But there should also only be 1 space between the fist part and the second, so one of the spaces needs to be removed as well. I've tested with the 547 most common browser from this site and tried to manually verify all the incompatible cases.
I use this version (first parenthesis are not required, but makes it clearer)
These should all match, they End with (KHTML, like Gecko)
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1273795-webkit-based-browser-macos-webkit
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1253253-webkit-based-browser-macos-webkit
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1095784-webkit-based-browser-macos-webkit
This should not match, there's additional stuff at the end
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
https://developers.whatismybrowser.com/useragents/parse/1302439-chrome-macos-blink"