PHPackages                             edunext-eu/simplesamlphp-module-simpletotp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. edunext-eu/simplesamlphp-module-simpletotp

ActiveSimplesamlphp-module[Authentication &amp; Authorization](/categories/authentication)

edunext-eu/simplesamlphp-module-simpletotp
==========================================

A highly configurable yet simple to use TOTP based two-factor authentication processing module for SimpleSAMLphp

2.1(2w ago)11MITPHPPHP &gt;=7.4

Since Jan 21Pushed 2w ago1 watchersCompare

[ Source](https://github.com/edunext-eu/SimpleTOTP)[ Packagist](https://packagist.org/packages/edunext-eu/simplesamlphp-module-simpletotp)[ RSS](/packages/edunext-eu-simplesamlphp-module-simpletotp/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (2)Dependencies (2)Versions (3)Used By (0)

SimpleTOTP
==========

[](#simpletotp)

A SimpleSAMLphp auth processing filter that adds TOTP-based MFA on an IdP or SP. Recommended placement is the IdP to keep TOTP secrets off SPs. Only HMAC-SHA1 TOTP is supported for maximum compatibility with authenticator apps. Current release: **2.1**. See [UPGRADING.md](UPGRADING.md) when updating from 2.0.

Key features
------------

[](#key-features)

- Works as an authproc filter (IdP or SP).
- Configurable secret attribute and validation timeout.
- Optional clock-drift window for TOTP verification.
- Atomic, credential-bound brute-force throttling.
- TOTP replay protection and one-time authentication state.
- Automatic removal of the TOTP secret from outbound attributes.
- Optional bypass when secret is an empty string.

Installation
------------

[](#installation)

### Via Git

[](#via-git)

Clone into the SimpleSAMLphp `modules/` directory.

### Via Composer

[](#via-composer)

Install the module from Packagist:

```
composer require edunext-eu/simplesamlphp-module-simpletotp
```

Quick start (IdP recommended)
-----------------------------

[](#quick-start-idp-recommended)

Add the filter to `authproc.idp` after the authentication source supplies the seed and before filters that rename identifiers. For example, when `core:AttributeMap` runs at priority 35:

```
'authproc.idp' => [
    34 => [
        'class' => 'simpletotp:mfa',
        'secret_attr' => 'totp_secret',
        'enforce_mfa' => false,
        'allow_empty_secret' => false,
        'totp_window' => 0,
        'max_attempts' => 5,
        'attempt_window' => 300,
        'totp_rate_limit_storage' => 'session',
        'totp_rate_limit_key' => 'uid',
        'restart_enabled' => false,
        'clear_cookies' => [],
    ],

    35 => [
        'class' => 'core:AttributeMap',
        'name2oid',
    ],
],
```

`validation_timeout` is intentionally omitted above: its default is 60 minutes. It caches the fact that MFA succeeded for the same SimpleSAMLphp session, authentication context, identity, and TOTP seed. It does not extend the validity of a six-digit TOTP code.

For a Redis-backed deployment, use the following values in the filter:

```
'totp_rate_limit_storage' => 'both',
'totp_rate_limit_key' => 'uid',
'restart_enabled' => true,
'clear_cookies' => [],
// Optional when the surrounding state has no suitable restart URL:
//'restart_url' => 'https://idp.example.org/your-login-entry-point',
```

TOTP secret handling
--------------------

[](#totp-secret-handling)

`secret_attr` is the long-lived Base32 seed (for example, `ga_secret`), not the six-digit code entered by the user. Version 2.1 removes that attribute as soon as the `simpletotp:mfa` filter runs, including cached, optional-MFA, and error paths. When a prompt is required, the seed exists only in saved server-side authentication state and is removed before the authproc chain resumes.

With the recommended IdP-side placement:

- the seed is not included in the SAML assertion and is never sent to an SP;
- no downstream `core:AttributeAlter` cleanup filter is required; and
- the TOTP filter should not also be installed at each SP.

Place this filter after the authentication source has supplied the seed but before filters such as `core:AttributeMap` that rename the user identifier used for rate limiting. Filters that run later cannot read `secret_attr`.

Module enablement
-----------------

[](#module-enablement)

Enable the module via `config.php` by setting:

```
'module.enable' => [
	'simpletotp' => true,
],
```

This is preferred over the legacy `modules//enable` file because it is explicit and visible in configuration management.

SP-side verification (legacy; not recommended)
----------------------------------------------

[](#sp-side-verification-legacy-not-recommended)

SP-side verification remains possible. In that deployment model:

1. Do not run `simpletotp:mfa` at the IdP. If the IdP filter runs, it removes the seed before producing the SAML assertion.
2. Configure the IdP to release the seed attribute only to the specific, trusted SP that needs it.
3. Run the filter in that SP's `authproc.sp` chain:

```
'authproc.sp' => [
    10 => [
        'class' => 'simpletotp:mfa',
        'secret_attr' => 'totp_secret',
        'enforce_mfa' => false,
        'allow_empty_secret' => false,
        'totp_rate_limit_storage' => 'session',
    ],
],
```

The SP-side filter consumes and removes the seed before the SP's downstream processing or application receives the attributes. This mode therefore still works, but the seed crosses the federation boundary and every receiving SP becomes responsible for protecting it.

Never send the user's six-digit TOTP code to an SP. If the SP only needs to know that MFA occurred, communicate an authentication-assurance result (for example, an agreed SAML AuthnContext or assertion attribute) rather than the seed or code. SimpleTOTP does not currently create that assurance signal.

Configuration notes
-------------------

[](#configuration-notes)

- secret\_attr: defaults to `totp_secret`. If your attributes use a different name (e.g. `ga_secret`), set it explicitly. The module always removes this attribute before authentication processing continues.
- enforce\_mfa: when true, users without a configured secret are blocked (or redirected to `not_configured_url`).
- allow\_empty\_secret: when true, an empty-string secret is treated as "not configured" and the user is allowed to continue.
- Interaction: `allow_empty_secret` only applies when the secret attribute exists but is an empty string. It does not override `enforce_mfa` for truly missing secrets.
- Null vs empty: "missing" means the attribute is absent or null, while "empty" means the attribute exists but the value is an empty string. This lets you use empty strings as an explicit "MFA disabled" flag.
- Recommended for DB-backed secrets: if your DB uses empty strings (e.g., `two_factor_secret = ''`) for "no MFA", set `allow_empty_secret = true` and keep `enforce_mfa = false`. For strict MFA, set `enforce_mfa = true` and require non-empty secrets.
- validation\_timeout: minutes to cache a successful MFA before re-prompting (default 60). The cache is bound to the authentication context, subject, and current TOTP secret, so switching accounts, IdPs/auth sources, or rotating the secret cannot reuse it. This setting does not change the 30-second lifetime of a TOTP code.
- totp\_window: number of 30-second steps to accept before/after the current step.
    - 0 = only the current 30s step (strict, most secure)
    - 1 = accept codes from 30s before or after (90s total window)
    - 2 = accept codes from 60s before or after (150s total window)
    - Values above 10 are rejected as unsafe configuration.
- max\_attempts / attempt\_window: throttles brute-force attempts across browser sessions using atomic state below the configured SimpleSAMLphp `cachedir`.
- Form fields: new integrations should post `totp` with `autocomplete="one-time-code"`; `code` is accepted for legacy forms.
- Start over button: set `restart_enabled = true` in the `simpletotp:mfa` authproc configuration. Set `restart_url` to the trusted URL that initiates a fresh login; when omitted, SimpleSAMLphp's state restart URL is used if available.
    - Restart accepts POST only, requires a one-time state-bound token, consumes the old authentication state, and only redirects to a server-side URL accepted by SimpleSAMLphp.
    - If `clear_cookies` is omitted or empty, the module clears the active SimpleSAMLphp session-handler cookie and `session.authtoken.cookiename` using their configured path, domain, Secure, HTTP-only, and SameSite attributes.
- TOTP rate limit storage: the historical `session` value now uses an atomic local counter keyed by the credential, so clearing cookies does not reset it. Set `totp_rate_limit_storage` to `store` or `both` to add shared state across application servers.
    - Redis uses atomic Lua operations and is recommended for clustered deployments.
    - SQL and memcache use the SimpleSAMLphp Store API plus a per-node lock. This prevents worker races on each server, but the Store API has no compare-and-set operation; Redis is required for an exact cluster-wide maximum.
    - If the configured key is missing at runtime, the limiter falls back to the TOTP secret and logs a warning.
    - `totp_rate_limit_key` uses built-in types (`uid`, `secret`, `ip`) or `attr:` to refer to a specific attribute (e.g. `attr:mail`, `attr:eduPersonPrincipalName`). The attribute must exist in the user attributes for your IdP/SP, otherwise it falls back to the secret.
    - For federations that standardize identifiers (e.g. `eduPersonPrincipalName`, `eduPersonUniqueID`, `subject-id`, `pairwise-id`), prefer one of those stable identifiers via `attr:`.
    - If `store.type` is unavailable or set to `phpsession`, verification remains protected by the atomic local limiter and a warning is logged.
    - If `cachedir` is unavailable on an older installation, the module falls back to SimpleSAMLphp's legacy temporary-directory helper.
    - TOTP codes are fixed at 6 digits.
    - A successfully accepted time slice cannot be reused for the same credential.
    - `clear_cookies` only uses admin-configured cookie names; it is not accepted from user input.
    - Cookie clearing only affects the current host; it does not clear cookies set on other subdomains or parent domains.

Security notes
--------------

[](#security-notes)

- TOTP seeds should remain on the IdP. Version 2.1 removes the configured seed attribute immediately when this filter runs.
- Keep totp\_window small (default 0) to reduce acceptance of old codes.
- Atomic brute-force throttling and replay protection are built in.
- Verification uses a timing-safe comparison, strict canonical Base32 decoding, fixed-length checking, bounded secret input, and 64-bit counter packing. Primitive hardening was reviewed and backported from  where compatible.

Translations
------------

[](#translations)

All user-facing strings are translatable via gettext. Add or edit translations in `locales//LC_MESSAGES/simpletotp.po` (e.g. `locales/it/LC_MESSAGES/simpletotp.po` or `locales/es/LC_MESSAGES/simpletotp.po`). SimpleSAMLphp selects the language based on the user's locale settings. Translations are best-effort and may need review by native speakers; some locales may still use English strings. Pull requests or issues to refine wording are welcome.

Fork notice
-----------

[](#fork-notice)

This repository is maintained at  and is a fork of the original module. If you are upgrading from the original, update your Composer package name to `edunext-eu/simplesamlphp-module-simpletotp`. Third-party TOTP primitive notices are retained in [THIRD\_PARTY\_NOTICES.md](THIRD_PARTY_NOTICES.md).

Changes in this fork
--------------------

[](#changes-in-this-fork)

- Version 2.1 strips the secret automatically, isolates cached MFA by credential, rejects TOTP replay, consumes successful state, and hardens restart and rate limiting.
- Fixed MFA bypass by setting lastverified only on successful TOTP verification.
- Updated BadRequest class for newer SimpleSAMLphp.
- Added empty-code validation and removed sensitive debug logging.
- Added totp\_window for clock drift tolerance.
- Added rate limiting for TOTP attempts.
- Removed the legacy token generator endpoint.
- Tightened StateId handling to accept only GET/POST (no generic $\_REQUEST).
- Added timing-safe code comparison and stricter base32 validation.
- Added translations and documentation improvements.
- Removed the legacy `default-enable` file; use `module.enable` in config.php instead.

Maintenance
-----------

[](#maintenance)

Long-term maintenance for this module is not guaranteed. If you want to take stewardship, open an issue.

Testing
-------

[](#testing)

Run `composer test`, or execute the four PHP scripts under `tests/`directly. The suite covers RFC vectors, strict Base32 parsing, parallel rate limiting, replay rejection, credential-bound MFA caching, state consumption, and restart CSRF/cookie behavior.

Disclaimer
----------

[](#disclaimer)

This software is provided "as is" without warranty of any kind; use at your own risk.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance96

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~189 days

Total

2

Last Release

20d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/29c64b31adf275e12e22f4830cace48d63af71a5263fcde41ea4c87629bd0a14?d=identicon)[PackEDUNEXT](/maintainers/PackEDUNEXT)

---

Top Contributors

[![clmcavaney](https://avatars.githubusercontent.com/u/5875512?v=4)](https://github.com/clmcavaney "clmcavaney (13 commits)")[![aidan-](https://avatars.githubusercontent.com/u/327286?v=4)](https://github.com/aidan- "aidan- (10 commits)")[![shoaibali](https://avatars.githubusercontent.com/u/180494?v=4)](https://github.com/shoaibali "shoaibali (5 commits)")[![gavio-dot](https://avatars.githubusercontent.com/u/196302292?v=4)](https://github.com/gavio-dot "gavio-dot (2 commits)")[![jfautley](https://avatars.githubusercontent.com/u/241795?v=4)](https://github.com/jfautley "jfautley (2 commits)")[![ziemek99](https://avatars.githubusercontent.com/u/15923635?v=4)](https://github.com/ziemek99 "ziemek99 (1 commits)")

### Embed Badge

![Health badge](/badges/edunext-eu-simplesamlphp-module-simpletotp/health.svg)

```
[![Health](https://phpackages.com/badges/edunext-eu-simplesamlphp-module-simpletotp/health.svg)](https://phpackages.com/packages/edunext-eu-simplesamlphp-module-simpletotp)
```

###  Alternatives

[simplesamlphp/simplesamlphp

A PHP implementation of a SAML 2.0 service provider and identity provider.

1.1k13.2M233](/packages/simplesamlphp-simplesamlphp)[simplesamlphp/simplesamlphp-module-oidc

A SimpleSAMLphp module adding support for the OpenID Connect protocol

5018.6k1](/packages/simplesamlphp-simplesamlphp-module-oidc)[drupalauth/simplesamlphp-module-drupalauth

A SimpleSAMLphp module adding support for Drupal as the authentication source.

25329.1k1](/packages/drupalauth-simplesamlphp-module-drupalauth)[cirrusidentity/simplesamlphp-module-authoauth2

SSP Module for Oauth2 authentication sources

33113.4k](/packages/cirrusidentity-simplesamlphp-module-authoauth2)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
