Skip to content
 
 

Repository files navigation

imio.googleauthenticator

Forked from collective.googleauthenticator.

Two-step verification for Plone 4 with use of Google Authenticator app. This app allows users to enable the two-step verification for their Plone accounts. A mobile device with Google Authenticator app installed is required. Usage of two-step verification is optonal, unless site admins have forced it (configurable in app control panel). Admins can white-list the IPs, for which the two-step verification would be skipped.

Prerequiresites

  • GoogleAuthenticator app installed on mobile device (official app available for Android, iPhone and Blackberry; third-party app exists for Windows Phone).
  • Plone 4 (tested with Plone >= 4.2.6)

Usage

Case 1: Enabling the two-step verification

Pre-conditions: User is not logged into the Plone site, does not yet have two-step verification enabled and has installed the Google Authenticator app (including the advised bar code scanner) on his mobile device.

From any page follow the "Enable two-step verification" link in the menu (next to "Log out").

_static/01_menu_enable.png

That will bring you to a page on which a bar code is shown.

_static/02_two_step_verification_setup.png

You're supposed to scan the bar code shown on the page using the bar code scanner installed on your mobile device, which you're going to use for verification.

After you have successfully scanned the bar code, enter the token shown in the "Enter the verification code to activate two-step verification" field for confirmation and press the "Verify" button.

Upon successful confirmation (you should see a message stating that) the two-step verification is enabled for your account.

_static/03_enable_two_step_verification_confirmation_message.png

Case 2: Two-step verification

Pre-conditions: User is not logged in and has enabled the two-step verification.

When you log into the Plone site (just using username and password), you would see an extra screen on which you are asked to provide the token, generated by Google Authenticator.

_static/04_login_token_form.png

You should then open the Google Authenticator app on your mobile device and type in the token shown into the "Enter code" field.

If token is valid, you would be logged in.

Case 3: Lost tokens

Pre-conditions: User is not logged in, has enabled the two-step verification.

There might be cases when you have lost your token (either removed it from your app by accident or lost the mobile device). For such cases, you can reset the bar code.

Log into the Plone site (just using username and password), for to see the extra screen on which you are asked to provide the token, generated by Google Authenticator and follow the link (help text of the "Enter code" field). You would then land on the page where from you can request the bar code reset.

_static/05_request_to_reset_bar_code.png

Enter your username in the "Username" field, press the "Submit" button. Link for resetting your bar code appear in your mailbox shortly. Having clicked on the link to reset the bar code, would bring your to a page where you can scan the bar-code same way you have done it when setting up the two-step verification.

_static/06_reset_two_step_verification_bar_code.png

Scan the code with your mobile device and enter the code in the "Enter the verification code to activate the two-step verification" field.

Upon successful confirmation (you should see a message stating that) your bar code is reset.

_static/07_bar_code_reset_confirmation_message.png

Case 4: Disabling the two-step verification

Pre-conditions: User is logged in and has enabled the two-step verification.

From any page follow the "Disable two-step verification" link in the menu (next to "Log out").

_static/08_menu_disable.png

After which you would get a message.

_static/08_disable_two_step_verification_confirmation_message.png

Installation

Buildout

>>> [instance]
>>> eggs +=
>>>     imio.googleauthenticator
>>> zcml +=
>>>     imio.googleauthenticator

Seed encryption key (required)

This is the Fernet key that encrypts every user's TOTP seed at rest, stored as v1$<token>. Without it set, three things stop working -- not only login, which is the symptom an operator notices first:

  • enrollment fails -- the setup form cannot generate or store a seed.
  • login fails for any user with two-step verification enabled -- refused outright, never silently downgraded to password-only.
  • new account creation fails entirely. userCreatedHandler runs on every IPrincipalCreatedEvent, and with globally_enabled defaulting on it calls get_or_create_secret(). A missing key raises there, the transaction aborts, and registration plus plone.api.user.create() both stop working. This is deliberate fail-closed behaviour, not a bug: enrolling a user with no recoverable second factor would be worse.

There is no plaintext fallback, by design.

Generate one with:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key())"

