The main branch is compatible with IdP Version >=5.0.0.
- Copy the package to the server:
- Choose the archive type for your system from the release page (.zip, .tar.gz, .tar.bz2).
- Copy it with the corresponding signature data (.asc).
- Place both files somewhere in your Shibboleth server.
- Open the terminal and run:
sudo -ifor the admin rights. - Enable the MFA Module:
$idp_install_path/bin/module.sh -t idp.authn.MFA || $idp_install_path/bin/module.sh -e idp.authn.MFA. - Check if the MFA Module is activated:
$idp_install_path/bin/module.sh -l. - Install the privacyIDEA Plugin:
$idp_install_path/bin/plugin.sh -i path/to/zip/from/step/1 --noCheck
Note: The installer will automatically install and enable the privacyIDEA Module. You can check the state by repeating the step 4. If you need to enable the privacyIDEA module run:$idp_install_path/bin/module.sh -e idp.authn.privacyIDEA.** - Check if the privacyIDEA Plugin is installed correctly:
$idp_install_path/bin/plugin.sh -l
Note: If the org.privacyidea.privacyIDEA is on the list, the plugin was installed successfully!
Otherwise, re-run the command from step 5 with--verbose.
-
Update the privacyidea.properties file (
$idp_install_path/conf/authn/privacyidea.properties) by adding your own configuration data.
Updating the following parameters is required to ensure at least the very basic functionality:privacyidea.server_urlprivacyidea.verify_sslprivacyidea.authentication_flow
-
Add the privacyIDEA subflow to the MFA flow.
- Path to the MFA flow configuration file:
$idp_install_path/conf/authn/mfa-authn-config.xml. - Example of the configuration: MFA Config Example.
- Remember to activate the MFA flow.
- Path to the MFA flow configuration file:
-
Turn on the MFA Module by updating the following file:
$idp_install_path/conf/authn/authn.properties.- Example of the basic configuration: Authn Config Example.
If you want to use passkey authentication without the password module, you can adjust the mfa-authn-config.xml file to use the privacyIDEA subflow directly.
Note: This will change the authentication flow to allow passkey authentication without need for entering the username and password.
Example of the configuration: MFA Config Example.
This is also located in the privacyidea.properties file ($idp_install_path/conf/authn/privacyidea.properties).
Important Note on User Existence:
When using the plugin in Passkey or Standalone mode (where the standard authn/Password flow is skipped), the plugin asserts the identity (username) returned by the privacyIDEA server.
The plugin does not verify if this user exists in the local IdP user store (e.g., LDAP, SQL, or htpasswd).
However, if you enter username and password in these modes, it will always send these parameters to privacyIDEA for validation.
To ensure that only valid local users can log in, you should rely on the standard Shibboleth mechanisms that run after authentication:
- Attribute Resolution: Ensure your Attribute Resolver is configured to look up the user in your backend. If the user does not exist, no attributes will be resolved. You can configure the IdP to fail the request if essential attributes are missing.
- Subject Canonicalization (c14n): Configure a c14n flow that verifies the principal against your user store.
You can let users skip the privacyIDEA second factor on a trusted device.
When privacyidea.remember_me_enabled=true (and a privacyidea.api_key is configured), a "remember this device" checkbox is shown on the privacyIDEA form. If the user ticks it and authentication succeeds, privacyIDEA issues a persistent-device cookie. On later logins from that browser the plugin presents the cookie to privacyIDEA, which recognises the device so the second factor can be skipped — for as long as the server-side policy allows.
The logic lives in privacyIDEA, not in the plugin: the plugin is only the transport. This requires privacyIDEA 3.14+ with a remember_device policy (scope authentication) enabled for this client. See privacyIDEA's own documentation for the policy, validity and counter options.
How it works and why it is safe:
- API client identity. The feature is tied to an API key (
privacyidea.api_key) that identifies this plugin to privacyIDEA and gates the feature per client. The key is sent as theX-API-Keyheader; it is never placed in the cookie. An admin can revoke or rotate it server-side at any time. - Rotating token, not a bearer secret. The cookie (
pi_remember_device) is a rotatingseries:countertoken. privacyIDEA advances it on every use and returns the new value, which the plugin stores. If a cookie is cloned, the stale copy is detected on next use and privacyIDEA destroys the whole session series — cutting off attacker and legitimate device alike and forcing a fresh full login. (Rotation makes theft detectable; it is not a device-bound credential, so it does not by itself make theft impossible — see the security note below.) - Only ever the second factor. Recognition can, at most, skip the privacyIDEA step. It requires an active first-factor result for the same user in the current login (e.g. a preceding
authn/Password, or a still-valid IdP SSO session). With no active first factor the cookie is ignored and the user is challenged normally, so it is ignored in standalone / passkey-only mode (where privacyIDEA is the only factor) — the checkbox is not shown there. - SSO / forced re-auth. As with any SSO login, when an IdP session is already active and the relying party does not request re-authentication, the first factor is satisfied by that existing session rather than re-prompted. If you need the first factor re-proven for a sensitive service, request
forceAuthn(or set an authenticationmaxAge) on that relying party: there is then no active first-factor result to reuse, so the remember-me skip does not apply and the second factor is enforced as well. - Cookie attributes. The plugin sets the cookie
HttpOnlyandSecure. ItsSameSiteattribute is governed by the IdP's global cookie policy (idp.cookie.sameSite, defaultNone), not set per-cookie by the plugin — set that property toStrict/Laxif you want to constrain it. Its lifetime is server-driven: the plugin applies theMax-AgeprivacyIDEA sends on its cookie, so the browser cookie expires exactly per the server-sideremember_devicepolicy — there is no client-side day setting. (If privacyIDEA sends noMax-Age, the plugin falls back to a 7-day persistent cookie.)
Security note. A remembered device is a deliberate, bounded relaxation of MFA, not a free one. Token rotation gives you detection of a stolen cookie, but between rotations the cookie is still a bearer token — it is not bound to the device the way a passkey is. Enable it where reducing second-factor friction is worth that trade-off, and rely on the server-side policy (max age, counters) and per-client key revocation to bound the exposure.
Configure it with privacyidea.api_key and privacyidea.remember_me_enabled — see the table below. The cookie name and lifetime are owned by privacyIDEA, so there is nothing else to set on the plugin side.
By default the privacyIDEA flow advertises no specific SAML AuthnContextClassRef. If a service provider sends a RequestedAuthnContext — or you need the IdP to assert a particular context class, e.g. the REFEDS MFA profile for eduGAIN / DFN-AAI — declare the values the flow can satisfy via idp.authn.privacyIDEA.supportedPrincipals in privacyidea.properties:
idp.authn.privacyIDEA.supportedPrincipals = https://refeds.org/profile/mfa,https://refeds.org/profile/sfa
Shibboleth then selects the privacyIDEA flow when an SP requests one of these classes and asserts the matched class back in the response. The value is comma-separated; leave it unset for no specific context class.
The privacyIDEA2 flow inherits this (and the other flow-descriptor settings — order, lifetime, reuseCondition, …) from the privacyIDEA keys by default. To give the second flow a different context class, set its own idp.authn.privacyIDEA2.supportedPrincipals in privacyidea2.properties; each idp.authn.privacyIDEA2.* key falls back to the matching idp.authn.privacyIDEA.* value.
An example of the privacyIDEA plugin configuration can be found in privacyidea.properties ($idp_install_path/conf/authn/privacyidea.properties).
The different configuration parameters are explained in the following table:
| Configuration | Explanation |
|---|---|
privacyidea.server_url |
The URL of your privacyIDEA server. This must be reachable from the Shibboleth IdP server. |
privacyidea.realm |
This realm will be appended to all requests to the privacyIDEA. Note: Drop it to use the default realm. |
privacyidea.verify_ssl |
Choose if the Shibboleth should verify the SSL certificate of the privacyIDEA. Note: Always verify the SSL certificate in a productive environment! |
privacyidea.http_timeout_ms |
HTTP timeout for all privacyIDEA requests, in milliseconds. Only digits allowed; a blank/invalid value keeps the default. Default 10000 (10s). |
privacyidea.default_message |
Use this parameter to edit the default user message. |
privacyidea.otp_field_hint |
Use this parameter to edit the default placeholder for the OTP input field. |
privacyidea.authentication_flow |
Choose one of three possible options:default - standard authentication flow,triggerChallenge - triggers all challenges beforehand using the provided service account. Required additional parameters: privacyidea.service_name, privacyidea.service_pass (see below),sendStaticPass - performs the privacyIDEA server request automatically beforehand using the provided static password. Required additional parameter: privacyidea.static_pass (see below). |
privacyidea.service_name |
The username of the service account required by the triggerchallenge config option. Note: Please make sure, that the service account has the correct rights. |
privacyidea.service_pass |
The password of your service account, which is required by the triggerchallenge config option. |
privacyidea.service_realm |
Specify a separate service account's realm if needed. Note: If the service account is located in the same realm as the users, it is sufficient to specify the realm in the privacyidea.realm parameter. |
privacyidea.static_pass |
The password which should be used in the sendStaticPass authentication flow. Note: You can also leave it empty to perform the privacyIDEA server request with an empty pass (useful in some scenarios). |
privacyidea.forward_headers |
Set the headers that should be forwarded to the privacyIDEA. Note: If some header doesn't exist or has no value, will be ignored. Note: The header names should be separated by a comma (","). |
privacyidea.otp_length |
If you want to turn on the form-auto-submit function after x number of characters are entered into the OTP input field, set the expected OTP length here. Note: Only digits as the parameter's value are allowed here. |
privacyidea.polling_interval |
Decide after how many seconds the form should be reloaded, to check if the push token was confirmed. Default is 2. |
privacyidea.polling_in_browser |
Enable this to do the polling for accepted push requests in the user's browser. When enabled, the login page does not refresh to confirm the push authentication. CORS settings for privacyidea can be adjusted in etc/apache2/sites-available/privacyidea.conf. |
privacyidea.polling_in_browser_url |
If 'poll in browser' should use a deviating URL, set it here. Otherwise, the general URL will be used. |
privacyidea.disable_passkey |
Set to 'true' to disable passkey authentication. |
privacyidea.skip_first_step |
Default true. When true, the plugin's own username/password form is skipped if a prior MFA sub-flow (e.g. authn/Password) produced a fresh authentication result in the current MFA run, and that result's principal is used. If no fresh result exists (e.g. privacyIDEA is the first factor, or only a stale session principal is available), the form is displayed regardless — prefilled with the principal if one is known. Set to false to always display the form. |
privacyidea.api_key |
API key identifying this plugin to privacyIDEA (pi_<key_id>_<secret>), obtained from the privacyIDEA admin. Required for remember-me; sent as the X-API-Key header. Keep it secret (see Securing the configuration). |
privacyidea.remember_me_enabled |
Default false. Set to true (with api_key set) to show a "remember this device" checkbox on the privacyIDEA form. When checked and authentication succeeds, privacyIDEA issues a rotating pi_remember_device cookie; on later logins the plugin presents it and privacyIDEA can skip the second factor per its remember_device policy. Requires privacyIDEA 3.14+. Only applies when a preceding first factor authenticated the user — ignored in standalone mode. See the Remember Me section. |
privacyidea.debug |
Set this parameter to true to see the debug messages in the idp-process.log. |
privacyidea.properties can contain secrets — the service-account password (privacyidea.service_pass) and, for the remember-me feature, the client API key (privacyidea.api_key). Restrict it so only the IdP service account can read it, e.g. on Linux:
chown root:<idp-group> conf/authn/privacyidea.properties
chmod 640 conf/authn/privacyidea.properties
Adjust owner/group to match how your IdP runs (e.g. the Tomcat/Jetty service user). The API key is revocable/rotatable server-side in privacyIDEA (/clients/<id>/rotate) if it is ever exposed.
- Main log:
$idp_install_path/logs/idp-process.log. - Warn and error log:
$idp_install_path/logs/idp-warn.log.
To update the plugin, repeat the installation process with the new archive data.
If something goes wrong, check if some of the plugin files have their .idpnew copies,
remove the .idpnew copies, and re-run the installation process.
If you want to set up a second authentication flow (e.g. for another privacyIDEA server or realm):
- Add a new subflow called
authn/privacyIDEA2to the mfa-authn-config.xml file. Note: Obviously, you need to adjust the flow transition map to your needs. - Example of the MFA flow configuration based on IP check: MFA Config Example.
- Copy the
privacyidea.propertiesfile toprivacyidea2.properties. - Update the
privacyidea2.propertiesfile with the new configuration data. Note: Make sure to change each configuration variables names toprivacyidea2.*in theprivacyidea2.propertiesfile. - Restart the Shibboleth IdP server to apply the changes and register new auth flow.
If you want to translate the plugin, you can use the messages.properties file located in $idp_install_path/conf/authn/messages.properties.
See shibboleth documentation for more information about the translation process: Shibboleth - Translation.