Requerir la contraseña para administrar las passkeys - #1211
Draft
santiagorodriguez96 wants to merge 1 commit into
Draft
Requerir la contraseña para administrar las passkeys#1211santiagorodriguez96 wants to merge 1 commit into
santiagorodriguez96 wants to merge 1 commit into
Conversation
santiagorodriguez96
force-pushed
the
sr--reauthenticate-before-adding-passkey
branch
from
August 21, 2026 18:08
67233ca to
624a78c
Compare
Until now the only check before adding a passkey was "is someone signed in?". Any valid session passed. If an attacker steals a session they can enrol their own passkey, which gives them a way back into the account that survives a password change. Setting `user_verification: "required"` during registration does not help, because on the attacker's own device that fingerprint is the attacker's. So a user now has to prove themselves again before they can read, add or delete a passkey, the way GitHub's sudo mode works. Proving themselves writes a grant that lasts 15 minutes and slides forward on every gated request, so adding two passkeys in a row only asks once. Signing in writes a grant too, so a user who has just signed in is never interrupted. The code is split so it can move to a gem later. `Devise::Reauthentication` and `Devise::Reauthenticatable` handle the grant and the gate and know about no particular proof; `Devise::ReauthenticationsController` checks the password and is the seam other proofs extend. The gate declares no `before_action` of its own, because each controller has to pick which actions to gate. Gating `index` is not ideal: reading your own passkey list should not need a challenge. Splitting the page into `index` and `new` is separate work. Deleting a passkey is gated too. A stolen session could otherwise strip an account of every passkey it signs in with. Password checks go through `valid_for_authentication?` rather than `valid_password?` so `lockable` keeps counting, and this page is not a way around `config.maximum_attempts`.
santiagorodriguez96
force-pushed
the
sr--reauthenticate-before-adding-passkey
branch
from
August 21, 2026 18:18
624a78c to
9df995e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivación
Hoy, para agregar una passkey, solo verificamos que haya una sesión iniciada. Cualquier sesión válida alcanza.
Esto es un riesgo. Si alguien roba una sesión, puede registrar su propia passkey. Esa passkey le da una entrada propia a la cuenta, y la conserva aunque la persona dueña cambie su contraseña.
Detalles
Antes de entrar a la página de passkeys, ahora le pedimos la contraseña de nuevo. El approach es parecido al modo "sudo" que se usa en GitHub.
Iniciar sesión también cuenta como prueba, así que un usuario acaba de loguearse no va a tener que reautenticarse. Esto es así por 15 minutos – una vez pasados los 15 minutos, la sesión se considera "inactiva" y se va a requerir una contraseña para agregar una passkey. A su vez, cada request actualiza la sesión como "activa" por 15 minutos más. Estos 15 minutos son configurables.
Verificamos la contraseña usando
valid_for_authentication?en vez de simplementevalid_password?, así los intentos fallidos suman alockable.El código queda separado en dos capas, pensando en extraerlo a su propia gema más adelante:
Devise::ReauthenticationyDevise::ReauthenticatableDevise::ReauthenticationsControllerDevise::Reauthenticatableno declara ningúnbefore_action. Cada controlador elige qué acciones proteger; en nuestra app de momento solo protegemosindex,createydestroydeUsers::PasskeysController.TODO