Where it goes, and who supplies it -- this is the part a deployer needs, and the part this repository does not own:

  • as an environment-vars entry on the Zope instance process, one per ZEO client. It is per ZEO client, not per database -- it is never stored in the ZODB, and every client needs the identical value.
  • this package's own base.cfg deliberately does not declare it on [instance]. environment-vars is whitespace-separated NAME value; an option reference defaulting to empty would emit a bare token and fail the buildout, and a literal placeholder would be worse still -- production would encrypt every seed under a key any reader of this repository can see, while suppressing the CRITICAL warning below because the key would no longer be absent. The deployment buildout supplies [instance]'s copy, exactly the way SSO_APPS_CLIENT_SECRET already arrives: server.dmsmail/base.cfg reads it through os.getenv().
  • for local development, run export IMIO_GOOGLEAUTHENTICATOR_SEED_KEY=<generated value> in your shell before bin/instance fg. Without it, a dev instance starts fine, logs the CRITICAL line below, and cannot enrol anybody -- correct, but confusing if undocumented.
  • bin/test needs no action: base.cfg's [testenv] section carries a throwaway key, and [test]'s environment = testenv hands it to the generated test runner -- which is also how CI inherits it, since CI only runs bin/buildout then bin/test. tests/test_subscribers.py's test_seed_key_is_present_in_the_test_environment asserts that this keeps being true.

The failure mode this section exists to document: one ZEO client with a stale or missing Puppet fragment does not fail visibly. It produces InvalidToken for the fraction of logins the load balancer happens to route to that client, intermittently, following no per-user pattern, with nothing in the database to inspect -- the seeds are fine, the registry is fine, only that one process's environment is wrong. At boot, that client logs one CRITICAL line naming the variable (see below); at runtime, watch for an intermittent 500 on the token form. Rotating the key makes every existing enrolled seed undecryptable and requires every user to re-enrol, so it is not a routine operation.

At Zope startup, a missing key logs one line at CRITICAL naming IMIO_GOOGLEAUTHENTICATOR_SEED_KEY and states the consequence; Zope still reaches "Ready to handle requests" rather than aborting -- the absence is loud, never fatal to the process itself.

