IdP-Authn-Plugin fudiscr

Das Plugin kann als weiterer Faktor für die Multi-Faktor-Authentifizierung (MFA) vom Shibboleth Identity Provider (IdP) verwendet werden. Es unterstützt Token basierte Authentifizierungen und folgt im Kern dem Challenge-Response-Pattern. Aus einem der vorherigen Faktoren in der MFA-Abfolge wird als Basis ein Benutzername ermittelt. Ein häufiger Fall ist eine vorangestellte Authentifizierung mit Benutzer und Passwort gegen LDAP mit dem authn/Password-Flow.

Das Plugin besitzt selbst keine Funktionen zur Verwaltung von Token. Es enthält auch selbst keine speziellen Algorithmen zur Validierung von Token. Über ein generisches ChallengeResponseClientInterface kann ein Token-System angebunden werden. Das Plugin enthält bereits eine Implementierung dieses Interfaces für eduMFA und privacyIDEA. Zudem ist eine rudimentäre Mockup-Implementierung für Tests enthalten. Eine externe Implementierung für Fortinet existiert auch bereits.

Die primären Entwicklungsarbeiten wurden von Steffen Hofmann von der Freien Universität Berlin durchgeführt. Er ist Mitarbeiter des Rechenzentrums (FUB-IT) und zuständig für den Bereich Identity Management. Das Identity Management System der Freien Universität Berlin trägt den Namen FUDIS (FU Directory and Identity Service). Aus diesem Grund wird für das Challenge-Response-Plugin in Konfigurationsparametern u.a. das Kürzel fudiscr verwendet.

Mit den Versionen 1.4.0/2.0.0 erfolgt eine Zusammenarbeit im Bereich Passkeys mit der Hochschule München. Das Plugin enthält ab diesen Versionen ein weiteres Authentifizierungsverfahren mit dem Namen fudispasskeys. Anstelle einer zweistufigen Authentifizierung aus z.B. Benutzernamen/Passwort und Token-Validierung mit fudiscr kann eine alleinige Authentifizierung mit Passkeys durchgeführt werden.

Plugin-Version IdP-Version eduMFA-Version privacyIDEA-Version Support-Level
1.0.0 min. 4.1.2 und < 4.3.0 N/A min. 3.7 nicht mehr verwenden
1.1.0 min. 4.1.2 und < 4.3.0 N/A min. 3.7 nicht mehr verwenden
1.1.1 min. 4.1.2 und < 4.3.0 N/A min. 3.7 nicht mehr verwenden
1.2.0 min. 4.1.2 und < 4.3.0 N/A min. 3.7 nicht mehr verwenden
1.3.0 min. 4.3.0 und < 5.0.0 N/A min. 3.7 nicht mehr verwenden
1.4.0 min. 4.3.0 und < 5.0.0 min. 2.0.0 min. 3.9.2 nicht mehr verwenden
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 nicht mehr verwenden
2.1.0 min. 5.1.0 min. 2.0.0 min. 3.9.2 aktuell

Aktuell werden die folgenden Token-Verfahren aus eduMFA (Token types in eduMFA) und privacyIDEA (Token types in privacyIDEA) unterstützt:

Token-Verfahren Typbezeichnung im Plugin min. Plugin-Version Einschränkungen / Hinweise
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 Es darf nur eine Antwort per Richtlinie verlangt werden.
Registration registration_code 1.3.0
Remote referenzierter Tokentyp 1.3.0 Nur referenzierte Token vom Typ Email, HOTP Token, mOTP Token, Paper Token (PPR), Registration, SMS Token, TAN Token, TOTP, Yubico und Yubikey werden unterstützt.
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

Das fudiscr-Plugin benötigt das MFA-Modul. Dieses sollte wie folgt aktiviert werden:

%{idp.home}/bin/module.sh -e idp.authn.MFA

Mit dem MFA-Modul wird auch die Konfigurationsdatei, %{idp.home}/conf/authn/mfa-authn-config.xml abgelegt.

(siehe auch Shibboleth-IdP MultiFactorAuthnConfiguration)

Die Installation sowie Updates erfolgen über den Shibboleth Plugin-Mechanismus (siehe offizielle Dokumentation PluginInstallation)

Achtung: Der Shibboleth Identity Provider führt nach einer Plugin-Installation automatisch einen Neustart durch.

Mit dem folgenden Aufruf wird das Plugin installiert und aktiviert:

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

