(Full context = latest response/discussion to this question: Button_to in email not posting)
I'd like a user to click a link in an email and be able to POST data in my database. My current understanding is:
Best practice (per latest answer to question above, as well as other answers I've already tried) = use GET to transmit a token to my website (so might be something like
example.com?token=asdfaiosugkljlfkdjslfjasklf
) and then have a script on my website to take that token, parse the data, and POST itThe reason tokens should be used with GET is because it's not secure; anyone can see the URI, it can be cached/bookmarked, and therefore, it can result in erroneous POSTs to the database if constantly submitted, i.e., I shouldn't do something like
example.com/:id/action
as a GET since this can trigger the action repeatedly (which in my case, since one of my actions results in deletion of records, that would be bad)
What I don't understand is how a token is any different, because it's not like every time the user opens his/her email, s/he sees a different link. Even if I passed a token and the link in the email were example.com?token=asdfaiosugkljlfkdjslfjasklf
, well this link could still be bookmarked/cached/whatever. As part of my research, I looked at emails that another web service sends me where in the email is a button I can click that POSTs something. Indeed each time I check the link, it's always the same: www.theirsite.com/?uid=ABC
where ABC is the Base64 token. And indeed I can click on it repeatedly, I can bookmark it and open it again and again. It just seems like a mask over data, but the same mask every single time, is pointless.
Now, if the mask is there for security's sake, I kind of get that because perhaps only your website is able to decode the token. But in my case, I just want to ensure that the URI doesn't inadvertently result in muliple repeat POSTings which, as I said would delete data. I don't have sensitive user information.
Of course, I realize that there are probably HUGE holes in my understanding so am opening this up for smarter people to explain what the "big guys" do in terms of:
Is it still necessary to use a token to transmit data (let's just say sensitive data because even if not useful now, I'm sure I'll need it at some point) via HTTP GET request when you need the data to POST to your database?
How do you ensure that the URI is only able to modify the database once? (is there a
first
method of some kind? after which the user gets a pop up that says they've already performed the action?)
Tokens serve a couple purposes here. First, they can be used to authenticate your action is legitimately coming from an email you sent, and not from someone brute-forcing a URL. Second, the token can be used to prevent accidental replays, or outright replay-attacks. Here's how:
When you send an email with a link to something like: /requests/1001/accept?token=asdf... you should actually be: A) randomly generating the token in a secure way, and B) storing the token into your database, perhaps with some additional metadata. More on the metadata in a bit.
The design should be: the request is valid if and only if the token hasn't been used, and the token is one-time use only. When the user clicks on your email link, you need to check if the provided token matches a row in your token table. THEN DELETE THE TOKEN FROM THE TABLE (unless you like race conditions, use a transaction). This is provably the simplest way to achieve what you want - you don't need to do anything else to ensure links can't be double-followed, and you can't do anything less (tracking whether a link has been used or not requires you remember some information specific to that link, by definition). The tokens you see in the emails you investigated operate in this way.
You can add other metadata to the token, such as the ID of only user that may legitimately use it, for example. But this may end up de-normalizing your database depending on how your other tables are set up.
As a side note, this isn't RESTful since it's a mutate action in a GET request, but in this case POST is overrated and doesn't give you anything over a GET. It is also difficult to implement in emails (you can hack it with rails's _method parameter, but it offers no real benefit).