====== IdP-Authn-Plugin fudiscr ======
===== General =====
The plugin can be used as an additional factor for Multi-Factor Authentication (MFA) by the [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/overview|Shibboleth Identity Provider (IdP)]].
It supports token-based authentication and fundamentally follows the Challenge-Response pattern.
A username is determined as a basis from one of the previous factors in the MFA sequence.
A common case is a preceding authentication with username and password against LDAP using the authn/Password-Flow.
The plugin itself has no functions for token management.
It also contains no special algorithms for token validation.
A token system can be connected via a generic ChallengeResponseClientInterface.
The plugin already includes an implementation of this interface for [[https://edumfa.io/|eduMFA]] and [[https://www.privacyidea.org/|privacyIDEA]].
Additionally, a rudimentary mockup implementation for testing is included.
An external implementation for [[https://www.fortinet.com/de/products/identity-access-management/fortiauthenticator|Fortinet]] also already exists.
The primary development work was carried out by Steffen Hofmann from the [[https://www.fu-berlin.de|Free University of Berlin]].
He is an employee of the Computing Center [[https://www.zedat.fu-berlin.de|(FUB-IT)]] and responsible for the Identity Management area. The Identity Management System of the Free University of Berlin is called FUDIS (FU Directory and Identity Service). For this reason, the abbreviation fudiscr is used in configuration parameters for the Challenge-Response-Plugin.
With versions 1.4.0/2.0.0, a collaboration in the area of [[https://fidoalliance.org/passkeys/|Passkeys]] with the [[https://www.hm.edu|Munich University of Applied Sciences]] was established. From these versions onward, the plugin contains an additional authentication method called fudispasskeys. Instead of a two-step authentication using e.g., username/password and token validation with fudiscr, a single authentication with [[https://fidoalliance.org/passkeys/|Passkeys]] can be performed.
===== Versions for Shibboleth Identity Provider v4 =====
End of life since 09/01/2024
^ Plugin Version ^ IdP Version ^ eduMFA Version ^ privacyIDEA Version ^ Support Level ^
| 1.0.0 | min. 4.1.2 and < 4.3.0 | N/A | min. 3.7 | no longer use |
| 1.1.0 | min. 4.1.2 and < 4.3.0 | N/A | min. 3.7 | no longer use |
| 1.1.1 | min. 4.1.2 and < 4.3.0 | N/A | min. 3.7 | no longer use |
| 1.2.0 | min. 4.1.2 and < 4.3.0 | N/A | min. 3.7 | no longer use |
| 1.3.0 | min. 4.3.0 and < 5.0.0 | N/A | min. 3.7 | no longer use |
| 1.4.0 | min. 4.3.0 and < 5.0.0 | min. 2.0.0 | min. 3.9.2 | no longer use |
===== Versions for Shibboleth Identity Provider v5 =====
^ Plugin Version ^ IdP Version ^ eduMFA Version ^ privacyIDEA Version ^ Support Level ^
| 2.0.0 | min. 5.0.0 | min. 2.0.0 | min. 3.9.2 | no longer use |
| 2.1.0 | min. 5.1.0 | min. 2.0.0 | min. 3.9.2 | current |
===== Supported Token Methods from eduMFA and privacyIDEA =====
Currently, the following token methods from eduMFA ([[https://edumfa.readthedocs.io/en/latest/tokens/tokentypes.html|Token types in eduMFA]]) and privacyIDEA ([[https://privacyidea.readthedocs.io/en/latest/tokens/tokentypes.html|Token types in privacyIDEA]]) are supported:
^ Token Method ^ Type Designation in Plugin ^ min. Plugin Version ^ Limitations / Notes ^
| Email | mail | 1.0.0 | |
| HOTP Token | hotp | 1.0.0 | |
| Indexed Secret Token | indexed_secret | 1.0.0 | |
| mOTP Token | motp | 1.0.0 | |
| Paper Token (PPR) | indexed_tan | 1.0.0 | |
| Questionnaire Token | question | 1.0.0 | Only one answer may be required by policy. |
| Registration | registration_code | 1.3.0 | |
| Remote | //referenced token type// | 1.3.0 | Only referenced tokens of type //Email//, //HOTP Token//, //mOTP Token//, //Paper Token (PPR)//, //Registration//, //SMS Token//, //TAN Token, TOTP//, //Yubico// and //Yubikey// are supported. |
| SMS Token | sms | 1.0.0 | |
| Spass | simple_pass_token | 1.4.0 / 2.0.0 | |
| TAN Token | tan | 1.0.0 | |
| TOTP | totp | 1.0.0 | |
| WebAuthn | web_authn | 1.2.0 | |
| Yubico | yubico_otp | 1.3.0 | |
| Yubikey | yubikey_aes | 1.3.0 | |
===== Preliminary Work in the Token Management System =====
==== eduMFA ====
see [[de:shibidp:plugin-fudiscr:edumfa_setup|Installation and Configuration of eduMFA]]
==== privacyIDEA ====
see [[de:shibidp:plugin-fudiscr:privacyidea_setup|Installation and Configuration of privacyIDEA]]
===== Installation =====
==== Activation of MFA Module ====
The fudiscr plugin requires the MFA module. This should be activated as follows:
%{idp.home}/bin/module.sh -e idp.authn.MFA
With the MFA module, the configuration file ''%{idp.home}/conf/authn/mfa-authn-config.xml'' is also stored.
(see also [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration|Shibboleth-IdP MultiFactorAuthnConfiguration]])
\\
==== Installation of fudiscr Plugin ====
The installation and updates are performed via the Shibboleth Plugin mechanism (see official documentation [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199500688/PluginInstallation|PluginInstallation]])
**Attention:** The Shibboleth Identity Provider automatically performs a restart after a plugin installation.
The plugin is installed and activated with the following command:
%{idp.home}/bin/plugin.sh -i https://identity.fu-berlin.de/downloads/shibboleth/idp/plugins/authn/fudiscr/current/fudis-shibboleth-idp-plugin-authn-fudiscr-current.tar.gz
Future updates will be automatically installed with the following command:
%{idp.home}/bin/plugin.sh -u de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr
For a fully automatic installation, the PGP keys can be downloaded in advance and specified during installation:
wget -O %{idp.home}/PGP_KEYS_FUDIS https://identity.fu-berlin.de/downloads/shibboleth/idp/plugins/PGP_KEYS
%{idp.home}/bin/plugin.sh -i https://identity.fu-berlin.de/downloads/shibboleth/idp/plugins/authn/fudiscr/current/fudis-shibboleth-idp-plugin-authn-fudiscr-current.tar.gz --truststore %{idp.home}/PGP_KEYS_FUDIS
\\
===== MFA Configuration =====
(For the REFEDS Multi-Factor Authentication Profile, see [[de:aai:refeds_authn_profiles_idp|REFEDS AuthN Profiles - Information for Identity Providers]])
\\
The following settings need to be made in ''%{idp.home}/conf/authn/authn.properties'':
idp.authn.flows = MFA
idp.authn.fudiscr.supportedPrincipals= \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR
idp.authn.MFA.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR
''idp.authn.MFA.supportedPrincipals'' is already activated by default.
Only the [[https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf|AuthenticationContextClass]] ''saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR'' is added.
Instead of ''saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR'', another [[https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf|AuthenticationContextClass]]
or additional ones can be entered under ''idp.authn.fudiscr.supportedPrincipals'' AND ''idp.authn.MFA.supportedPrincipals''.
It is recommended to define at least one additional class through which multi-factor authentication can be explicitly triggered.
However, there is no obligation, as multi-factor authentication can also be triggered through other mechanisms.
The sequence of individual factors is controlled via the transitions in ''%{idp.home}/conf/authn/mfa-authn-config.xml''.
Different mechanisms can be used here to formulate the conditions for transitions to the next factors.
You can find the official documentation here:
[[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration|MultiFactorAuthnConfiguration]]
==== MFA Configuration Examples ====
Below are three examples of MFA configuration.
**Example 1:** After authentication with username and password, token-based authentication with fudiscr always follows.
**Example 2:** After authentication with username and password, token-based authentication follows if the AuthenticationContextClass required by the ServiceProvider is insufficient.
**Example 3:** After authentication with username and password, token-based authentication follows if the AuthenticationContextClass required by the ServiceProvider is insufficient or if the user belongs to the employee group according to eduPersonAffiliation.
**Example 4:** After authentication with username and password, token-based authentication follows if the AuthenticationContextClass required by the ServiceProvider is insufficient or if the user already has at least one token. The token status is not checked. A blocked token would, for example, cause the ''fudiscr.UserHasTokenPredicate'' to return ''true''. This predicate was introduced specifically for rollout scenarios.
\\
**Example 5:** Optional authn/Password and authn/SPNEGO as first factor -> [[de:aai:mfa_mit_passwd_spnego_first|here]].
\\
=== Reuse Condition ===
A //Reuse Condition// can generally be defined for authentication procedures in the Shibboleth Identity Provider.
Usually, the default value is ''shibboleth.Conditions.TRUE''.
Thus, the condition is always fulfilled.
Custom functions can also be defined as //Reuse Conditions//.
Special rules apply to multi-factor authentication. A description can be found in the official documentation under
[[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration|MultiFactorAuthnConfiguration]].
With the following configuration in ''%{idp.home}/conf/authn/authn.properties'', you achieve that username and password are only requested once within a single sign-on session, but token-based authentication with fudiscr is required for each Service Provider authentication.
idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE
idp.authn.Password.reuseCondition=shibboleth.Conditions.TRUE
idp.authn.fudiscr.reuseCondition=shibboleth.Conditions.FALSE
Accordingly, the MFA authentication result must not be reused, but within the multi-factor authentication, the existing successful result of the username-password authentication (//Password//) may be used, while the result of //fudiscr// may not.
It is recommended to set ''idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE'' so that the logic in ''./conf/authn/mfa-authn-config.xml'' is executed for each authentication request. In the reuse case, for example, with an existing SSO session, it is not checked whether a user belongs to a specific affiliation.
Special caution is required when using ''mfaCtx.isAcceptable()'' in connection with Reuse Conditions. This function does not consider Reuse Conditions. If a requested [[https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf|AuthenticationContextClass]] has been previously fulfilled, the function returns ''true''.
...
...
The following example extends the previous example and forces the call of the second factor with fudiscr when it is requested via [[https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf|AuthenticationContextClass]] 'urn:de:zedat:fudis:SAML:2.0:ac:classes:CR'.
...
...
When 'ForceAuthn' is requested via SAML, the function ''mfaCtx.isAcceptable()'' always returns ''false''.
\\
===== AuthenticationFlowDescriptor for fudiscr =====
Each authentication method in the Shibboleth Identity Provider is based on an abstract Flow with an AuthenticationFlowDescriptor, see [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505085/AuthenticationConfiguration|AuthenticationConfiguration]].
The properties of the AuthenticationFlowDescriptor are usually set for all flows in ''%{idp.home}/conf/authn/authn.properties''. As a template, these are commented out in ''%{idp.home}/conf/authn/fudiscr.properties'' to be provided after plugin installation:
#idp.authn.fudiscr.order=1000
#idp.authn.fudiscr.nonBrowserSupported=true
#idp.authn.fudiscr.passiveAuthenticationSupported=true
#idp.authn.fudiscr.forcedAuthenticationSupported=true
#idp.authn.fudiscr.proxyRestrictionsEnforced=%{idp.authn.fudiscr.forcedAuthenticationSupported:true}
#idp.authn.fudiscr.proxyScopingEnforced=false
#idp.authn.fudiscr.discoveryRequired=false
#idp.authn.fudiscr.lifetime=%{idp.authn.defaultLifetime:PT1H}
#idp.authn.fudiscr.inactivityTimeout=%{idp.authn.defaultTimeout:PT30M}
#idp.authn.fudiscr.reuseCondition=shibboleth.Conditions.TRUE
#idp.authn.fudiscr.activationCondition=shibboleth.Conditions.TRUE
#idp.authn.fudiscr.subjectDecorator=
#idp.authn.fudiscr.errorMessageFunction = DefaultPasswordErrorFunction
#idp.authn.fudiscr.genericMessageID = authn
#idp.authn.fudiscr.addDefaultPrincipals=true
#idp.authn.fudiscr.trimUsernamePrincipal=true
#idp.authn.fudiscr.lowercaseUsernamePrincipal=false
#idp.authn.fudiscr.uppercaseUsernamePrincipal=false
#idp.authn.fudiscr.supportedPrincipals= \
# saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR
'idp.authn.fudiscr.supportedPrincipals' was already introduced in https://doku.tid.dfn.de/de:shibidp:plugin-fudiscr#mfa-konfiguration.
\\
===== fudiscr Configuration =====
==== Note for older IdP configurations ====
If you maintain an IdP configuration from before version 4.1.0, please ensure that the file ''%{idp.home}/conf/idp.properties'' contains the following line at the beginning so that all ''.properties'' files below the ''%{idp.home}/conf'' folder (such as ''%{idp.home}/conf/fudiscr.properties'') are automatically read.
idp.searchForProperties=true
==== Configuration files ====
With the installation of the fudiscr plugin, the configuration file ''%{idp.home}/conf/authn/fudiscr.properties'' is created. Changes in this file usually only take effect after restarting the Identity Provider.
Additionally, the configuration file ''%{idp.home}/conf/authn/fudiscr.xml'' is created. The configuration beans contained in this file are managed by the [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199507931/ReloadableServices|ReloadableService]] mechanism of the Shibboleth Identity Provider (see also [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199507931/ReloadableServices|ReloadableServices]]).
The service can be reloaded as follows:
%{idp.home}/bin/reload-service.sh -id fudiscr.ConfigurationBeansService
Alternatively, the reload can be triggered by directly calling the URL and avoids errors from the wrapper in ''reload-service.sh''.
GET http(s)://[idp-base-url]/idp/profile/admin/reload-service?id=fudiscr.ConfigurationBeansService
Similar to the parameters for other [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199507931/ReloadableServices|ReloadableServices]] from the Shibboleth Identity Provider, the following exist for the fudiscr plugin:
fudiscr.service.failFast=false
fudiscr.service.checkInterval=PT0S
fudiscr.service.resources=fudiscr.ConfigurationResources
If configuration changes in ''%{idp.home}/conf/authn/fudiscr.xml'' should be loaded automatically, set the check interval with ''fudiscr.service.checkInterval''. By default (PT0S), regular checking of the timestamp change of ''%{idp.home}/conf/authn/fudiscr.xml'' is disabled.
==== General Configuration Options ====
The following general configuration options can be set via the configuration file ''%{idp.home}/conf/authn/fudiscr.properties''.
^Option^Default Value^Description^
|''fudiscr.challengeResponseClient''|''EduMfaChallengeResponseClient''|This option determines which token backend should be addressed. Since version 2.1.0, the default value is ''EduMfaChallengeResponseClient'', previously ''PrivacyIdeaChallengeResponseClient''. There is also the possibility to use a ''PrivacyIdeaChallengeResponseClient'' that works without a backend and writes all information as DEBUG messages to the log files when starting the IdP.|
|''fudiscr.default_users_realm''|//empty//|If no realm can be determined from the username, this option can be used to set a realm that will be added when missing in the fudiscr plugin. In most cases, no realm is needed, so by default no realm is added.|
|''fudiscr.filter_tokens.id_exclude_regex''|//empty//|This option can be used to exclude tokens based on their ID/serial number for use in the fudiscr plugin. For example, //^HOTP.*$// ignores all HOTP tokens that were created as software tokens in privacyIDEA and received a serial number starting with HOTP. By default, the filter is not used. The regular expression is not defined.|
|''fudiscr.filter_tokens.type_exclude_regex''|//empty//|This option can be used to exclude token types for use in the fudiscr plugin. For example, //^totp$// ignores all TOTP tokens. By default, the filter is not used. The regular expression is not defined.|
|''fudiscr.on_failed_restart_completely_mfa''|''shibboleth.Conditions.FALSE''|This [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether a restart of authentication from the fudiscr plugin only restarts the //fudiscr// factor in the MFA flow (default) or restarts the entire authentication. If the [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] returns ''TRUE'', the results of previous factors are reset. This means username and password, among others, will be requested again.|
|''fudiscr.max_retries''|''10''|Number of failed attempts for validation. When this value is reached, validation ends unsuccessfully. This value is independent of the allowed failed attempts in privacyIDEA. If only the error counters from privacyIDEA should take effect, it is recommended to increase the value for ''fudiscr.max_retries'' accordingly (> max. failcounter in privacyIDEA).|
|''fudiscr.restart_allowed.max_retries_exceeded''|''shibboleth.Conditions.TRUE''|This [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether a restart of authentication in the fudiscr plugin is allowed when ''fudiscr.max_retries'' is reached.|
|''fudiscr.restart_allowed.selected_tokens_not_available''|''shibboleth.Conditions.TRUE''|This [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether a restart of authentication in the fudiscr plugin is allowed when the tokens selected by users are no longer available (e.g., due to being blocked in privacyIDEA).|
|''fudiscr.restart_allowed.within_response_page''|''shibboleth.Conditions.TRUE''|This [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether restarting fudiscr authentication is allowed via the response input page (OTP, TAN, etc.).||
|''fudiscr.result_idp_attribute_principal_attribute_name''|//empty//|If an attribute name is specified with this option, an [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199503198/PrincipalNameAttributeDefinition|IdpAttributePrincipal]] with this attribute name is added to the successful authentication result of the fudiscr plugin. The attribute value contains the ID/serial number of the token used for authentication.|
|''fudiscr.result_idp_attribute_principal_attribute_name_for_token_type''|//empty//|If an attribute name is specified with this option, an [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199503198/PrincipalNameAttributeDefinition|IdpAttributePrincipal]] with this attribute name is added to the successful authentication result of the fudiscr plugin. The attribute value contains the token type of the token used for authentication.|
|''fudiscr.result_with_realm_in_username_principal''|''shibboleth.Conditions.FALSE''|When a [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505212/AttributePostLoginC14NConfiguration|UsernamePrincipal]] is generated with ''fudiscr.result_with_username_principal'', this [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether the realm is included. ''TRUE'' (default) generates e.g., //dummy@fu-berlin.de//, ''FALSE'' only generates //dummy// as username.|
|''fudiscr.result_with_username_principal''|''shibboleth.Conditions.TRUE''|This [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199508946/PredefinedBeans|Predicate]] determines whether a [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505212/AttributePostLoginC14NConfiguration|UsernamePrincipal]] is added to the Subject after successful authentication. By default, the authentication result contains a [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505212/AttributePostLoginC14NConfiguration|UsernamePrincipal]] after successful authentication with the fudiscr plugin.|
|''fudiscr.username_realm_split_regex''|''@''|A username is generated from a previous factor during authentication. This can also contain a realm, e.g., //dummy@fu-berlin.de//. This option sets the separator used to split the username and realm. The default value is ''@''|
|''fudiscr.user_token_selection''|''none''|This option determines whether tokens can be selected by users. Possible values are: ''none''=no selection, ''singleToken''=selection of a single token (by ID/serial number), ''multipleToken''=selection of multiple tokens (by serial number), ''singleTokenTypeGroup''=selection of a single token type (e.g., totp), ''multipleTokenTypeGroup''=selection of multiple token types. By default (''none''), a challenge is generated for all active tokens.|
==== Configuration Options for eduMFA/privacyIDEA ====
=== eduMFA ===
See [[en:shibidp:plugin-fudiscr:edumfa_setup|Installation and Configuration of eduMFA]]
=== privacyIDEA ===
See [[en:shibidp:plugin-fudiscr:privacyidea_setup|Installation and Configuration of privacyIDEA]]
\\
==== Token Filtering ====
Tokens can be filtered based on properties such as token ID/serial number, status, type, and others. Additionally, properties from SAML or OIDC requests can be used for filtering. These include, for example, the entityID of the Service Provider, the Authentication Context Class, or the user's IP address.
=== General Filtering via Regular Expressions ===
Generally, tokens can be excluded based on their type or ID/serial number using the (above-mentioned) configuration options ''fudiscr.filter_tokens.type_exclude_regex'' and ''fudiscr.filter_tokens.id_exclude_regex'' in ''%{idp.home}/conf/authn/fudiscr.properties''.
fudiscr.filter_tokens.type_exclude_regex=^(sms|web_authn)$
In this example, all SMS and WebAuthn tokens are filtered out.
fudiscr.filter_tokens.id_exclude_regex=^(OATH|TOTP).+$
In this example, all tokens with the prefix OATH or TOTP in their serial number are filtered out. This affects all HOTP and TOTP tokens in eduMFA and privacyIDEA.
=== Filtering with Advanced Exclusion Conditions ===
Advanced exclusion conditions can be defined in ''%{idp.home}/conf/authn/fudiscr.xml''. The conditions are combined in the list ''fudiscr.ExcludeTokenPredicates'' and processed sequentially.
This example shows how the exclusion list is generally defined.
The exclusion conditions can be formulated as //Scripted// or using //Regular Expressions// related to token properties.
\\
\\
== Scripted Example ==
== Example with Regular Expression ==
In this example, tokens are filtered based on a token property. In eduMFA and privacyIDEA, active tokens can exist that require initial validation before they can be effectively used. For example, it should be enforced that users scan the QR code with the authenticator when setting up a TOTP token and confirm this process with an OTP. Until confirmation, the tokens have the property //rollout_state// with the values //verify// or //clientwait//. The two predicates used in the example can also be represented with one predicate and the regular expression //^(clientwait|verify)$//. The example is only meant to illustrate the list property of ''fudiscr.ExcludeTokenPredicates''.
(The constructor parameter ''c:negateResult="true"'' can be used to negate the result of an //ExcludeTokenPredicate//.)
\\
\\
==== Function fudiscr.UserHasTokenPredicate ====
The bean 'fudiscr.UserHasTokenPredicate' provides a function to determine whether users have generally been set up for the second factor or for a token procedure. This function can be particularly useful during the rollout phase. However, the decision whether users are activated for a second factor can also be controlled through groups in an LDAP directory service.
This function also offers the previously mentioned filtering options via beans in '%{idp.home}/conf/authn/fudiscr.xml'. The filter list used has the id 'fudiscr.UserHasTokenExcludeTokenPredicates'. If the filter lists 'fudiscr.ExcludeTokenPredicates' and 'fudiscr.UserHasTokenExcludeTokenPredicates' should be identical, an alias can be defined for 'fudiscr.UserHasTokenExcludeTokenPredicates':
\\
==== Preselected Tokens ====
Equivalent to the filters 'fudiscr.ExcludeTokenPredicates' and 'fudiscr.UserHasTokenExcludeTokenPredicates', a filter can be defined that filters out a user's preferred tokens. If the exclude filter list 'fudiscr.PreferredExcludeTokenPredicates' is defined, only the preferred tokens will be used initially. If the filtering results in all tokens being excluded, the filtering is reset. If the filtering reduces the initial list, users will be offered a button on the token selection page and the response input page to access the complete token selection. If selection by users is no longer necessary after filtering with 'fudiscr.PreferredExcludeTokenPredicates', the challenge is generated immediately. For example, if filtering results in only //TOTP// tokens remaining and 'fudiscr.user_token_selection=singleTokenTypeGroup' is configured, a challenge for the //TOTP// tokens is immediately triggered and the response input page is displayed. However, users will still have the option to select other token methods as mentioned before.
=== Example Preferred ===
This example filters out all tokens that do not have the token info attribute 'fudis_preferred' with the value 'true' (case-insensitive) and are not of type 'totp'. 'p:defaultValueForMissingProperty="no"' causes the //RegexExcludeTokenPropertiesPredicate// to return //no// when a token (info) attribute is missing. Please note that 'c:negateResult="true"' turns an exclude into an include. In this example, unlike the previous filter examples, the beans are defined directly in the list and not as references.
\\
\\
==== Realm-Transformation ====
It can happen that users are distributed across multiple data sources in Identity Management. For example, students and employees might be managed in two separate LDAP servers. This in turn means that two realms must be created in eduMFA or privacyIDEA, and these realms must be distinguished in API requests. For the User object in the fudiscr plugin, the following logic is followed to determine the realm:
- Using the Java class 'net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy', the username is first determined from the previous authentication results according to Shibboleth standard.
- Using 'fudiscr.username_realm_split_regex', an attempt is made to separate the realm as a separate attribute from the username.
- If a list 'fudiscr.UserRealmTransformationStrategies' with realm transformation strategies is defined in '%{idp.home}/conf/authn/fudiscr.xml', these strategies are processed in the listed order.
- If no realm exists yet, a default realm is assigned if it has been defined with 'fudiscr.default_users_realm'.
The realm determined in this way is used by all classes in the fudiscr plugin that process a User object.
If the realm in the User object from the fudiscr plugin is not defined (//null//), then a realm for the API request can be added with 'fudiscr.edumfa.default_realm' against eduMFA or with 'fudiscr.privacyidea.default_realm' against privacyIDEA. However, the realm added in this way is not incorporated into the general User object from the fudiscr plugin!
=== Example UserRealmTransformationStrategies ===
In this example, it checks whether an //LdapPrincipal// exists in the active authentication result from the authentication with username and password (//authn/Password//) and whether this //LdapPrincipal// contains the attribute //realm//. The first value from //realm// is adopted as the realm. Subsequently, a //Scripted// strategy is applied.
\\
\\
==== Subject Customizers ====
The [[https://refeds.org/assurance|REFEDS Assurance Framework]] defines, among other things, profiles for multi-factor authentication. Generally, Service Providers can require properties via Authentication Context Class that only apply to specific tokens. Filtering of matching tokens is possible with ''fudiscr.ExcludeTokenPredicates'', but this filtering is not helpful when determining whether an authentication result in an SSO session can be used for another Service Provider.
Authentication Context Classes are mapped as Principals in the Shibboleth Identity Provider. Each authentication method has the properties ''idp.authn..supportedPrincipals'' and ''idp.authn..addDefaultPrincipals'' (Default: true). The ''idp.authn..supportedPrincipals'' determine whether the respective authentication method matches the requested Authentication Context Class. With ''idp.authn..addDefaultPrincipals=true'', the ''idp.authn..supportedPrincipals'' are added to the authentication result.
The list bean ''fudiscr.SubjectCustomizers'' allows modifying the Principals for the Authentication Context Class.
Additionally, two functions are offered as Reuse-Conditions.
''fudiscr.FlowSpecificRequestedPrincipalsReuseCondition'' requires the single (second) factor again if the existing Authentication Context Classes are insufficient. ''fudiscr.GeneralRequestedPrincipalsReuseCondition'' requires all factors if the existing Authentication Context Classes are insufficient.
The following example shows the interaction of the configuration elements.
=== Example SubjectCustomizers with supportedPrincipals and ReuseConditions ===
In ''%{idp.home}/conf/authn/authn.properties'', the ''supportedPrincipals'' are defined for the authentication procedure ''fudiscr'':
idp.authn.fudiscr.supportedPrincipals= \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:HIGH
For the MFA flow, all supportedPrincipals must be defined that are covered by the individual factors used in total. If the first factor consists of the usual query for user name and password (flowid=password), then the entry in ''%{idp.home}/conf/authn/authn.properties'' should look like this:
idp.authn.MFA.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW, \
saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:HIGH
A subject customizer is now defined in ''%{idp.home}/conf/authn/fudiscr.xml'', which always adds the authentication context class ''saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR'' for ''fudiscr''. Depending on the properties of the successfully used token, either only ''saml2/urn:de:zedat:fudis:SAML:2. 0:ac:classes:CR:IAP:LOW'' or ''saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW'' and ''saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:HIGH'' are added.
The two variants for the reuse conditions can now be set in ''%{idp.home}/conf/authn/authn.properties''.
Variant 1: Only the second factor is required again if the previous Authentication Context Classes are not sufficient.
idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE
idp.authn.Password.reuseCondition=shibboleth.Conditions.TRUE
idp.authn.fudiscr.reuseCondition=fudiscr.FlowSpecificRequestedPrincipalsReuseCondition
Variant 2: All factors are required again if the previous Authentication Context Classes are not sufficient.
idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE
idp.authn.Password.reuseCondition=fudiscr.GeneralRequestedPrincipalsReuseCondition
idp.authn.fudiscr.reuseCondition=fudiscr.GeneralRequestedPrincipalsReuseCondition
\\
==== Subject Canonicalization ====
As an additional feature, a flow based on ''de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.domain.ChallengeResponseTokenIdPrincipal'' can be activated for the [[https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199512211/SubjectCanonicalization|SubjectCanonicalization]] process in the Shibboleth Identity Provider. This additional Principal generated by the fudiscr-Plugin contains the ID/serial number of the successfully used token.
To activate the flow, the reference '''' must be added to the list ''shibboleth.PostLoginSubjectCanonicalizationFlows'' in the configuration file ''%{idp.home}/conf/c14n/subject-c14n.xml''.
In addition, transformation options can be specified as for other principals, e.g. in ''%{idp.home}/conf/c14n/subject-c14n.properties'':
idp.c14n.fudiscr.lowercase=false
idp.c14n.fudiscr.uppercase=false
idp.c14n.fudiscr.trim=true
\\
===== fudispasskeys Configuration (eduMFA only) =====
Fudispasskeys provides an authentication method that integrates into the login page for username and password authentication and enables direct authentication with a security key or passkeys. The configuration is done in the following steps:
\\
=== Use of the EduMfaChallengeResponseClient ===
The procedure is only supported by the ''EduMfaChallengeResponseClient'' in conjunction with eduMFA. The following property must therefore be set in ''%{idp.home}/conf/authn/fudiscr.properties'':
fudiscr.challengeResponseClient=EduMfaChallengeResponseClient
=== Definition of the Relying Party Id for WebAuthn/Passkeys ===
In addition, the Relying Party Id for WebAuthn/Passkeys must be set in ''%{idp.home}/conf/authn/fudiscr.properties'' in accordance with the enrollment policy [[https://edumfa.readthedocs.io/en/v2.2.0/policies/enrollment.html#policy-webauthn-enroll-relying-party-id|webauthn_relying_party_id]] from eduMFA:
fudispasskeys.relying_party_id=
=== Definition/extension of the shibboleth.CustomViewContext bean ===
To be able to access special functions in the views, the bean ''shibboleth.CustomViewContext'' must be defined or extended in ''%{idp.home}/conf/global.xml''. This is a list that requires at least one entry for the ''fudispasskeys.ChallengeGenerationStrategy'':
=== Adaptation of authn-events-flow.xml ===
The following extensions must be made in ''%{idp.home}/conf/authn/authn-events-flow.xml'':
=== Adaptation of mfa-authn-config.xml ===
In the existing ''%{idp.home}/conf/authn/mfa-authn-config.xml'', the ''shibboleth.authn.MFA.TransitionMap'' must be extended:
=== Adaptation of login.vm ===
In ''%{idp.home}/views/login.vm'' the following line must be inserted outside the login form:
#parse("fudispasskeys/login-passkeys.vm")
In addition, the attribute ''autocomplete'' with the value ''webauthn'' can be inserted or added to the input fields for user name and password, e.g.
autocomplete="webauthn"
This means that additional passkey authentication is offered when these input fields are selected.
=== Policies in eduMFA ===
According to [[https://doku.tid.dfn.de/de:shibidp:plugin-fudiscr:edumfa_setup#richtlinien_fuer_fudispasskeys|eduMFA guidelines for fudispasskeys]], the following authentication and enrollment policies must be set in eduMFA:
for the scope authentication:
webauthn_user_verification_requirement = required
webauthn_usernameless_authn = True
for scope enrollment:
webauthn_relying_party_id =
webauthn_relying_party_name =
webauthn_resident_key = required
webauthn_user_verification_requirement = required
==== AuthenticationFlowDescriptor für fudispasskeys ====
Analog zum [[https://doku.tid.dfn.de/de:shibidp:plugin-fudiscr#authenticationflowdescriptor_fuer_fudiscr|AuthenticationFlowDescriptor für fudiscr]] existiert auch ein AuthenticationFlowDescriptor für fudispasskeys:
#idp.authn.fudispasskeys.order=1000
#idp.authn.fudispasskeys.nonBrowserSupported=true
#idp.authn.fudispasskeys.passiveAuthenticationSupported=true
#idp.authn.fudispasskeys.forcedAuthenticationSupported=true
#idp.authn.fudispasskeys.proxyRestrictionsEnforced=%{idp.authn.fudispasskeys.forcedAuthenticationSupported:true}
#idp.authn.fudispasskeys.proxyScopingEnforced=false
#idp.authn.fudispasskeys.discoveryRequired=false
#idp.authn.fudispasskeys.lifetime=%{idp.authn.defaultLifetime:PT1H}
#idp.authn.fudispasskeys.inactivityTimeout=%{idp.authn.defaultTimeout:PT30M}
#idp.authn.fudispasskeys.reuseCondition=shibboleth.Conditions.TRUE
#idp.authn.fudispasskeys.activationCondition=shibboleth.Conditions.TRUE
#idp.authn.fudispasskeys.subjectDecorator=
#idp.authn.fudispasskeys.errorMessageFunction = DefaultPasswordErrorFunction
#idp.authn.fudispasskeys.genericMessageID = authn
#idp.authn.fudispasskeys.addDefaultPrincipals=true
#idp.authn.fudispasskeys.trimUsernamePrincipal=true
#idp.authn.fudispasskeys.lowercaseUsernamePrincipal=false
#idp.authn.fudispasskeys.uppercaseUsernamePrincipal=false
#idp.authn.fudispasskeys.supportedPrincipals= \
# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
# saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
# saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR
===== Logging =====
To obtain detailed logging information from the fudiscr-Plugin, the following line can be added to ''%{idp.home}/conf/logback.xml'':
If all API requests against privacyIDEA are to be logged separately, the following additional configuration is suggested:
${idp.logfiles}/idp-fudis-request.log
${idp.logfiles}/idp-request-%d{yyyy-MM-dd}.log.gz
${idp.loghistory}
UTF-8
%msg%n
\\
===== Extension for Fortinet (FortiAuthenticator) =====
The Fortinet extension currently supports the token types //FortiToken (Mobile and Hardware)//, //SMS// and //Email//. //FIDO2 (WebAuthn)// is currently not supported by the FortiAuthenticator API.
The extension is available under the Apache 2.0 License and is provided by DAASI International GmbH [[https://gitlab.daasi.de/shibboleth-identity-provider/shibboleth-idp-plugin-authn-fudiscr-fortinetclient|here]]. DAASI also offers software maintenance contracts for the extension. Please send inquiries to [[info@daasi.de|info@daasi.de]].
\\
===== Additional Materials =====
* Flow: [[https://identity.fu-berlin.de/downloads/shibboleth/idp/plugins/authn/fudiscr/doc/ChallengeResponseFlow.pdf|ChallengeResponseFlow.pdf]]
* Materials from the [[de:aai:events:ws2022|Shibboleth Workshops February 2022]]
{{tag>2FA MFA Two-Factor-Authentication Multi-Factor-Authentication}}
==== Source Code =====
Universities and other public institutions can get access to the source code upon request. Please send an email to [[fudis@fu-berlin.de|fudis@fu-berlin.de]]. Work is in progress to publish all components of the fudiscr-Plugin under the Apache 2.0 License. We ask for (continued) patience.
===== Contact =====
Please send bugs, questions, suggestions etc. via email to [[fudis@fu-berlin.de|fudis@fu-berlin.de]].