Feature request: Cookie prefixes
Browsers will reject cookies named with special prefixes unless corresponding conditions are met (for reference, https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Cookie_prefixes):
__Host-If a cookie name has this prefix, it is accepted in aSet-Cookieheader only if it is also marked with theSecureattribute, was sent from a secure origin, does not include aDomainattribute, and has thePathattribute set to/. In this way, these cookies can be seen as "domain-locked".__Secure-If a cookie name has this prefix, it is accepted in aSet-Cookieheader only if it is marked with theSecureattribute and was sent from a secure origin. This is weaker than the__Host- prefix.
While it is unlikely for someone to stumble upon this accidentally, this means that an otherwise-valid cookie (like self.set_cookie('__Secure-name', 'value')) will fail to work for no apparent reason. It might be nice to have some out-of-the-box support for these cookie prefixes.
Maybe set_cookie could raise an exception if there is a mismatch between the cookie name and the domain, path, and secure kwargs. Or the appropriate values could be set within set_cookie according to the prefix. Either way, this behavior could be enabled/disabled by a keyword argument to set_cookie.
Oh, those sound useful! This feature is new to me.
When I think about supporting cookie prefixes in Tornado, I think less about alerting users who specify one of these prefixes without the corresponding attributes and more about encouraging their use. If you're setting the secure attribute on a cookie, you almost certainly want the __Secure- prefix (and likewise for __Host), except for the obvious backwards-compatibility problems. I wonder if it would make sense to add an option like enforced=True to add the appropriate prefix for you.
I'm not sure what the best way to make it work with get_[secure_]cookie would be. It seems like it would be easier on that side if the prefix were visible. In other words this would seem funny to me
self.set_cookie("name", "value", secure=True, enforced=True)
self.get_cookie("__Secure-name")
Maybe there's a nicer way to tell get_cookie to add a prefix, though.
Maybe [get|set]_[secure|host]_prefix_cookie?
I've thought about including this in Tornado 6.3, although I've tried a few designs and haven't found anything I'm happy with yet. An enforced boolean on set_cookie that adds the prefix for you has the problem you point out that there is nothing analogous on the get_cookie side. {get,set}_{https,host}_prefix_cookie is a little nicer in some ways (I like the separate methods because they can have different argument lists - no need for the host version to take a domain or path argument at all), but it leads to a lot of different methods (especially when you add in the support for signed cookies).
I think the cleanest design may be to make set_cookie take a Morsel (or equivalent) object and decompose the APIs separately:
self.set_cookie(self.create_host_prefix_morsel('name'), self.create_signed_value(v))
Where
def create_host_prefix_morsel(self, name):
morsel = http.cookies.Morsel()
morsel.key = '__Host-' + name
morsel["secure"] = True
return morsel
Then you could also have clear_cookie accept morsels to address #2911.
However, an API like this would need some more design thinking. I'd prefer not to just expose the stdlib's Morsel type directly - aside from its age and quirks, I think I want to separate the "value" component so you have a "key-only" object that's not bound to a value until later.