Tony Messias

July 13, 2022

Rotating Signed Global IDs in Attachments

I was watching the GoRails episode called How To Update ActionText Attachments which deals with the issue that apparently popped up in the Rails 6 to Rails 7 upgrade and it's still undocumented. Looks like the key generator changed from SHA1 to SHA256 in the upgrade, which broke some people's attachments. Here's the discussion about that.

As I'm maintaining a port of the ActionText gem called Rich Text Laravel, I thought this would be a fun exercise.

A little bit of context before we talk about the solution: some more advanced attachments get their own `sgid` attribute, which is short for "signed global ID". This is generated using another gem port called Globalid Laravel. By default, it signs these global IDs using a derived key based on the app's secret key.

This means that if we change our `APP_KEY` for whatever reason, the signed global IDs stored in the database cannot be verified, which will result in a bunch of unknown attachments showing up in the Rich Text documents when rendering.

When changing the `APP_KEY` we need to rotate our Rich Text Attachments, just like we'd have to do for anything that we store encrypted using Laravel's Encryption.

To rotate, we'd have to loop over all our stored RichText entries, replacing all instances of the `<rich-text-attachment>` tag in the documents with a new version of it. The trick is: we need to keep the old derived key around when we're running this migration command because we need that to use in the Locator so it's able to verify the old `sgid` using the old key and update it with a new `sgid` that is signed using the new version of the derived key based on the app's new secret.

Here's an example of how we could go about it. I've added a pseudo-code of the Ruby on Rails version of that code just for fun. There might be other and maybe better ways to achieve this, but that's my first take on handling this.

It might be easy to forget that we need to rotate all of our encrypted and signed data when the `APP_KEY` changes, so keep that in mind!