I have a feature request idea for Rails 7.1 around generates_token_for

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

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] } 

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?