Zukünftige Updates werden automatisch mit dem folgendem Aufruf installiert:

%{idp.home}/bin/plugin.sh -u de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr

Für ein vollautomatische Installation können die PGP-Keys vorab heruntergeladen und bei der Installation mit angeben werden:

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


(Zum REFEDS Multi-Factor Authentication Profile siehe unter REFEDS AuthN Profiles - Hinweise für Identity Provider)
In %{idp.home}/conf/authn/authn.properties sind folgende Einstellungen vorzunehmen:

./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 ist bereits im Standard aktiviert. Es wird lediglich die AuthenticationContextClass saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR ergänzt. Anstelle von saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR kann auch eine andere AuthenticationContextClass oder zusätzliche unter idp.authn.fudiscr.supportedPrincipals UND idp.authn.MFA.supportedPrincipals eingetragen werden. Es wird empfohlen, mindestens eine zusätzliche Klasse zu definieren, über die explizit die Multi-Faktor-Authentifizierung ausgelöst werden kann. Es besteht aber keine Verpflichtung, da eine Multi-Faktor-Authentifizierung auch über andere Mechanismen ausgelöst werden kann.

Über die Transitionen in %{idp.home}/conf/authn/mfa-authn-config.xml wird die Abfolge der einzelnen Faktoren gesteuert. Hier können verschiedene Mechanismen verwendet werden, um die Bedingungen für die Übergänge in die nächsten Faktoren zu formulieren. Die offizielle Dokumentation dazu finden Sie hier: MultiFactorAuthnConfiguration

Im Folgenden werden drei Beispiele für eine MFA-Konfiguration aufgeführt.

Beispiel 1: Nach der Authentifizierung mit Benutzername und Passwort erfolgt immer eine Token basierte Authentifizierung mit fudiscr.

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

Beispiel 2: Nach der Authentifizierung mit Benutzername und Passwort erfolgt dann eine Token basierte Authentifizierung, wenn die AuthenticationContextClass, die der ServiceProvider benötigt, nicht ausreicht.

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

Beispiel 3: Nach der Authentifizierung mit Benutzername und Passwort erfolgt dann eine Token basierte Authentifizierung, wenn die AuthenticationContextClass, die der ServiceProvider benötigt, nicht ausreicht oder wenn der/die Benutzer*in nach eduPersonAffiliation zu der Gruppe der Beschäftigten (employee) gehört.

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

Beispiel 4: Nach der Authentifizierung mit Benutzername und Passwort erfolgt dann eine Token basierte Authentifizierung, wenn die AuthenticationContextClass, die der ServiceProvider benötigt, nicht ausreicht oder wenn der/die Benutzer*in bereits mindestens einen Token besitzt. Bei den Tokens wird der Zustand nicht geprüft. Ein gesperrter Token würde z.B. dafür sorgen, dass das fudiscr.UserHasTokenPredicate den Wert true zurückgibt. Dieses Predicate wurde insbesondere für Rollout-Szenarien eingeführt.

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


Beispiel 5: Wahlweise authn/Password und authn/SPNEGO als ersten Faktor → hier.


Reuse Condition

Für Authentifizierungsverfahren im Shibboleth Identity Provider kann generell eine Reuse Condition definiert werden. In der Regel ist der Default-Wert shibboleth.Conditions.TRUE. Die Bedingung ist also immer erfüllt. Es können als Reuse Condition auch eigene Funktionen definiert werden.

Für die Multi-Faktor-Authentifizierung gelten besondere Regeln. Eine Beschreibung finden Sie in der offiziellen Dokumentation unter MultiFactorAuthnConfiguration.

Mit folgender Konfiguration in %{idp.home}/conf/authn/authn.properties erreichen Sie, dass innerhalb einer Single-Sign-On-Session nur einmal Benutzername und Passwort verlangt wird, aber pro Service Provider Authentifizierung immer eine Token basierte Authentifizierung mit fudiscr.

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

Das Ergebnis der MFA-Authentifizierung darf demnach nicht erneut verwendet werden, aber innerhalb der Multi-Faktor-Authentifizierung darf das existierende erfolgreiche Ergebnis der Benutzername-Passwort-Authentifizierung (Password) verwendet werden und das Ergebnis von fudiscr aber nicht.

Wenn 'ForceAuthn' via SAML angefragt wird, dann gibt die Funktion mfaCtx.isAcceptable() immer false zurück.

Jedes Authentifizierungsverfahren im Shibboleth Identity Provider basiert auf einem abstrakten Flow mit einem AuthenticationFlowDescriptor, siehe hierzu AuthenticationConfiguration.

Die Eigenschaften des AuthenticationFlowDescriptor werden in der Regel für alle Flows in %{idp.home}/conf/authn/authn.properties festgelegt. Als Vorlage sind diese auskommentiert in %{idp.home}/conf/authn/fudiscr.properties enthalten, um sie nach der Plugin-Installation bereitzustellen:

#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 wurde bereits in der https://doku.tid.dfn.de/de:shibidp:plugin-fudiscr#mfa-konfiguration vorgestellt.

Wenn Sie eine IdP-Konfiguration von vor Version 4.1.0 weiter pflegen, achten Sie bitte darauf, dass die Datei %{idp.home}/conf/idp.properties zu Beginn die folgende Zeile enthält, damit automatisch alle .properties-Dateien unterhalb des %{idp.home}/conf-Ordners (wie z.B. %{idp.home}/conf/fudiscr.properties) eingelesen werden.

idp.searchForProperties=true

Mit der Installation des fudiscr-Plugins wird die Konfigurationsdatei %{idp.home}/conf/authn/fudiscr.properties abgelegt. Änderungen in dieser Datei werden in der Regel erst mit einem Neustart des Identity Providers wirksam.

Außerdem wird die Konfigurationsdatei %{idp.home}/conf/authn/fudiscr.xml abgelegt. Die in dieser Datei enthaltenen Konfigurations-Beans werden über den ReloadableService-Mechanismus vom Shibboleth Identity Provider verwaltet (siehe auch ReloadableServices).

Der Service kann wie folgt neu geladen werden:

%{idp.home}/bin/reload-service.sh -id fudiscr.ConfigurationBeansService

Alternativ kann der Reload durch direkten Aufruf der URL ausgelöst werden und vermeidet Fehler durch den Wrapper in reload-service.sh.

GET http(s)://[idp-base-url]/idp/profile/admin/reload-service?id=fudiscr.ConfigurationBeansService

Analog zu den Parametern für die anderen ReloadableServices vom Shibboleth Identity Provider existieren für das fudiscr-Plugin die nachfolgenden:

fudiscr.service.failFast=false
fudiscr.service.checkInterval=PT0S
fudiscr.service.resources=fudiscr.ConfigurationResources

Sollen Konfigurationsänderungen in %{idp.home}/conf/authn/fudiscr.xml automatisch geladen werden, so ist das Prüfintervall mit fudiscr.service.checkInterval zu setzen. Per Default (PT0S) ist die regelmäßige Prüfung der Veränderung des Zeitstempels von %{idp.home}/conf/authn/fudiscr.xml deaktiviert.

Folgende allgemeine Konfigurationsoptionen können über die Konfigurationsdatei %{idp.home}/conf/authn/fudiscr.properties gesetzt werden.

OptionDefault-WertBeschreibung
fudiscr.challengeResponseClientEduMfaChallengeResponseClientDiese Option legt fest, welches Token-Backend angesprochen werden soll. Seit Version 2.1.0 ist der Default-Wert EduMfaChallengeResponseClient, zuvor PrivacyIdeaChallengeResponseClient. Es gibt auch die Möglichkeit, einen PrivacyIdeaChallengeResponseClient zu verwenden, der ohne Backend funktioniert und sämtliche Informationen als DEBUG-Meldungen beim Starten des IdP in die Logfiles schreibt.
fudiscr.default_users_realmleer
fudiscr.filter_tokens.id_exclude_regexleerMit dieser Option können Token anhand ihrer ID/Seriennummer für die Verwendung im fudiscr-Plugin ausgeschlossen werden. Z.B. werden mit ^HOTP.*$ alle HOTP-Token ignoriert, die als Softwaretoken in privacyIDEA angelegt wurden und eine Seriennummer beginnend mit HOTP erhalten. Per Default wird der Filter nicht verwendet. Die Regular Expression ist nicht definiert.
fudiscr.filter_tokens.type_exclude_regexleerMit dieser Option können Token-Typen für die Verwendung im fudiscr-Plugin ausgeschlossen werden. Z.B. werden mit ^totp$ alle TOTP-Token ignoriert. Per Default wird der Filter nicht verwendet. Die Regular Expression ist nicht definiert.
fudiscr.on_failed_restart_completely_mfashibboleth.Conditions.FALSEDieses Predicate legt fest, ob bei einem Neustart der Authentifizieurung aus dem fudiscr-Plugin heraus nur der Faktor fudiscr im MFA-Flow (Default) oder die gesamte Authentifizierung neu gestartet wird. Gibt das Predicate TRUE zurück, werden also die Ergebnisse der vorherigen Faktoren zurückgesetzt. Es werden also Benutzername und Passwort u.a. erneut verlangt.
fudiscr.max_retries10Anzahl an Fehlversuchen für die Validierung. Ist der Wert erreicht, wird die Validierung erfolglos beendet. Dieser Wert ist unabhängig von den erlaubten Fehlversuchen in privacyIDEA. Sollen ausschließlich die Fehlerzähler aus privacyIDEA wirken, so empfiehlt es sich, den Wert für fudiscr.max_retries passend zu erhöhen (> max. Failcounter in privacyIDEA).
fudiscr.restart_allowed.max_retries_exceededshibboleth.Conditions.TRUEDieses Predicate legt fest, ob ein Neustart der Authentifizierung im fudiscr-Plugin erlaubt ist, wenn fudiscr.max_retries erreicht ist.
fudiscr.restart_allowed.selected_tokens_not_availableshibboleth.Conditions.TRUEDieses Predicate legt fest, ob ein Neustart der Authentifizierung im fudiscr-Plugin erlaubt ist, wenn die durch Nutzer ausgewählten Token nicht mehr verfügbar sind (z.B. durch Sperrung in privacyIDEA).
fudiscr.restart_allowed.within_response_pageshibboleth.Conditions.TRUEDieses Predicate legt fest, ob ein Neustart der fudiscr-Authentifizierung über die Eingabeseite für die Response (OTP, TAN u.a.) erlaubt ist.
fudiscr.result_idp_attribute_principal_attribute_nameleerWird mit dieser Option ein Attributname angeben, so wird ein IdpAttributePrincipal mit diesem Attributnamen dem erfolgreichen Authentifizierungsergebnis des fudiscr-Plugins hinzugefügt. Der Attributwert enthält die ID/Seriennummer des Token, mit dem die Authentifizierung erfolgt ist.
fudiscr.result_idp_attribute_principal_attribute_name_for_token_typeleerWird mit dieser Option ein Attributname angeben, so wird ein IdpAttributePrincipal mit diesem Attributnamen dem erfolgreichen Authentifizierungsergebnis des fudiscr-Plugins hinzugefügt. Der Attributwert enthält den Token-Typ des Token, mit dem die Authentifizierung erfolgt ist.
fudiscr.result_with_realm_in_username_principalshibboleth.Conditions.FALSEWenn mit fudiscr.result_with_username_principal ein UsernamePrincipal erzeugt wird, dann legt dieses Predicate fest, ob der Realm enthalten ist. TRUE (default) erzeugt z.B. dummy@fu-berlin.de, FALSE nur dummy als Username.
fudiscr.result_with_username_principalshibboleth.Conditions.TRUEDieses Predicate legt fest, ob nach erfolgreicher Authentifizierung ein UsernamePrincipal dem Subject hinzugefügt wird. Per Default enthält das Authentifizierungsergebnis nach erfolgreicher Authentifizierung mit dem fudiscr-Plugin einen UsernamePrincipal.
fudiscr.username_realm_split_regex@Aus einem vorherigen Faktor wird bei der Authentifizierung ein Benutzername erzeugt. Dieser kann auch einen Realm enthalten, z.B. dummy@fu-berlin.de. Mit dieser Option wird das Trennzeichen festgelegt, anhand dessen die Auftrennung in Benutzernamen und Realm erfolgt. Der Defaultwert ist @
fudiscr.user_token_selectionnoneDiese Option legt fest, ob Token durch Nutzer*innen ausgewählt werden können. Mögliche Werte sind: none=keine Auswahl, singleToken=Auswahl eines einzelnen Token (anhand der ID/Seriennummer), multipleToken=Auswahl mehrerer Token (anhand der Seriennummer), singleTokenTypeGroup=Auswahl eines einzelnen Tokentyps (z.B. totp), singleTokenTypeGroup=Auswahl mehrerer Token-Typen. Per Default (none) wird eine Challenge für alle aktive Token erzeugt.

eduMFA

privacyIDEA

Token können anhand von Eigenschaften wie Token-ID/-Seriennummer, -Status, -Typ u.a. herausgefiltert werden. Außerdem können für die Filterung die Eigenschaften aus einer SAML- oder OIDC-Anfrage genutzt werden. Hierzu gehören z.B. die entityID des Service Provider, die Authentication Context Class oder die IP-Adresse der Nutzer*innen.

Generelle Filterung via Regular Expressions

Allgemein können Token anhand ihres Typs oder ihrer ID/Seriennummer mit den (o.g.) Konfigurationsoptionen fudiscr.filter_tokens.type_exclude_regex und fudiscr.filter_tokens.id_exclude_regex in %{idp.home}/conf/authn/fudiscr.properties ausgeschlossen werden.

fudiscr.filter_tokens.type_exclude_regex=^(sms|web_authn)$

In diesem Beispiel werden alle SMS- und WebAuthn-Token herausgefiltert.

fudiscr.filter_tokens.id_exclude_regex=^(OATH|TOTP).+$

In diesem Beispiel werden alle Token herausgefiltert, die den Präfix OATH oder TOTP in der Seriennummer besitzen. Das betrifft in eduMFA und privacyIDEA alle HOTP- und TOTP-Token

Filterung mit erweiterten Ausschlussbedingungen

In %{idp.home}/conf/authn/fudiscr.xml können erweiterte Ausschlussbedingungen definiert werden. Die Bedingungen werden in der Liste fudiscr.ExcludeTokenPredicates zusammengefasst und nacheinander abgearbeitet.

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

In diesem Beispiel wird gezeigt, wie die Ausschlussliste generell definiert wird.

Die Ausschlussbedingungen können Scripted oder anhand von Regular Expressions bezogen auf Token-Eigenschaften formuliert werden.

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

In diesem Beispiel dürfen Notfallcodes (registration_code) nur für ein (Service-)Portal und sonst keinen anderen Service Provider verwendet werden. Nutzer*innen können sich mit Hilfe der Notfallcodes wieder andere Token-Verfahren im (Service-)Portal einrichten bzw. freischalten.

Beispiel mit 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 diesem Beispiel werden Token anhand einer Token-Eigenschaft herausgefiltert. In eduMFA und privacyIDEA können aktive Token existieren, die eine erste Validierung erfordern, bevor sie effektiv verwendet werden können. So soll z.B. erzwungen werden, dass Nutzer*innen bei der Einrichtung eines TOTP-Token den QR-Code mit dem Authenticator einscannen und diesen Vorgang mit einem OTP bestätigen. Die Token besitzen bis zur Bestätigung die Eigenschaft rollout_state mit den Werten verify oder clientwait. Die in dem Beispiel verwendeten zwei Predicates lassen sich auch mit einem Predicate und der Regular Expression ^(clientwait|verify)$ abbilden. Es soll in dem Beispiel nur die Listeneigenschaft von fudiscr.ExcludeTokenPredicates verdeutlicht werden.

(Mit dem Konstruktor-Parameter c:negateResult=„true“ kann das Ergbnis eines ExcludeTokenPredicates negiert werden.)

Mit der Bean fudiscr.UserHasTokenPredicate wird eine Funktion zur Verfügung gestellt, um zu entscheiden, ob Nutzer*innen generell schon für den zweiten Faktor bzw. für ein Token-Verfahren eingerichtet wurden. Diese Funktion kann insbesondere in der Rollout-Phase von Nutzen sein. Die Entscheidung, ob Nutzer*innen für einen zweiten Faktor aktiviert sind, kann aber auch über z.B. Gruppen in einem LDAP-Verzeichnisdienst gesteuert werden.

Auch für diese Funktion werden die zuvor genannten Filtermöglichkeiten per Beans in %{idp.home}/conf/authn/fudiscr.xml angeboten. Die verwendete Filter-Liste hat die id fudiscr.UserHasTokenExcludeTokenPredicates. Sollen die Filterlisten fudiscr.ExcludeTokenPredicates und fudiscr.UserHasTokenExcludeTokenPredicatesvon identisch sein, so kann für fudiscr.UserHasTokenExcludeTokenPredicates ein Alias definiert werden:

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


Äquivalent zu den Filtern fudiscr.ExcludeTokenPredicates und fudiscr.UserHasTokenExcludeTokenPredicates kann ein Filter definiert werden, der die bevorzugten Token eines Nutzers herausfiltert. Wird die Exclude-Filter-Liste fudiscr.PreferredExcludeTokenPredicates definiert, so werden initial nur die bevorzugten Token verwendet. Führt die Filterung dazu, dass alle Token ausgeschlossen werden, wird die Filterung zurückgesetzt. Sollte die Filterung die Ausgangsliste reduzieren, so wird dem Nutzer auf der Auswahlseite für die Token und der Eingabeseite für die Response ein Button angeboten, um die vollständige Token-Auswahl zu erhalten. Sollte nach der Filterung mit fudiscr.PreferredExcludeTokenPredicates eine Selektion durch die Nutzer*innen nicht mehr notwendig sein, wird sofort die Challenge erzeugt. Ergibt also z.B. die Filterung, dass nur TOTP-Token übrig bleiben und wurde fudiscr.user_token_selection=singleTokenTypeGroup konfiguriert, so wird sofort eine Challenge auf die TOTP-Token ausgelöst und die Eingabeseite für die Response angezeigt. Nutzer erhalten aber dann wie zuvor genannt die Möglichkeit, trotzdem andere Token-Verfahren auszuwählen.

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

Das Beispiel filtert alle Token heraus, die nicht das Token-Info-Attribut fudis_preferred mit dem Wert true (case-insensitive) und nicht vom Typ 'totp' sind. p:defaultValueForMissingProperty=„no“ bewirkt, dass das RegexExcludeTokenPropertiesPredicate bei fehlendem Token-(Info-)Attribut no zurückgibt. Bitte beachten Sie, dass durch c:negateResult=„true“ aus einem Exclude ein Include wird. In dem Beispiel werden im Gegensatz zu den vorherigen Filterbeispielen die Beans direkt in der Liste und nicht als Referenzen definiert.

Es kann vorkommen, dass Nutzer*innen im Identity Management über mehrere Datenquellen verteilt sind. So können z.B. Studierende und Beschäftigte in zwei getrennten LDAP-Servern verwaltet werden. Dies führt wiederum dazu, dass z.B. in eduMFA oder privacyIDEA zwei Realms angelegt werden müssen und bei den API-Anfragen müssen diese Realms unterschieden werden. Für das User-Objekt im fudiscr-Plugin wird folgende Logik durchlaufen, um den Realm zu ermitteln:

  1. Mit der Java-Klasse net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy wird zunächst gemäß Shibboleth-Standard der Benutzername aus den vorherigen Authentifizierungsergebnissen ermittelt.
  2. Anhand von fudiscr.username_realm_split_regex wird versucht, den Realm als gesondertes Attribut vom Benutzernamen abzutrennen.
  3. Wenn eine Liste fudiscr.UserRealmTransformationStrategies mit Realm-Transformationsstrategien in %{idp.home}/conf/authn/fudiscr.xml definiert wurde, dann werden diese Strategien in der aufgeführten Reihenfolge durchlaufen.
  4. Sollte noch kein Realm existieren, wird ein Default-Realm zugeordnet, wenn dieser mit fudiscr.default_users_realm definiert wurde.

Der so ermittelte Realm wird im fudiscr-Plugin von allen Klassen verwendet, die ein User-Objekt verarbeiten.

Sollte der Realm im User-Objekt vom fudiscr-Plugin nicht definiert (null) sein, dann kann mit fudiscr.edumfa.default_realm ein Realm für die API-Anfrage gegen eduMFA bzw. mit fudiscr.privacyidea.default_realm gegen privacyIDEA hinzugefügt werden. Der so hinzugefügte Realm wird aber nicht in das allgemeine User-Objekt vom fudiscr-Plugin übernommen!

Beispiel 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 dem Beispiel wird geprüft, ob in dem aktiven Authentifizierungsergebnis aus der Authentifizierung mit Benutzername und Passwort (authn/Password) ein LdapPrincipal existiert und ob dieser LdapPrincipal das Attribut realm enthält. Der erste Wert aus realm wird als Realm übernommen. Anschließend wird eine Scripted Strategie angewandt.

