4 ms·
This is pretty common and has a bunch of advantages, like the fact you can link to and bookmark a particular state. Also, if you are careful you get undo and r
by AndrewStephens 4y ago
This is pretty common and has a bunch of advantages, like the fact you can link to and bookmark a particular state.
Also, if you are careful you get undo and redo for free with the browser's back button doing all the work for you.
The disadvantages are that your representation of internal state becomes part of the interface - if you ever change your app you need to deal with versioning the state so your new version can transparently handle the old state format.
If your app has a server component that acts on this state, be super careful about acting on it and treat it as you would any other input under user control.
If you app is completely client side, consider storing the state in the #fragment section of the URL. This never gets sent to the server. An example from my own site [0] - see how the fragment part of the URL changes as you select different topics.
There are also limits on just how much you can cram into a URL but with care you can shove a lot of state.
[0] https://sheep.horse/tagcloud.html#computing https://sheep.horse/tagcloud.html#computing
- typingerror 4y ago> If your app has a server component that acts on this state, be super careful about acting on it and treat it as you would any other input under user control. I would recommend signing it if it's generated by the server component, and checking the signature when the server component is provided this signed state. For example to do this in Node is quite straightforward. Key generation: const crypto = require('crypto'); crypto.generateKeyPair('ed25519', (e, pubkey, privkey) => { // save pubkey and privkey somewhere // ... } Signing: const data = Buffer.from(JSON.stringify(state)); const signature = crypto.sign(null, data, privkey); const signeddata = `${data.toString('base64')}.${signature.toString('base64')}`.replace(/=/g,''); Verification: const parts = signeddata.split('.'); const data = Buffer.from(parts[0], 'base64'); const signature = Buffer.from(parts[1], 'base64'); if (crypto.verify(null, data, pubkey, signature)) { // signature not verified, throw or return // ... } const state = JSON.parse(data); As the above uses Ed25519 the signatures are quite small too. It needs a bit more error checking, and might need extras like expiry time and such, but should be roughly sufficient.
- AndrewStephens 4y agoGood advice. For some apps you might also need to protect against replay attacks to prevent the user from reverting to a previous state in a way that you app should not allow (undo'ing a change of bank balance, etc). But if your state is so important it is probably better to not uses these techniques at all and just store the state on the server.
- awb 4y agoThis technique is client-side data only, so updating a client-side state would only appear to revert your bank balance on the UI, but wouldn’t trigger any server-side functions to do so. If you sent the client state to the server for some type of CRUD action or persistence, you’d need to sanitize and validate it first. And at that point like you said, why not just keep the state secure server-side and not trust the client.
- ivanhoe 4y agoExcellent idea for those apps that need that type of protection. For many apps however ability to manually edit the link to get to the different state is actually a free bonus feature. I've built a CRM/Cases management app for the client that stores data filters in the url, and while there's a nice UI to control filters, we observed that lots of users simply go and edit the url directly to quickly get what they need.
- Brometheus 4y agoI did this for passing parameter to a twitter card generator php script :joy:
- hot_gril 4y ago> The disadvantages are that your representation of internal state becomes part of the interface This is the biggest reason to avoid this. URLs aren't meant to be used this way. I'd only do it if I wanted a quick and dirty solution.
- AndrewStephens 4y ago> URLs aren't meant to be used this way I disagree, URLs are supposed to point to a resource - in this case the resource is the client state of the app (aka "deep linking"). It is a useful approach. Of course, it can easily be misused.
- hot_gril 4y agoThe suggestion was to put your entire client state into it, which is probably more than just the resource location (aka deep link).
- naasking 4y agoThere is no such thing as "client state" separate from the notion of resource under REST. The URL is meaningful only to a server. If a server can encode data in the URL so it doesn't need to persist it, that's perfectly fine, so long as the URL conforms to all expectations of resources, ie. caching directives, lifetime, etc.
- hot_gril 4y agoModern web apps often use the URL in ways that only matter to the client and can be edited client-side without issuing another request to the server, unless you reload manually, in which case everything is reset except the URL state. This doesn't violate REST principles. Some of that makes sense to put in the URL so users can return to past state, some is very temporary and wouldn't make sense at all to persist across a reload/back. For example, you may track the scroll position for the purpose of loading more content when the user reaches the bottom of a feed, but you don't want to persist that across reloads. And the user may click on some content and anchor the view to it, which you do persist in the URL for the purpose of history or sharing.
- amadeuspagel 4y ago> This is pretty common and has a bunch of advantages, like the fact you can link to and bookmark a particular state. Conversely, you can't link to the newest version.
- ivanhoe 4y agoIMHO you should never store the data itself in the url, only the params that you use to fetch the data. So if the page is the list of the latest 10 posts in a category, you'll store the category in the url and the sorting criteria, not the IDs of the posts themselves. On the refresh you always get the fresh data.
- cryptonector 4y agoIf the state cookie is generated by the server, then the best thing to do is to encrypt all application state cookies, and include an expiration time in them. Also, consider whether you want to allow the user to share the URI or not. If not, then you need to always check that the user is authenticated as whatever you wrote in the state cookie.
- moralestapia 4y ago>Also, if you are careful you get undo and redo for free with the browser's back button doing all the work for you. Ugh! Please don't do this!