Repository navigation
Conversation
…cheb#150 Lets an application store the authenticator secret encrypted. The new secret_decryptor option names a service implementing SecretDecryptorInterface, which the TOTP factories call before using the secret. Without the option, nothing changes.
|
Thanks for working on this and for the detailled explanation. I'm not fully convinced this should be handled within the bundle. In my opinion, such encryption logic is better placed at the persistence/ORM layer, where data is read from and written to the database. This bundle's responsibility should be to process the secret, though it should not need to know how that value was stored. There are great solutions to address this problem on the data model layer:
Given these solutions are solving the problem with a bit of configuration, I'd rather prefer one of these, than adding an abstraction layer to the bundle that needs to be maintained, and requires users to implement custom code. I'd be open to add to the documentation to mention this use case and point users towards ORM-level encrypted fields as the recommended approach. |
Description
Some applications want to store the Google Authenticator or TOTP secret encrypted in the database, so that a database leak does not also leak the secrets. Today the bundle reads the secret straight from the user object, so there is no clean way to decrypt it first. The only option is to decorate the authenticator service and wrap the user object. This was discussed in #150, where you suggested an interface plus an optional service in the configuration. This PR does that.
What it adds:
SecretDecryptorInterface, with one method:decrypt(string $secret): string.secret_decryptorforgoogleand fortotp. It takes the id of a service that implements the interface:GoogleTotpFactoryandTotpFactorycall the decryptor before they create the TOTP object. Checking a code, the QR code content and the validator constraints all go through these factories, so they all get the plain secret.Backwards compatibility: the option is
nullby default. The decryptor is a new optional, last constructor argument of both factories. Without the option, nothing changes.Encrypting the secret is still the job of the application, because the application stores it.
generateSecret()returns a plain secret, as before. The Google and TOTP docs have a new section explaining this.One open question: should
decrypt()also get the user object, e.g.decrypt(string $secret, object $user)? That would help applications that use a different key per user or per customer. I kept it to the secret only to keep it simple, but it is easy to change before this is merged.