Das REFEDS Assurance Framework definiert u.a. Profile für die Multifaktor-Authentifizierung. Grundsätzlich können Service Provider per Authentication Context Class Eigenschaften verlangen, die nur auf bestimmte Token zutreffen. Eine Filterung der passenden Token ist mit fudiscr.ExcludeTokenPredicates im möglich, aber bei der Frage, ob ein Authentifizierungsergebnis in einer SSO-Session für einen anderen Service Provider genutzt werden kann, nützt diese Filterung nichts.

Authentication Context Classes werden im Shibboleth Identity Provider als Principals abgebildet. Jedes Authentifizierungsverfahren besitzt die Eigenschaften idp.authn.<flowid>.supportedPrincipals und idp.authn.<flowid>.addDefaultPrincipals (Default: true). Die idp.authn.<flowid>.supportedPrincipals entscheiden darüber, ob das jeweilige Authentifizierungsverfahren zu der angefragten Authentication Context Class passt. Mit idp.authn.<flowid>.addDefaultPrincipals=true werden die idp.authn.<flowid>.supportedPrincipals zum Authentifizierungsergebnis hinzugefügt.

Die Listen-Bean fudiscr.SubjectCustomizers ermöglicht es, die Principals für die Authentication Context Class zu modifizieren.

Zusätzlich werden zwei Funktionen als Reuse-Conditions angeboten.

fudiscr.FlowSpecificRequestedPrincipalsReuseCondition verlangt nochmal den einzelnen (zweiten) Faktor, wenn die bisherigen Authentication Context Classes nicht ausreichen. fudiscr.GeneralRequestedPrincipalsReuseCondition verlangt alle Faktoren, wenn die bisherigen Authentication Context Classes nicht ausreichen.

Das nachfolgende Beispiel zeigt das Zusammenspiel der Konfigurationselemente.

Beispiel SubjectCustomizers mit supportedPrincipals und ReuseConditions

In %{idp.home}/conf/authn/authn.properties werden für das Authentifizierungsverfahren fudiscr die supportedPrincipals definiert:

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

Für den MFA-Flow müssen alle supportedPrincipals definiert werden, die durch die einzelnen verwendeten Faktoren in Summe abgedeckt werden. Besteht der erste Faktor aus dem üblichen Abfrage nach Benutzernamen und Password (flowid=password), dann sollte der Eintrag in der %{idp.home}/conf/authn/authn.properties wie folgt aussehen:

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

In der %{idp.home}/conf/authn/fudiscr.xml wird nun ein Subject Customizer definiert, der für fudiscr immer die Authentication Context Class saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR hinzufügt. Abhängig von den Eigenschaften des erfolgreich verwendeten Tokens wird entweder nur saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW oder saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:LOW sowie saml2/urn:de:zedat:fudis:SAML:2.0:ac:classes:CR:IAP:HIGH ergänzt.

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

In %{idp.home}/conf/authn/authn.properties können nun die zwei Varianten für die Reuse Conditions gesetzt werden.

Variante 1: Nur der zweite Faktor wird noch einmal verlangt, wenn die bisherigen Authentication Context Classes nicht ausreichen.

idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE
idp.authn.Password.reuseCondition=shibboleth.Conditions.TRUE
idp.authn.fudiscr.reuseCondition=fudiscr.FlowSpecificRequestedPrincipalsReuseCondition

Variante 2: Alle Faktoren werden wieder verlangt, wenn die bisherigen Authentication Context Classes nicht ausreichen.

idp.authn.MFA.reuseCondition=shibboleth.Conditions.FALSE
idp.authn.Password.reuseCondition=fudiscr.GeneralRequestedPrincipalsReuseCondition
idp.authn.fudiscr.reuseCondition=fudiscr.GeneralRequestedPrincipalsReuseCondition


Als Zusatzangebot kann für den Prozess der SubjectCanonicalization im Shibboleth Identity Provider ein Flow auf Basis von de.zedat.fudis.shibboleth.idp.plugin.authn.fudiscr.domain.ChallengeResponseTokenIdPrincipal aktiviert werden. Dieser vom fudiscr-Plugin erzeugte zusätzliche Principal enthält die ID/Seriennummer des erfolgreich verwendeten Token.

Um den Flow zu aktivieren, muss in der Konfigurationsdatei %{idp.home}/conf/c14n/subject-c14n.xml die Referenz <ref bean=„c14n/fudiscr“ /> in die Liste shibboleth.PostLoginSubjectCanonicalizationFlows aufgenommen werden.