The production value ships as a concat::fragment in the separate industrialisation repository (modules/plone/manifests/buildout.pp), following the same path SSO_APPS_CLIENT_SECRET already takes (buildout.pp -> the deployment's base.cfg -> os.getenv()). This is not one of this repository's commits. The code above is complete and fully tested without it; the feature is not deployable until that Puppet change ships.

ZMI

ZMI -> portal_quickinstaller

Choose "Google Authenticator Plone" and install it.

ZMI -> acl_users

  1. Choose "google_auth (Google Authenticator plugin (imio.googleauthenticator))".

  2. Verify the "Active plugins" section of "Authentication" has the following plugins in this order, with "google_auth" first -- critical!:

    • google_auth
    • session
    • source_users

    It is critical because authenticateCredentials wipes the shared credentials dict in place once it decides to challenge -- only authenticators listed after google_auth are blinded by that wipe; one listed before it would still see the original password and could log the user in before the second factor is checked.

    This order is no longer a manual install step: the profile sets it itself with movePluginsTop and re-asserts it on every re-application, so the step above is a verification, not an instruction. If "google_auth" is ever found out of first place -- for example, another add-on's own install step reordered the list after this one ran -- the recovery is to re-apply the imio.googleauthenticator:default profile from portal_setup -> Import, not to drag the row by hand in the ZMI. The profile is now the authoritative source for this ordering.

Configuration options

App control panel can be accessed at http://your-plone-site.com/@@google-authenticator-settings

_static/09_control_panel.png

Secret Key

Site secret key - can be any string. See it as some sort of a password.

Globally enabled

If checked, two-step verification is globally force-enabled for all site users and they no longer have an option to disable it; this applies to all new users (just registered accounts) as well.

White-listed IP addresses or IP ranges

List of white-listed IP addresses or ranges - one per line. If user comes from one of those, the two-step verification is skipped even if user has enabled it or two-step verification is globally enabled.

For specifying IP ranges the CIDR slash notation can be used, for example: 192.168.0.0/16.

Extra

Additionals options of the control panel are:

  • Enable two-step verification for all users.
  • Disable two-step verification for all users.

Notes

It's important that Google Authenticator comes as first in the ZMI -> acl_users -> Authentication.

Tested in combination with the following products:

What two-step verification does not cover

This plugin is registered in the Plone site's own acl_users, so it only ever sees logins that the site's own PAS authenticates. An account defined in the Zope root user folder -- typically the buildout's inituser admin -- is authenticated above the site: this plugin's password pre-check delegates to the site's other IAuthenticationPlugins, none of which can resolve a root account, so it declines to veto and the root user folder logs the user in on the password alone. Enrolment refuses such an account outright rather than reporting success for a second factor that will never be demanded.

There is a second carve-out above even the plugin machinery: PluggableAuthService's own _extractUserIds tries the emergency user before the authenticator loop runs, and again after it, with the upstream comment "Emergency user via HTTP basic auth always wins" (PluggableAuthService.py lines 630-636 and 677-679). No plugin ordering, no extractor change and nothing in this package reaches that path -- it sits above PAS's plugin machinery entirely, and no plugin can close it.

Protecting the Zope root account is therefore a deployment concern, not a site-configuration one: restrict /Control_Panel and the root ZMI at the front end, or do not ship a root password at all. Nothing inside the Plone site can do it. This is deliberately out of scope for this package, which exists to protect in-site users and site admins until MFA moves to Keycloak.

HTTP Basic Auth, WebDAV, FTP and XML-RPC

A human account with two-step verification enabled cannot be used over HTTP Basic Auth, WebDAV, FTP or XML-RPC, no matter how the site is configured -- none of those protocols has anywhere to enter a six-digit code. This follows from the second factor itself, not from any setting below.

The credentials_basic_auth extractor was reviewed for this milestone (MFA-03) and, on 2026-07-31, kept active rather than deactivated. The review covered three iMio repositories -- imio.dms.mail, server.dmsmail and industrialisation -- and found no live dependency on HTTP Basic Auth against this Plone site's own acl_users: the one webdav-address setting found is commented out everywhere it appears, no XML-RPC client was found, one Basic Auth script authenticates outward to a different site, and pack_zeo.sh targets the Zope-root Control_Panel, above any Plone site's acl_users, so it is unaffected either way. That search was not exhaustive across every iMio repository -- it cannot prove no external consumer exists. Because credentials_basic_auth stays active, the protection on that path is this plugin's position at index 0 among IAuthenticationPlugins (see "ZMI -> acl_users" above); had it instead been deactivated, Basic Auth would stop authenticating against this site's acl_users for any user, 2FA-enabled or not, and reactivating the extractor in the ZMI would reverse that.

For scripts and API consumers that still need Basic Auth, WebDAV, FTP or XML-RPC access: use a dedicated service account with enable_two_factor_authentication left false, combined with a source-IP restriction through the existing ip_addresses_whitelist control-panel setting (CIDR notation supported -- see "White-listed IP addresses or IP ranges" above), which authenticateCredentials checks before anything else. That is a real, already-shipped mechanism, not a promise.

Before deploying, confirm with the operations owners that no cron job, script or integration authenticates against this site's acl_users over Basic Auth -- no test in this package can prove the absence of an external consumer.

Implementation details

This package is beta. Comments and suggestions are welcome.

  • Plone PAS plugin, which checks if user has the two-step verification enabled for user trying to log in. If so, redirect user to a separate page (a view), where the extra credentials (Google Authenticator token) is asked for.
  • Google Authenticator token validation form view. If token is valid, definitely authenticates the user.
  • Google Authenticator setup form view, where each user scans a bar code image is shown and fills in the token generated by his Google Authenticator app. Upon successful token validation, the two-step verification is enabled for the user.
  • User record is extended with two fields:
    • enable_two_factor_authentication (bool): Indicates whether user has enabled the two-step verification for his profile.
    • two_factor_authentication_secret (str): Users' secret key to be used for generating the bar code image. Filled in automatically when user enables the two-step verification.
  • Google Authenticator disable view, on which user can disable the two-step verification for his account.
  • This package ships no override of Plone's login form and no copy of Plone's overlay script (popupforms.js). Plone 4.3's own login overlay is used unmodified; TokenForm renders the id="login_form" attribute that overlay's form selector binds its ajax fetch on, so the token step served at @@google-authenticator-token loads inside the same overlay the stock login form uses.
  • Because of this, the package registers only resources under its own ++resource++imio.googleauthenticator/ prefix and never unregisters a resource it does not own. Installing it alongside another add-on that repositions a stock Plone resource -- imio.dms.mail does exactly that, for popupforms.js -- cannot break that add-on, regardless of which package's GenericSetup profile imports first.

Documentation

See the documentation at:

Troubleshooting

If you're quick enough to enter the tokens generated by GoogleAuthenticator app before they expire (remaining time is clearly indicated in the GoogleAuthenticator app) but still get the "Invalid token or token expired" message, make sure your timezone settings on the server are accurate.

Support

For feature requests or bugs, open an issue. For questions, send us an email to info@gw20e.com.

License

GPL 2.0

Authors & copyright

Copyright (C) 2014 Goldmund, Wyldebeast & Wunderliebe.

Authors listed in alphabetic order (by name):

  • Artur Barseghyan
  • Kim Chee Leong
  • Pawel Lewicki
  • Peter Uittenbroek

TODOs and Roadmap

See TODOS.rst file for the list of TODOs.

About

Two-step verification for Plone 4 using the Google Authenticator app.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages