goauthentik / goauthentik/authentik

Limit number of MFA devices per user (cap and/or auto-expire stale devices)

Open
#23,356 0 comments 0 reactions 0 assignees View on GitHub
enhancement/confirmed
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 2h
Merged PRs (30d)
659

Description

### Is your feature request related to a problem?

There is currently no way to limit how many MFA devices a user can enroll. Over time users accumulate authenticators (old phones, replaced security keys, stale TOTP entries) that are never cleaned up. This creates two concerns:

- **Security/hygiene:** abandoned-but-still-valid devices increase the attack surface and make it harder to reason about which credentials are trusted.
- **Administration:** admins have no policy lever to enforce an organizational limit (e.g. "at most 2 TOTP + 2 WebAuthn per user").

Today the only related controls are partial: the Email authenticator is hardcoded to one device per user, and the WebAuthn stage has `prevent_duplicate_devices` and `max_attempts` — but none of these cap the total device count or expire aging devices.

### Describe the solution you'd like

Open to either (or both) of these approaches and would like community/maintainer input on preference:

1. **Maximum device count parameter** — a configurable cap on the number of enrolled devices, enforced at enrollment time. Ideally configurable per authenticator enrollment stage (and/or per device class), with `0`/unset meaning unlimited, consistent with existing fields like WebAuthn `max_attempts`.

2. **Auto-expire stale devices** — automatically remove (or mark unusable) MFA devices that have not been used within a configurable age/threshold, similar in spirit to the Authenticator Validate stage's `last_auth_threshold`. This naturally bounds the number of *active* devices without a hard cap.

### Describe alternatives that you've considered

- **Expression Policy bound to the enrollment stage** that counts `Device.objects.devices_for_user(user)` and denies enrollment past a threshold. This works today but is per-deployment custom logic, isn't discoverable, and can't easily auto-clean stale devices.
- **Manual admin cleanup** of old devices — not scalable and error-prone.

### Additional context

- Email authenticator one-device limit: `authentik/stages/authenticator_email/stage.py`
- WebAuthn `prevent_duplicate_devices` / `max_attempts`: `authentik/stages/authenticator_webauthn/models.py`
- `last_auth_threshold` precedent for time-based behavior: `authentik/stages/authenticator_validate/models.py`

Question for the community: is a hard **device-count cap** preferred, an **auto-expire by age** mechanism, or both?

Contributor guide

Open the contributing guide

Research direction

Start by comparing authentik/stages/authenticator_email/stage.py, authentik/stages/authenticator_webauthn/models.py, and authentik/stages/authenticator_validate/models.py for the existing device-limit and threshold precedents. Clarify with maintainers whether the desired outcome is a count cap, stale-device expiry, or both; done requires an agreed policy and acceptance criteria before implementation.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authentication, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.