Zusätzlich können wie für andere Principals Transformationsoptionen z.B.in %{idp.home}/conf/c14n/subject-c14n.properties angegeben werden:

idp.c14n.fudiscr.lowercase=false
idp.c14n.fudiscr.uppercase=false
idp.c14n.fudiscr.trim=true


Mit fudispasskeys wird ein Authentifizierungsverfahren angeboten, das sich in die Login-Seite für die Authentifizierung mit Benutzernamen und Passwort integrieren lässt und eine direkte Authentifizierung mit einem Sicherheitsschlüssel bzw. Passkeys ermöglicht. Die Konfiguration erfolgt in folgenden Schritten:

Verwendung des EduMfaChallengeResponseClient

Das Verfahren wird nur vom EduMfaChallengeResponseClient in Zusammenspiel mit eduMFA unterstützt. In %{idp.home}/conf/authn/fudiscr.properties muss also die folgende Eigenschaft gesetzt sein:

fudiscr.challengeResponseClient=EduMfaChallengeResponseClient

Definition der Relying Party Id für WebAuthn/Passkeys

Zusätzlich muss in %{idp.home}/conf/authn/fudiscr.properties die Relying Party Id für WebAuthn/Passkeys gemäß der enrollment policy webauthn_relying_party_id aus eduMFA gesetzt werden:

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

Definition/Erweiterung der Bean shibboleth.CustomViewContext

Um in den Views auf spezielle Funktionen zugreifen zu können, muss in der %{idp.home}/conf/global.xml die Bean shibboleth.CustomViewContext definiert oder erweitert werden. Es handelt sich hierbei um eine Liste, die mindestens einen Eintrag für die fudispasskeys.ChallengeGenerationStrategy benötigt:

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

Anpassung von authn-events-flow.xml

In der %{idp.home}/conf/authn/authn-events-flow.xml müssen folgende Erweiterungen vorgenommen werden:

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

Anpassung von mfa-authn-config.xml

In der bestehenden %{idp.home}/conf/authn/mfa-authn-config.xml muss die shibboleth.authn.MFA.TransitionMap erweitert werden:

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

Anpassung von login.vm

In der %{idp.home}/views/login.vm muss außerhalb des Login-Formulars folgende Zeile eingefügt werden:

#parse("fudispasskeys/login-passkeys.vm")

Zusätzlich kann bei den Input-Feldern für Benutzernamen und Passwort das Attribut autocomplete mit dem Wert webauthn eingefügt bzw. ergänzt werden, z.B.

autocomplete="webauthn"

Dadurch wird zusätzlich eine Passkeys-Authentifizierung angeboten, wenn man diese Input-Felder auswählt.

Policies in eduMFA

Gemäß eduMFA-Richtlinien für fudispasskeys müssen folgende authentication- und enrollment-Policies in eduMFA gesetzt sein:

für den Scope authentication:

webauthn_user_verification_requirement = required
webauthn_usernameless_authn = True

für den 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

Um detaillierte Logging-Informationen vom fudiscr-Plugin zu erhalten, kann die folgende Zeile in %{idp.home}/conf/logback.xml ergänzt werden:

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

Sollen alle API-Anfragen gegen privacyIDEA separat protokolliert werden, so wird folgende zusätzliche Konfiguration vorgeschlagen:

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


Die Erweiterung für Fortinet unterstützt aktuell die Token-Typen FortiToken (Mobile und Hardware), SMS und E-Mail. FIDO2 (WebAuthn) wird aktuell von der FortiAuthenticator API nicht unterstützt.

Die Erweiterung steht unter der Apache 2.0 Lizenz und wird von der DAASI International GmbH hier bereitgestellt. DAASI bietet für die Erweiterung auch Softwarepflegeverträge an. Anfragen sind bitte an info@daasi.de zu senden.

Hochschulen und andere öffentliche Einrichtungen erhalten auf Anfrage Zugriff auf den Quellcode. Bitte senden Sie hierfür eine E-Mail an fudis@fu-berlin.de. Es wird daran gearbeitet, sämtliche Komponenten des fudiscr-Plugins unter der Apache 2.0 Lizenz zu veröffentlichen. Wir bitten (weiterhin) um Geduld.

Fehler, Fragen, Anregungen etc. bitte per E-Mail an fudis@fu-berlin.de senden.

  • Zuletzt geändert: vor 14 Tagen