Let's talk about cookie storage…
- dakur
- Member | 493
I'm experimenting with cookie user storage in our Nette app and it looks really promising (solving some issues with serialization and invalidating), but I'm a bit confused/dissatisfied about how it works.
Life-cycle problem
When I call $storage->saveAuthentication($identity)
and then
$storage->getState()[1]
, I would expect to obtain previously
stored identity. However, as cookie storage works on “read from request, write
to response” basis, it doesn't return the identity as there is still nothing
in the request. On next request, it works well. For this, I made a PR which caches the
identity in memory, but as I was testing it, another problem occurred..
Hard-coded SimpleIdentity
The aforementioned two main methods have following signature:
public function saveAuthentication(IIdentity $identity): void;
public function getState(): array{bool, ?IIdentity, ?int};
That works well unless you start using something else than
SimpleIdentity
. With the caching mechanism I've introduced above,
it became even more visible. I've stored my own StorageIdentity
(which implements IIdentity
and even extends
SimpleIdentity
). On getState()
in the same request, it
correctly obtained StorageIdentity
(from memory cache), but on next
request, it obtained SimpleIdentity
as that's how
CookieStorage#getState()
is implemented – hard-coded
instantiating of SimpleIdentity
.
I think I understand the objective why it was made this way –
getState()
doesn't know what identity object user means to use. But
this behavior is just confusing. CookieStorage
pretends to support
IIdentity
when in fact it requires SimpleIdentity
.
What can I do?
- avoid passing anything else than
SimpleIdentity
tosaveAuthentication()
method – input/output is symmetric, thus more predictable - hard-code
new SimpleIdentity()
in caching mechanism the same way as it is in the current non-caching behavior and count with the implicit asymmetry – in:IIdentity
, out:SimpleIdentity
Both options seem to be work-arounds to a problem that
CookieStorage
lies about its input/output. 🙂
Thrown away roles/data
This is related issue to see the full picture. When identity is stored with
CookieStorage
, roles and data are thrown away. When
getState()
is called then, there's only ID from the originally
stored identity. With SessionStorage
, what you store is what
you get.
Sum up
SessionStorage
and CookieStorage
have technically
common interface, but they seem to be two different solutions of preserving
authentication data across requests with different required inputs and different
produced outputs.
Question
Should they really have common interface? I'd like to hear some opinions on this.
To be honest, I don't know what would be the impact of un-doing the
interface on Nette ecosystem, we use UserStorage
standalone, pass
its instances manually and we deregistered User
class from our app.
But as Nette claims itself as framework of standalone packages, I believe that
such low-level/standalone usages should be supported.
Last edited by dakur (2022-10-06 08:07)
- David Grudl
- Nette Core | 8218
SessionStorage and CookieStorage are just parts of a larger system where IdentityHandler also plays an important role.
Yes, CookieStorage can only store a special type of identity. This is a feature of it. You could create some EncryptedCookieStorage that would store more information in the cookie in encrypted form. To be able to use CookieStorage with any custom identity class, there is just IdentityHandler. And it can also preserve roles and data, for example.
(This brings me to the idea of creating a CookieIdentity class that would be the only one supported in CookieStorage, to make it clear that nothing else can be used.)
The fact that CookieStorage::getState() does not take into account the previous saveAuthentication() can of course be changed.
- Marek Bartoš
- Nette Blogger | 1261
You could create some EncryptedCookieStorage that would store more information in the cookie in encrypted form
Cookies have very limited storage space. Some applications dump all user data into identity and would reach limit for all cokies quite quickly.
Also it would need a checksum because it is possible to change data without decrypting them
Imho it is okay to require IdentityHandler to be used with CookieStorage. But it should be probably noted here https://blog.nette.org/…security-3-1 and here https://doc.nette.org/…thentication#… as the behavior is not that obvious for newcomers
- David Grudl
- Nette Core | 8218
@MarekBartoš If you have an idea what to improve in the documentation, feel free to do so.
- dakur
- Member | 493
Thanks for the response!
Yes, CookieStorage can only store a special type of identity. This is a feature of it. You could create some EncryptedCookieStorage that would store more information in the cookie in encrypted form.
It still seems odd to me that we have an interface to require from DI, but then we have to be aware of implementation specifics to use it. Isn't point of interface to be independent of implementation specifics?
To be able to use CookieStorage with any custom identity class, there is just IdentityHandler. And it can also preserve roles and data, for example.
With IdentityHandler
, there's another problem I wrote about here. In short, you can't
use it when you use User
but don't use
Authenticator
.
(This brings me to the idea of creating a CookieIdentity class that would be the only one supported in CookieStorage, to make it clear that nothing else can be used.)
Yes, that would improve the experience. 👍
- David Grudl
- Nette Core | 8218
In short, you can't use it when you use User but don't use Authenticator.
Sure, go ahead and send a PR.
- dakur
- Member | 493
Ok, I found out that it just wasn't adapted/widened to what
Response
supports after cookie storage has been introduced as an
alternative for session storage. I've made PRs:
- https://github.com/…rity/pull/71 for master
- https://github.com/…rity/pull/72 for v3.1