Skip to content

Requerir la contraseña para administrar las passkeys - #1211

Draft
santiagorodriguez96 wants to merge 1 commit into
masterfrom
sr--reauthenticate-before-adding-passkey
Draft

Requerir la contraseña para administrar las passkeys#1211
santiagorodriguez96 wants to merge 1 commit into
masterfrom
sr--reauthenticate-before-adding-passkey

Conversation

@santiagorodriguez96

@santiagorodriguez96 santiagorodriguez96 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

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 simplemente valid_password?, así los intentos fallidos suman a lockable.

El código queda separado en dos capas, pensando en extraerlo a su propia gema más adelante:

Capa Qué hace
Devise::Reauthentication y Devise::Reauthenticatable Llevan el registro de la última validación y deciden cuándo interrumpir.
Devise::ReauthenticationsController Muestra la página y verifica la contraseña.

Devise::Reauthenticatable no declara ningún before_action. Cada controlador elige qué acciones proteger; en nuestra app de momento solo protegemos index, create y destroy de Users::PasskeysController.

TODO

  • Permitir usar passkeys para reautenticarse
  • Los usuarios que usan SSO no pueden reautenticarse porque se les setea una contraseña random. Hay que ver cómo resolvemos esos casos

@santiagorodriguez96
santiagorodriguez96 force-pushed the sr--reauthenticate-before-adding-passkey branch from 67233ca to 624a78c Compare August 21, 2026 18:08
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
santiagorodriguez96 force-pushed the sr--reauthenticate-before-adding-passkey branch from 624a78c to 9df995e Compare August 21, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant