IdP-Authn-Plugin fudiscr

The plugin can be used as an additional factor for Multi-Factor Authentication (MFA) by the 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 eduMFA and privacyIDEA. Additionally, a rudimentary mockup implementation for testing is included. An external implementation for Fortinet also already exists. The primary development work was carried out by Steffen Hofmann from the Free University of Berlin. He is an employee of the Computing Center (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 Passkeys with the 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 Passkeys can be performed.

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
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

Currently, the following token methods from eduMFA (Token types in eduMFA) and privacyIDEA (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

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 Shibboleth-IdP MultiFactorAuthnConfiguration)

The installation and updates are performed via the Shibboleth Plugin mechanism (see official documentation 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


(For the REFEDS Multi-Factor Authentication Profile, see REFEDS AuthN Profiles - Information for Identity Providers)
The following settings need to be made in %{idp.home}/conf/authn/authn.properties:

./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 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 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: MultiFactorAuthnConfiguration

Below are three examples of MFA configuration.

Example 1: After authentication with username and password, token-based authentication with fudiscr always follows.

./conf/authn/mfa-authn-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
 
       default-init-method="initialize"
       default-destroy-method="destroy">
 
    <!-- most parts copied from https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration -->
 
    <util:map id="shibboleth.authn.MFA.TransitionMap">
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password"/>
        </entry>
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/fudiscr"/>
        </entry>
    </util:map>
 
</beans>

Example 2: After authentication with username and password, token-based authentication follows if the AuthenticationContextClass required by the ServiceProvider is insufficient.

./conf/authn/mfa-authn-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
 
       default-init-method="initialize"
       default-destroy-method="destroy">
 
    <!-- most parts copied from https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration -->
 
    <util:map id="shibboleth.authn.MFA.TransitionMap">
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password"/>
        </entry>
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor"/>
        </entry>
    </util:map>
 
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
        <constructor-arg>
            <value>
                <![CDATA[
            nextFlow = "authn/fudiscr";
 
            authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
            mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
            if (mfaCtx.isAcceptable()) {
                nextFlow = null;
            }
 
            nextFlow;
        ]]>
            </value>
        </constructor-arg>
    </bean>
 
</beans>

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.

./conf/authn/mfa-authn-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
 
       default-init-method="initialize"
       default-destroy-method="destroy">
 
    <!-- most parts copied from https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration -->
 
    <util:map id="shibboleth.authn.MFA.TransitionMap">
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password"/>
        </entry>
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor"/>
        </entry>
    </util:map>
 
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript" p:customObject-ref="shibboleth.AttributeResolverService">
        <constructor-arg>
            <value>
                <![CDATA[
            nextFlow = "authn/fudiscr";
 
            authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
            mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
            if (mfaCtx.isAcceptable()) {
                nextFlow = null;
 
                resCtx = input.getSubcontext(
                    "net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", true);
 
                usernameLookupStrategyClass = Java.type("net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy");
                usernameLookupStrategy = new usernameLookupStrategyClass();
                resCtx.setPrincipal(usernameLookupStrategy.apply(input));
 
 
                resCtx.getRequestedIdPAttributeNames().add("eduPersonAffiliation");
                resCtx.resolveAttributes(custom);
 
                attribute = resCtx.getResolvedIdPAttributes().get("eduPersonAffiliation");
                valueType =  Java.type("net.shibboleth.idp.attribute.StringAttributeValue");
                if (attribute != null && attribute.getValues().contains(new valueType("employee"))) {
                    nextFlow = "authn/fudiscr";
                }
 
                input.removeSubcontext(resCtx);
            }
 
            nextFlow;
        ]]>
            </value>
        </constructor-arg>
    </bean>
 
</beans>

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.

./conf/authn/mfa-authn-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
 
       default-init-method="initialize"
       default-destroy-method="destroy">
 
    <util:map id="shibboleth.authn.MFA.TransitionMap">
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password"/>
        </entry>
        <entry key="authn/Password">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor"/>
        </entry>
    </util:map>
 
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript" p:customObject-ref="fudiscr.UserHasTokenPredicate">
        <constructor-arg>
            <value>
                <![CDATA[
            nextFlow = "authn/fudiscr";
 
            authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
            mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
            if (mfaCtx.isAcceptable()) {
                nextFlow = null;
 
                if (custom.test(input)) {
                    nextFlow = "authn/fudiscr";
                }
            }
 
            nextFlow;
        ]]>
            </value>
        </constructor-arg>
    </bean>
 
</beans>


Example 5: Optional authn/Password and authn/SPNEGO as first factor → 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 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.

./conf/authn/authn.properties
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.

When 'ForceAuthn' is requested via SAML, the function mfaCtx.isAcceptable() always returns false.

Each authentication method in the Shibboleth Identity Provider is based on an abstract Flow with an AuthenticationFlowDescriptor, see 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.

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

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 ReloadableService mechanism of the Shibboleth Identity Provider (see also 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 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.

The following general configuration options can be set via the configuration file %{idp.home}/conf/authn/fudiscr.properties.

OptionDefault ValueDescription
fudiscr.challengeResponseClientEduMfaChallengeResponseClientThis 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_realmemptyIf 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_regexemptyThis 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_regexemptyThis 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_mfashibboleth.Conditions.FALSEThis 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 Predicate returns TRUE, the results of previous factors are reset. This means username and password, among others, will be requested again.
fudiscr.max_retries10Number 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_exceededshibboleth.Conditions.TRUEThis 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_availableshibboleth.Conditions.TRUEThis 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_pageshibboleth.Conditions.TRUEThis Predicate determines whether restarting fudiscr authentication is allowed via the response input page (OTP, TAN, etc.).
fudiscr.result_idp_attribute_principal_attribute_nameemptyIf an attribute name is specified with this option, an 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_typeemptyIf an attribute name is specified with this option, an 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_principalshibboleth.Conditions.FALSEWhen a UsernamePrincipal is generated with fudiscr.result_with_username_principal, this 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_principalshibboleth.Conditions.TRUEThis Predicate determines whether a UsernamePrincipal is added to the Subject after successful authentication. By default, the authentication result contains a 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_selectionnoneThis 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.

eduMFA

privacyIDEA

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.

<util:list id="fudiscr.ExcludeTokenPredicates">
    <ref bean="fudiscr.ExcludeTokenPredicate1"/>
    <ref bean="fudiscr.ExcludeTokenPredicate2"/>
    <ref bean="fudiscr.ExcludeTokenPredicate3"/>
    <!-- etc. -->
</util:list>

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
<util:list id="fudiscr.ExcludeTokenPredicates">
   <ref bean="fudiscr.ScriptedExcludeTokenPredicateExample1"/>
</util:list>
 
<!-- Input1: profileRequestContext (org.opensaml.profile.context.ProfileRequestContext) -->
<!-- Input2: token (de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.domain.Token) -->
<!-- Output -> java.lang.Boolean / boolean -->
<bean id="fudiscr.ScriptedExcludeTokenPredicateExample1" parent="fudiscr.Functions.ScriptedExcludeTokenPredicate" factory-method="inlineScript">
   <constructor-arg>
      <value>
      <![CDATA[
         // default is to keep the token
         exclude = false;
 
         relyingPartyId = profileContext.getSubcontext("net.shibboleth.profile.context.RelyingPartyContext").getRelyingPartyId();
 
         // if the service provider is not 'https://portal.local/shibboleth', tokens that are of type 'registration_code' are excluded
         if (!relyingPartyId.equals("https://portal.local/shibboleth") && token.getType().toString().equalsIgnoreCase("registration_code")) {
            exclude = true;
         }
 
         exclude;
      ]]>
      </value>
   </constructor-arg>
</bean>
Example with Regular Expression
<util:list id="fudiscr.ExcludeTokenPredicates">
   <ref bean="fudiscr.RegexExcludeTokenPredicateExample1"/>
   <ref bean="fudiscr.RegexExcludeTokenPredicateExample2"/>
</util:list>
 
<bean id="fudiscr.RegexExcludeTokenPredicateExample1"  
      class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.RegexExcludeTokenPropertiesPredicate"
      c:propertyName="rollout_state"
      c:excludePattern="^verify$"/>
<bean id="fudiscr.RegexExcludeTokenPredicateExample2"  
      class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.RegexExcludeTokenPropertiesPredicate"
      c:propertyName="rollout_state"
      c:excludePattern="^clientwait$"/>

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.)

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':

<alias name="fudiscr.ExcludeTokenPredicates" alias="fudiscr.UserHasTokenExcludeTokenPredicates"/>


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

<util:list id="fudiscr.PreferredExcludeTokenPredicates">
    <bean class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.RegexExcludeTokenPropertiesPredicate"
          c:propertyName="info:fudis_preferred"
          c:excludePattern="^(?i)no$"
          p:defaultValueForMissingProperty="no"/>
    <bean class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.RegexExcludeTokenPropertiesPredicate"
          c:propertyName="type"
          c:excludePattern="^totp$"
          c:negateResult="true"/>
</util:list>

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.

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

<util:list id="fudiscr.UserRealmTransformationStrategies">
   <!-- The attribute specified under 'ldapAttributeName' must also be included in the list of 'idp.authn.LDAP.returnAttributes'. -->
   <bean class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.UserRealmFromLdapPrincipalTransformationStrategy"
         p:activeAuthenticationResultKey="authn/Password"
         p:ldapAttributeName="realm"/>
 
   <!-- For ScriptedUserRealmTransformationStrategy: -->
   <!-- Input1: profileRequestContext (org.opensaml.profile.context.ProfileRequestContext) -->
   <!-- Input2: currentUserRealm (java.lang.String) -->
   <!-- Output -> java.lang.String -->         
   <bean class="de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.function.impl.ScriptedUserRealmTransformationStrategy" factory-method="inlineScript">
      <constructor-arg>
         <value>
            <![CDATA[
            newUserRealm = null;
 
            if (currentUserRealm == null) {
               newUserRealm = "idp-users";
            }
 
            newUserRealm;
            ]]>
         </value>
      </constructor-arg>
   </bean>
</util:list>

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.

The 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.<flowid>.supportedPrincipals and idp.authn.<flowid>.addDefaultPrincipals (Default: true). The idp.authn.<flowid>.supportedPrincipals determine whether the respective authentication method matches the requested Authentication Context Class. With idp.authn.<flowid>.addDefaultPrincipals=true, the idp.authn.<flowid>.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.

<util:list id="fudiscr.SubjectCustomizers">
    <ref bean="fudiscr.ScriptedAuthenticationMethodAndContextPrincipalsTokenBasedSubjectCustomizer"/>
</util:list>
 
<bean id="fudiscr.ScriptedAuthenticationMethodAndContextPrincipalsTokenBasedSubjectCustomizer" parent="fudiscr.Functions.ScriptedAuthenticationMethodAndContextPrincipalsSubjectCustomizer" factory-method="inlineScript">
    <constructor-arg>
        <value>
            <![CDATA[
            var logger = Java.type( "org.slf4j.LoggerFactory" ).getLogger("fudiscr.ScriptedAuthenticationMethodAndContextPrincipalsSubjectCustomizer");
            logger.trace("Input 1, principals: '{}'", principals);
            logger.trace("Input 2, token: '{}'", token)
            var HashSet = Java.type("java.util.HashSet");
            var newPrincipals = new HashSet();
 
            var AuthnContextClassRefPrincipal = Java.type("net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal");
            var standardPrincipal = new AuthnContextClassRefPrincipal("urn:de:zedat:fudis:SAML:2.0:ac:classes:CR");
            newPrincipals.add(standardPrincipal);
            if (token.getProperties().get("description") != null && token.getProperties().get("description").toString().equalsIgnoreCase("IAP=high")) {
                var lowPrincipal = new AuthnContextClassRefPrincipal("urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW");
                newPrincipals.add(lowPrincipal);
                var highPrincipal = new AuthnContextClassRefPrincipal("urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:HIGH");
                newPrincipals.add(highPrincipal);
            }
 
            if (token.getProperties().get("description") != null && token.getProperties().get("description").toString().equalsIgnoreCase("IAP=low")) {
                var lowPrincipal = new AuthnContextClassRefPrincipal("urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW");
                newPrincipals.add(lowPrincipal);
            }
 
            logger.trace("Result, newPrincipals: '{}'", newPrincipals);
            newPrincipals;
            ]]>
        </value>
    </constructor-arg>
</bean>                                                

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


As an additional feature, a flow based on de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.domain.ChallengeResponseTokenIdPrincipal can be activated for the 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 <ref bean=“c14n/fudiscr” /> 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 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 webauthn_relying_party_id from eduMFA:

fudispasskeys.relying_party_id=<same value here as in the enrollment policy 'webauthn_relying_party_id' in eduMFA>

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:

<util:map id="shibboleth.CustomViewContext">
    <!-- hier die bisherigen Einträge -->
    <entry key="fudispasskeys.ChallengeGenerationStrategy" value-ref="fudispasskeys.ChallengeGenerationStrategy" />
</util:map>

Adaptation of authn-events-flow.xml

The following extensions must be made in %{idp.home}/conf/authn/authn-events-flow.xml:

<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
      abstract="true">
 
    <!-- Custom error events to reflect back from user-supplied login subflows. -->
    <!-- <end-state id="MyCustomEvent" /> -->
    <end-state id="fudispasskeys" /><!-- add this for fudispasskeys -->
 
    <global-transitions>
        <!-- <transition on="MyCustomEvent" to="MyCustomEvent" /> -->
        <transition on="fudispasskeys" to="fudispasskeys" /><!-- add this for fudispasskeys -->
        <transition on="#{!'proceed'.equals(currentEvent.id)}" to="InvalidEvent" />
    </global-transitions>
 
</flow>

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:

<util:map id="shibboleth.authn.MFA.TransitionMap">
    <entry key="">
         <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/fudispasskeys"/>
    </entry>
    <entry key="authn/fudispasskeys">
        <bean parent="shibboleth.authn.MFA.Transition">
            <property name="nextFlowStrategyMap">
                <map>
                    <entry key="NoCredentials" value="authn/Password" />
                    <entry key="InvalidCredentials" value="authn/Password" />
                </map>
            </property>
        </bean>
    </entry>
    <entry key="authn/Password">
        <bean parent="shibboleth.authn.MFA.Transition">
            <property name="nextFlowStrategyMap">
                <map>
                    <entry key="fudispasskeys" value="authn/fudispasskeys" /><!-- add this for fudispasskeys -->
                    <entry key="proceed" value-ref="checkSecondFactor" /><!-- or the previous entry for the second factor -->
                </map>
            </property>
        </bean>
    </entry>
</util:map>

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 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 = <domain of your institution>
webauthn_relying_party_name = <name of the relying party>
webauthn_resident_key = required
webauthn_user_verification_requirement = required

Analog zum 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

To obtain detailed logging information from the fudiscr-Plugin, the following line can be added to %{idp.home}/conf/logback.xml:

<logger name="de.fu.dis" level="DEBUG"/>
<logger name="de.zedat.fudis" level="DEBUG"/>

If all API requests against privacyIDEA are to be logged separately, the following additional configuration is suggested:

<logger name="de.fu.dis.rest.interceptors" level="TRACE">
    <appender-ref ref="IDP_FUDIS_REQUEST"/>
</logger>
<logger name="de.zedat.fudis.rest.interceptors" level="TRACE">
    <appender-ref ref="IDP_FUDIS_REQUEST"/>
</logger>
<logger name="de.fu.dis.edumfa.client" level="WARN">
    <appender-ref ref="IDP_FUDIS_REQUEST"/>
</logger>
<logger name="de.zedat.fudis.privacyidea.client" level="WARN">
    <appender-ref ref="IDP_FUDIS_REQUEST"/>
</logger>
 
<!-- FUDIS-Interceptor log. -->
<appender name="IDP_FUDIS_REQUEST" class="ch.qos.logback.core.rolling.RollingFileAppender">
   <File>${idp.logfiles}/idp-fudis-request.log</File>
   <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${idp.logfiles}/idp-request-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
      <maxHistory>${idp.loghistory}</maxHistory>
   </rollingPolicy>
   <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <charset>UTF-8</charset>
      <Pattern>%msg%n</Pattern>
   </encoder>
</appender>


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 here. DAASI also offers software maintenance contracts for the extension. Please send inquiries to info@daasi.de.

Universities and other public institutions can get access to the source code upon request. Please send an email to 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.

Please send bugs, questions, suggestions etc. via email to fudis@fu-berlin.de.

  • Last modified: 3 weeks ago