A while back this PR was merged: Add `ActiveRecord::Base::generates_token_for` by jonathanhefner · Pull Request #44189 · rails/rails · GitHub
It adds generates_token_for
which lets you add a payload to the token which is used to help validate the token, but there doesn’t seem to be a mechanism to read the payload out of the token that’s publicly available.
Inside of that PR there’s this method:
def resolve_token(token)
payload = message_verifier.verified(token, purpose: full_purpose)
model = yield(payload[0]) if payload
model if model && payload_for(model) == payload
end
The first line grabs the payload. What do you think about 2 things:
- A standard around how this payload is structured so that you can include more than 1 piece of data
- A way to get the payload out of a token so your app can do whatever you need with it
One use case I had was a sign in link being emailed to a user. I want to use the email address as the payload for validation purposes so the link is invalid if the email changes. That can already be handled today without new behavior.
However, I also want to include a return_to
URL in the token so that when the user clicks the link with the token they are logged in and returned to the protected URL they tried to access.
Example workflow:
- User is logged out and visits https://example.com/dashboard
- A before_action triggers and writes
return_to
to the session with that URL - They get redirected to https://example.com/sign_in (handled by an Authentication concern)
- They put in their email address and submit the form
- The email template includes a link to
https://example.com/sign_in/:token
That token
would be generated with generates_token_for
with a payload of their email address for validation but also the return_to
URL so when they click it to sign in, the sign in confirm action can redirect_to
https://example.com/dashboard.
The above is not possible with the current implementation. A workaround would be adding the ?return_to
query string to the email template link which works but it felt like it could be cleaner to include this detail into the token?
What do you think? Are there other use cases where you might want to put custom data into a token and then read / act upon it?
If so, the token verify portion of generates_token_for
would need to know which attribute should be used for verification purposes. There’s also the issue of figuring how this data could be passed into the method.
It could look something like this:
generates_token_for :sign_in, expires_in: 2.hours do
{ verify: email, return_to: session[:return_to] }
end
The verify
property could be used by Rails to verify whatever you’re verifying. Anything else is fair game, such as dropping in return_to
or maybe a nested data
structure of your choosing.
But this implementation falls apart as is because this is normally defined in your model based on a model attribute, but in the above case you would be reading from the session or anything if it’s arbitrary data.
Maybe you could do something with CurrentAttributes for this specific use case but it feels like you’re delving down the wrong path?