Shibboleth IdP als OpenID Connect - Provider

Danke

Der Dank für diese Dokumentation geht an Manuel Haim von der Philipps-Universität Marburg.

Die OIDC-Login-Schnittstelle wird für Webanwendungen benötigt, die ausschließlich das OIDC- bzw. OAuth-2.0-Protokoll unterstützen.

Versionsnummern prüfen!

Prüfen Sie bitte vor dem Download die aktuellen Versionsnummern der Plugins! Die Download-Links ändern sich leider mit den Versionsnummern.
root@idp:~# cd /opt/install/
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-common/2.2.0/oidc-common-dist-2.2.0.tar.gz
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-common/2.2.0/oidc-common-dist-2.2.0.tar.gz.asc
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-op/3.4.0/idp-plugin-oidc-op-distribution-3.4.0.tar.gz
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-op/3.4.0/idp-plugin-oidc-op-distribution-3.4.0.tar.gz.asc
# seit OIDC-OP Plugin-Version 3.4.0 zusätzlich:
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-config/1.0.1/idp-plugin-oidc-config-dist-1.0.1.tar.gz
root@idp:~# curl -O https://shibboleth.net/downloads/identity-provider/plugins/oidc-config/1.0.1/idp-plugin-oidc-config-dist-1.0.1.tar.gz.asc

Bei der Installation prüft der Plugin-Installer online die Kompatibilität der Plugin-Version zur Shibboleth-Version. Falls der Shibboleth-IdP nur über einen HTTP-Proxy Zugriff zum Internet erhält, muss für die Plugin-Installation zunächst ein HTTP-Proxy konfiguriert werden.

Im IdP <= 4.1.2 gibt es einen Bug (vgl. https://issues.shibboleth.net/jira/browse/IDP-1838), hier lässt sich die Kompatibilitäts-Prüfung mittels Parameter --nocheck umgehen:

root@idp:~# /opt/shibboleth-idp/bin/plugin.sh --nocheck -i /opt/install/oidc-common-dist-2.2.0.tar.gz
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh --nocheck -i /opt/install/idp-plugin-oidc-config-dist-1.0.1.tar.gz
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh --nocheck -i /opt/install/idp-plugin-oidc-op-distribution-3.4.0.tar.gz

Zur Konfiguration des HTTP-Proxy (IdP >= 4.1.3) müssen wir eine Datei /opt/install/beanfile.xml mit eigenen HttpClient-Parametern anlegen:

/opt/install/beanfile.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">
 
    <!--
    HttpClient bean for plugin installation.
    Use with /opt/shibboleth-idp/bin/plugin.sh -hc <BeanName> -i <plugin.tar.gz> <this-file.xml>
    -->
    <bean id="myHttpClient" parent="shibboleth.HttpClientFactory"
        p:connectionProxyHost="http-proxy.example.org"
        p:connectionProxyPort="3128" />
 
</beans>

Und dann mit folgenden Parametern installieren:

root@idp:~# /opt/shibboleth-idp/bin/plugin.sh -hc myHttpClient -i oidc-common-dist-2.2.0.tar.gz beanfile.xml
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh -hc myHttpClient -i idp-plugin-oidc-op-distribution-3.4.0.tar.gz beanfile.xml
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh -i /opt/install/oidc-common-dist-2.2.0.tar.gz
2021-07-21 11:07:04,309 - INFO [net.shibboleth.idp.installer.plugin.impl.PluginInstaller:233] - Installing Plugin net.shibboleth.oidc.common version 2.2.0
Installing Plugin net.shibboleth.oidc.common version 2.2.0
2021-07-21 11:07:04,400 - INFO [net.shibboleth.idp.installer.BuildWar:225] - Rebuilding /opt/shibboleth-idp/war/idp.war, Version 4.1.2
Rebuilding /opt/shibboleth-idp/war/idp.war, Version 4.1.2
2021-07-21 11:07:04,445 - INFO [net.shibboleth.idp.installer.BuildWar:225] - Initial populate from /opt/shibboleth-idp/dist/webapp to /opt/shibboleth-idp/webpapp.tmp
Initial populate from /opt/shibboleth-idp/dist/webapp to /opt/shibboleth-idp/webpapp.tmp
2021-07-21 11:07:05,614 - INFO [net.shibboleth.idp.installer.BuildWar:225] - Overlay from /opt/shibboleth-idp/dist/plugin-webapp to /opt/shibboleth-idp/webpapp.tmp
Overlay from /opt/shibboleth-idp/dist/plugin-webapp to /opt/shibboleth-idp/webpapp.tmp
2021-07-21 11:07:05,670 - INFO [net.shibboleth.idp.installer.BuildWar:225] - Overlay from /opt/shibboleth-idp/edit-webapp to /opt/shibboleth-idp/webpapp.tmp
Overlay from /opt/shibboleth-idp/edit-webapp to /opt/shibboleth-idp/webpapp.tmp
2021-07-21 11:07:05,736 - INFO [net.shibboleth.idp.installer.BuildWar:217] - Creating war file /opt/shibboleth-idp/war/idp.war
Creating war file /opt/shibboleth-idp/war/idp.war
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh -i /opt/install/idp-plugin-oidc-config-dist-1.0.1.tar.gz
Installing Plugin net.shibboleth.idp.plugin.oidc.config version 1.0.1
Rebuilding /opt/shibboleth-idp/idp2.local/war/idp.war, Version 4.3.1
Initial populate from /opt/shibboleth-idp/idp2.local/dist/webapp to /opt/shibboleth-idp/idp2.local/webpapp.tmp
Overlay from /opt/shibboleth-idp/idp2.local/dist/plugin-webapp to /opt/shibboleth-idp/idp2.local/webpapp.tmp
Overlay from /opt/shibboleth-idp/idp2.local/edit-webapp to /opt/shibboleth-idp/idp2.local/webpapp.tmp
Creating war file /opt/shibboleth-idp/idp2.local/war/idp.war
root@idp:~# /opt/shibboleth-idp/bin/plugin.sh -i /opt/install/idp-plugin-oidc-op-distribution-3.4.0.tar.gz
Plugin net.shibboleth.idp.plugin.oidc.op: Trust store folder does not exist, creating
Plugin net.shibboleth.idp.plugin.oidc.op: Trust store does not exist, creating
TrustStore does not contain signature 0X26691839355EBCA
Accept this key:
Signature:	0X26691839355EBCA
FingerPrint:	6D18FD63708FCCA079B68CCE026691839355EBCA
Username:	Henri Mikkonen <henri.mikkonen@iki.fi>
 [yN] y
Installing Plugin net.shibboleth.idp.plugin.oidc.op version 3.4.0
Rebuilding /opt/shibboleth-idp/war/idp.war, Version 4.1.2
Initial populate from /opt/shibboleth-idp/dist/webapp to /opt/shibboleth-idp/webpapp.tmp
Overlay from /opt/shibboleth-idp/dist/plugin-webapp to /opt/shibboleth-idp/webpapp.tmp
Overlay from /opt/shibboleth-idp/edit-webapp to /opt/shibboleth-idp/webpapp.tmp
Creating war file /opt/shibboleth-idp/war/idp.war
Module file changes as a result of this install
	conf/oidc-clientinfo-resolvers.xml created
	static/openid-configuration.json created
	bin/lib/json-web-key-generator-0.8.2-jar-with-dependencies.jar created
	conf/oidc-credentials.xml created
	conf/attributes/oidc-claim-rules.xml created
	bin/jwtgen.sh created
	conf/examples/oidc-attribute-filter.xml created
	bin/jwtgen.bat created
	conf/oidc.properties created
	conf/examples/oidc-attribute-resolver.xml created

Da es bisher keine OIDC-Metadaten für Föderationen gibt, müssen Sie entscheiden: Soll Ihr IdP nur mit einzelnen, vorkonfigurierten Relying Parties kommunizieren? Oder darf sich jede RP als Client bei Ihrem IdP registrieren? Letzteres sagt noch nichts über die tatsächlichen Attributfreigaben aus, die Sie - wie beim SAML-Protokoll - separat einstellen können. Hier werden beide Wege vorgestellt.

Unabhängig davon, ob Sie mit statischen RPs oder mit dynamischer Client-Registrierung arbeiten möchten, müssen Sie zunächst folgende Konfigurationsschritte gehen:

Wenn Sie eine veraltete IdP-Konfiguration weiterpflegen, achten Sie bitte darauf, dass die Datei conf/idp.properties oben die folgende Zeile enthält, damit automatisch alle .properties-Dateien unterhalb des conf-Ordners (wie z.B. conf/oidc.properties) eingelesen werden.

idp.searchForProperties=true

Setzen Sie Folgendes in die Datei /opt/shibboleth-idp/conf/credentials.xml ein, und zwar vor dem schließenden </beans>-Tag ganz unten:

/opt/shibboleth-idp/conf/credentials.xml
...
    <!-- OIDC extension default credential definitions -->
    <import resource="oidc-credentials.xml" />
...

Importieren Sie die vom Plugin mitgelieferten Claims (Attribute) in die Attribute Registry des IdP, indem Sie die Datei /opt/shibboleth-idp/conf/attributes/default-rules.xml anpassen. Die später freigegebenen Attribute werden gemäß conf/attributes/oidc-claim-rules.xml in OIDC Claims übersetzt.

/opt/shibboleth-idp/conf/attributes/default-rules.xml
...
    <import resource="oidc-claim-rules.xml" />
...

Erzeugen Sie die JSON Web Keys:

root@idp:~# /opt/shibboleth-idp/bin/jwtgen.sh -t RSA -s 2048 -u sig -i defaultRSASign | tail -n +2 >/opt/shibboleth-idp/credentials/idp-signing-rs.jwk
root@idp:~# /opt/shibboleth-idp/bin/jwtgen.sh -t EC -c P-256 -u sig -i defaultECSign | tail -n +2 >/opt/shibboleth-idp/credentials/idp-signing-es.jwk
root@idp:~# /opt/shibboleth-idp/bin/jwtgen.sh -t RSA -s 2048 -u enc -i defaultRSAEnc | tail -n +2 >/opt/shibboleth-idp/credentials/idp-encryption-rsa.jwk

Setzen Sie eine Issuer ID in der Datei /opt/shibboleth-idp/conf/oidc.properties:

/opt/shibboleth-idp/conf/oidc.properties
...
idp.oidc.issuer = https://idp.example.org
...

Bereitstellen der Konfigurationsparameter für OIDC-RPs

Momentan gibt es für OIDC keine signierten Föderationsmetadaten, wie sie für die SAML-Kommunikation existieren. Der Grund für das Fehlen ist, dass OpenID Connect Federation noch nicht fertig spezifiziert ist. Daher kann nur jeder OP (also jeder IdP) seine Metadaten direkt abrufbar machen. Die standardisierte Request-URI dafür lautet /.well-known/openid-configuration.

Um die „Metadaten“, also die Konfigurationsparameter, unter https://idp.example.org/.well-known/openid-configuration bereitzustellen, gehen Sie so vor (vgl. auch OPDiscovery im Shibboleth-Wiki):

  • In der mitgelieferten Datei /opt/shibboleth-idp/static/openid-configuration.json muss jeweils {{ service_name }} durch Ihren Wert für idp.example.org ausgetauscht werden. (Ja, muss es. Das ist kein Jinja-Template.) In derselben Datei sollten Sie nicht unterstützte Scopes aus dem Abschnitt scopes_supported entfernen.
  • Dann müssen Sie für unverifizierte Relying Parties ein Profil freischalten:
    /opt/shibboleth-idp/conf/relying-party.xml
    ...
        <bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty">
            <property name="profileConfigurations">
                <list>
                    <bean parent="OIDC.Configuration" />
                    <!-- <bean parent="SAML2.SSO" p:encryptAssertions="false" /> -->
                </list>
            </property>
        </bean>
    ...
  • Wenn Ihr IdP so aufgesetzt ist, wie wir es empfehlen, dann wird alles unter dem URL-Pfad /idp/ vom Tomcat bereitgestellt, der Basispfad / hingegen von Apache (Einrichtung weiter unten). Daher müssen Sie den Redirect für /.well-known/openid-configuration in der Konfiguration des virtuellen Hosts im Apache einrichten:
    ...
      Redirect seeother /.well-known/openid-configuration https://idp.example.org/idp/profile/oidc/configuration
    ...
  • Alternativ können die Konfigurationsparameter statisch bereitgestellt werden (erfordert a2enmod headers):
      Alias /.well-known/openid-configuration /opt/shibboleth-idp/static/openid-configuration.json
      <Location "/.well-known/openid-configuration">
        ForceType application/json
        Header set Access-Control-Allow-Origin *
      </Location>
  • Starten oder laden Sie den Webserver neu:
    root@idp:~# systemctl restart apache2
  • Testen Sie, ob Sie die Konfiguration öffentlich oder zumindest von der Relying Party aus abrufen können. Wenn das noch nicht möglich ist, beheben Sie das zuerst, sonst wird die RP nicht mit dem IdP kommunizieren können.
    root@idp:~# curl https://idp.example.org/.well-known/openid-configuration

Profile aktivieren

Aktivieren Sie die entsprechenden OIDC-Profile im Shibboleth IdP, in der Datei conf/relying-party.xml. Um OIDC-Clients manuell hinzuzufügen, fügen Sie in die Abschnitte shibboleth.UnverifiedRelyingParty und shibboleth.DefaultRelyingParty folgende Parameter hinzu. Außerdem sollten auch beim OIDC-Login dieselben Flows wie beim SAML-Login berücksichtigt werden, hier die Anzeige der Nutzungsbedingungen und die Abfrage der Zustimmung zur Attributübertragung. Achtung: Wenn Sie weitere Flows unter postAuthenticationFlows eintragen, etwa context-check, dann berücksichtigen Sie dies ggf. beim Debugging! Hier wird nur die Standardkonfiguration beschrieben.

  • Bearbeiten Sie die Datei /opt/shibboleth-idp/conf/relying-party.xml:
    /opt/shibboleth-idp/conf/relying-party.xml
    ...
        <bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty">
            <property name="profileConfigurations">
                <list>
                    <bean parent="OIDC.Configuration" />
                    <bean parent="OIDC.Keyset" />
                    <!-- <bean parent="SAML2.SSO" p:encryptAssertions="false" /> -->
                </list>
            </property>
        </bean>
    ...
        <!-- Default configuration, with default settings applied for all profiles. -->
        <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
            <property name="profileConfigurations">
                <list>
    ...
                    <bean parent="OIDC.SSO"
                         p:postAuthenticationFlows="#{ {'terms-of-use', 'attribute-release'} }" />
                    <ref bean="OIDC.UserInfo" />
                    <ref bean="OAUTH2.Revocation" />
                    <ref bean="OAUTH2.Introspection" />
                </list>
            </property>
        </bean>
    ...
  • Testen Sie dann, ob Sie das Schlüsselmaterial öffentlich oder zumindest von der Relying Party aus abrufen können.
    root@idp:~# curl https://idp.example.org/idp/profile/oidc/keyset

RP-Metadaten hinterlegen

Da die OIDC-Clients (Relying Parties, RP) ja derzeit noch nicht aus Föderationsmetadaten kommen, werden sie über statische XML-Metadaten hinzugefügt (vgl. OPMetadataClientRegistration und OAuthRPMetadataProfile im Shibboleth-Wiki). Sie gehen dabei genau so bzw. so ähnlich vor, wie Sie sonst auch Metadata Provider hinzufügen. Von den Betreiber*innen der RP benötigen Sie mindestens folgende Werte:

Option 1: xml-Metadaten
/opt/shibboleth-idp/conf/metadata-providers.xml
<MetadataProvider id="ShibbolethMetadata" xsi:type="ChainingMetadataProvider"
    xmlns="urn:mace:shibboleth:2.0:metadata"
    xmlns:resource="urn:mace:shibboleth:2.0:resource"
    xmlns:security="urn:mace:shibboleth:2.0:security"
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd
                        urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd
                        urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd
                        urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd">
...
<!-- für OIDC ohne Dynamic Client Registration muss ein Metadatensatz mit manuell gepflegten Relying Parties hier eingebunden sein. -->
 
    <MetadataProvider id="rp.example.org" xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/rp.example.org.xml" /> 
...
</MetadataProvider>

Die Metadaten legen Sie dann unter dem Pfad ab, den Sie dort eben angegeben haben, im Beispiel /opt/shibboleth-idp/metadata/rp.example.org.xml. Der Inhalt / Beispieldatei:

<md:EntityDescriptor
        xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
        xmlns:oidcmd="urn:mace:shibboleth:metadata:oidc:1.0"
        entityID="https://rp.example.org">
    <md:SPSSODescriptor protocolSupportEnumeration="http://openid.net/specs/openid-connect-core-1_0.html">
        <md:Extensions>
            <oidcmd:OAuthRPExtensions
                grant_types="authorization_code"
                response_types="code"
                token_endpoint_auth_method="client_secret_basic"
                scopes="openid profile email" />
            <mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
                <mdui:DisplayName xml:lang="de">Beispiel-OIDC-RP</mdui:DisplayName>
                <mdui:DisplayName xml:lang="en">Example OIDC RP</mdui:DisplayName>
                <mdui:Description xml:lang="de">Beispiel-OIDC-RP der Hochschule XY.</mdui:Description>
                <mdui:Description xml:lang="en">Example OIDC RP of University XY.</mdui:Description>
                <mdui:InformationURL xml:lang="de">https://rp.example.org</mdui:InformationURL>
                <mdui:InformationURL xml:lang="en">https://rp.example.org</mdui:InformationURL>
                <mdui:Logo height="93" width="260">https://rp.example.org/logo.png</mdui:Logo>
            </mdui:UIInfo>
        </md:Extensions>
        <md:KeyDescriptor>
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <oidcmd:ClientSecret>HIER-STEHT-EIN-GEHEIMES-SECRET</oidcmd:ClientSecret>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:NameIDFormat>urn:mace:shibboleth:metadata:oidc:1.0:nameid-format:public</md:NameIDFormat>
        <md:AssertionConsumerService
                Binding="https://tools.ietf.org/html/rfc6749#section-3.1.2"
                Location="https://rp.example.org/OIDC-ODER-OAUTH-ENDPOINT"
                index="1"/>
    </md:SPSSODescriptor>
    <md:Organization>
        <md:OrganizationDisplayName xml:lang="de">Hochschule XY</md:OrganizationDisplayName>
        <md:OrganizationDisplayName xml:lang="en">University XY</md:OrganizationDisplayName>
        <md:OrganizationURL xml:lang="de">https://www.example.org</md:OrganizationURL>
        <md:OrganizationURL xml:lang="en">https://www.example.org</md:OrganizationURL>
    </md:Organization>
</md:EntityDescriptor>
Option 2: JSON-Metadaten

Um die RP-Metadaten um JSON-Format einzubinden, hinterlegen Sie eine entsprechende Datei mit den Angaben zu einer oder auch gleich mehreren Relying Parties:

/opt/shibboleth-idp/metadata/oidc-client.json
[
    {
        "client_id": "https://rp.example.org",
        "client_secret": "HIER-STEHT-EIN-GEHEIMES-SECRET",
        "response_types": ["code"],
        "grant_types": ["authorization_code"],
        "scope": "openid info profile email",
        "redirect_uris": ["https://rp.example.org/CALLBACK-ENDPOINT"]
    }
]

Aktivieren Sie in conf/oidc-clientinfo-resolvers.xml den Abschnitt, mit dem Sie eine JSON-Datei einlesen lassen können:

    <!-- Kommentarzeichen um die folgenden Zeilen entfernen -->
    <bean id="ExampleFileResolver" parent="shibboleth.oidc.FilesystemClientInformationResolver"
        c:metadata="%{idp.home}/metadata/oidc-client.json" />

Mit dynamischer Client-Registrierung erlaubt Ihr OP auch unverifizierten Relying Parties, sich als Client am OP zu registrieren, also die RP-Konfiguration auf Ihrem IdP einzureichen. Dies hat noch nichts mit Attributfreigaben zu tun. Es bedeutet erstmal nur, dass Sie nicht die einzelnen RP-Metadatensätze hinterlegen, sondern dass der IdP die registrierten Clients in seine Datenbank schreibt.

dynreg-Einstellungen vornehmen

In conf/oidc.properties finden Sie verschiedene Einstellungen zur dynamischen Client-Registrierung. Beginnen Sie wie folgt:

#Dynamic registration properties
# The validity of registration before a new one is required. -> Registrierung soll dauerhaft gespeichert werden.
idp.oidc.dynreg.defaultRegistrationValidity = PT0S
 
# The default scopes accepted in dynamic registration
idp.oidc.dynreg.defaultScope = openid profile email
 
# The default subject type if not set by client in request. Maybe set to pairwise or public.
idp.oidc.dynreg.defaultSubjectType = pairwise
 
# Where to store the dynamically registered client information -> Datenbank
idp.oidc.dynreg.StorageService = shibboleth.JPAStorageService

Profile erlauben

Für unverifizierte (also nicht aus Metadaten bekannte) RPs muss noch ein weiteres Profil erlaubt werden, nämlich die Registrierung. In der Datei conf/relying-party.xml stehen dann folgende Profile:

/opt/shibboleth-idp/conf/relying-party.xml
    <bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <list>
                <!-- dynamic client registration -->
                <bean parent="OIDC.Registration"
                    p:authorizationCodeFlowEnabled="true"
                    p:hybridFlowEnabled="false"
                    p:implicitFlowEnabled="false"
                    />
                <bean parent="OIDC.Configuration" />
                <bean parent="OIDC.Keyset" />
            </list>
        </property>
    </bean>
 
    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <list>
                <bean parent="OIDC.SSO" p:postAuthenticationFlows="#{ {'terms-of-use', 'attribute-release'} }" />
                <ref bean="OIDC.UserInfo" />
                <ref bean="OAUTH2.Revocation" />
                <ref bean="OAUTH2.Introspection" />
            </list>
        </property>
    </bean>

Blick in die Datenbank

Nachdem sich eine Relying Party erfolgreich an Ihrem OP registriert hat, sehen Sie in der IdP-Datenbank, in der Tabelle StorageRecords einen Eintrag:

MariaDB [testidpoidc]> select * from StorageRecords where context = "oidcClientInformation" limit 1\G;
*************************** 1. row ***************************
context: oidcClientInformation
     id: _0d6abed03a4b51fbd6170d7dc1d5c302
expires: NULL
  value: {"grant_types":["refresh_token","authorization_code"],"subject_type":"pairwise","application_type":"web","redirect_uris":["https:\/\/testsp-oidc.aai.dfn.de\/protected\/callback"],"token_endpoint_auth_method":"client_secret_basic","client_id":"_0d6abed03a4b51fbd6170d7dc1d5c302","client_secret_expires_at":0,"scope":"openid profile email","client_id_issued_at":1656398840,"client_secret":"_13ddb8d7b852b3cf323429f50bbc27d4","client_name":"testsp-oidc.aai.dfn.de","contacts":["hotline@aai.dfn.de"],"response_types":["code"],"id_token_signed_response_alg":"RS256"}
version: 1
1 row in set (0.000 sec)

Für OIDC sollten eine globale subject-id und/oder eine anwendungsbezogene pairwise-id definiert werden. Nachfolgend zwei Beispiele:

1. Syntax IdP 4.x

Die Kurzbeschreibungen und Transcoding-Regeln für die Attribute subject-public und subject-pairwise werden momentan nicht mitgeliefert. Legen Sie folgende Datei an und importieren Sie sie in die Attribute Registry:

/opt/shibboleth/conf/attributes/oidc-sub-claims.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">
 
    <!-- https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims -->
 
    <bean parent="shibboleth.TranscodingRuleLoader">
    <constructor-arg>
    <list>
 
        <!-- OIDC sub claims -->
 
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">subject-public</prop>
                    <prop key="transcoder">OIDCStringTranscoder</prop>
                    <prop key="oidc.name">sub</prop>
                    <prop key="displayName.en">Unique ID</prop>
                    <prop key="displayName.de">Eindeutige ID</prop>
                    <prop key="description.en">A unique identifier for a person, mainly for inter-institutional user identification</prop>
                    <prop key="description.de">Eindeutige, einrichtungsübergreifende Benutzeridentifikation</prop>
                </props>
            </property>
        </bean>
 
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">subject-pairwise</prop>
                    <prop key="transcoder">OIDCStringTranscoder</prop>
                    <prop key="oidc.name">sub</prop>
                    <prop key="displayName.en">Pairwise ID</prop>
                    <prop key="displayName.de">Pairwise ID</prop>
                    <prop key="description.en">A unique identifier for a person, different for each service provider</prop>
                    <prop key="description.de">Eindeutige Benutzeridentifikation, unterschiedlich pro Service Provider</prop>
                </props>
            </property>
        </bean>
    </list>
    </constructor-arg>
    </bean>
</beans>

Der Import erfolgt wie gewohnt durch eine Zeile in conf/attributes/default-rules.xml:

<import resource="oidc-sub-claims.xml" />

Dann wird die Subject ID (OIDC: „subject-public“), wie in anderen Beispielen hier im Wiki, mit dem subjectHash belegt, also einem Hash, der aus einem anderen Attribut, z.B. der uid (bzw. hier dem Quellattribut der persistentID) generiert wird. Die Pairwise ID (OIDC: „subject-pairwise“) erhält denselben Wert wie die persistentID (SAML 2 Name ID).

/opt/shibboleth-idp/conf/attribute-resolver.xml
<?xml version="1.0" encoding="UTF-8"?>
<AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:oidc="urn:mace:shibboleth:2.0:resolver:oidc"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd
                            urn:mace:shibboleth:2.0:resolver:oidc http://shibboleth.net/schema/oidc/shibboleth-attribute-encoder-oidc.xsd">
...
    <AttributeDefinition id="subjectHash" xsi:type="ScriptedAttribute" dependencyOnly="true">
        <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
        <Script><![CDATA[
          var digestUtils = Java.type("org.apache.commons.codec.digest.DigestUtils");
          var saltedHash  = digestUtils.sha256Hex(%{idp.persistentId.sourceAttribute}.getValues().get(0) + "%{idp.persistentId.salt}");
          subjectHash.addValue(saltedHash);
        ]]></Script>
    </AttributeDefinition>
 
    <!-- OIDC -->
    <AttributeDefinition id="subject-public" xsi:type="Simple"
            activationConditionRef="shibboleth.oidc.Conditions.PublicRequired">
        <InputAttributeDefinition ref="subjectHash" />
    </AttributeDefinition>
 
    <AttributeDefinition id="subject-pairwise" xsi:type="Simple"
            activationConditionRef="shibboleth.oidc.Conditions.PairwiseRequired">
        <InputDataConnector ref="StoredId" attributeNames="persistentID"/>
    </AttributeDefinition>

2. Syntax IdP 3.x

Wenn Sie einen IdP betreiben, der seit Shibboleth 3.x nie neu installiert, sondern immer aktualisiert wurde, dann haben Sie die Attribute Registry möglicherweise noch nicht in Betrieb genommen. Die Transcoding-Regeln für die Attribute werden dann nicht aus der Registry geholt, sondern sie müssen direkt in der Attribut-Definition genannt werden. Eine Attributdefinition kann dann so aussehen, hier basierend auf den Attributen uid und persistentId:

In der Datei /opt/shibboleth-idp/conf/attribute-resolver.xml Attribute analog zu /opt/shibboleth-idp/conf/examples/oidc-attribute-resolver.xml ergänzen, inkl. der Namespace-Deklaration im allerersten Absatz:

/opt/shibboleth-idp/conf/attribute-resolver.xml
<?xml version="1.0" encoding="UTF-8"?>
<AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:oidc="urn:mace:shibboleth:2.0:resolver:oidc"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd
                            urn:mace:shibboleth:2.0:resolver:oidc http://shibboleth.net/schema/oidc/shibboleth-attribute-encoder-oidc.xsd">
...
    <!-- OIDC subjects -->
    <AttributeDefinition id="subject-public" xsi:type="Simple"
            activationConditionRef="shibboleth.oidc.Conditions.PublicRequired">
        <InputDataConnector ref="myLDAP" attributeNames="uid" />
        <AttributeEncoder xsi:type="oidc:OIDCString" name="sub" />
    </AttributeDefinition>
 
    <AttributeDefinition id="subject-pairwise" xsi:type="Simple"
            activationConditionRef="shibboleth.oidc.Conditions.PairwiseRequired">
        <InputDataConnector ref="myStoredId" attributeNames="persistentId"/>
        <AttributeEncoder xsi:type="oidc:OIDCString" name="sub" />
    </AttributeDefinition>
...

In Datei /opt/shibboleth-idp/conf/attribute-filter.xml weitere Attribute analog zu /opt/shibboleth-idp/conf/examples/oidc-attribute-filter.xml ergänzen:

/opt/shibboleth-idp/conf/attribute-filter.xml
...
    <!-- OIDC scopes -->
    <AttributeFilterPolicy id="OPENID_SCOPE">
        <PolicyRequirementRule xsi:type="oidc:OIDCScope" value="openid" />
        <AttributeRule attributeID="subject-public">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="subject-pairwise">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
    </AttributeFilterPolicy>
 
    <AttributeFilterPolicy id="OPENID_SCOPE_EMAIL">
        <PolicyRequirementRule xsi:type="oidc:OIDCScope" value="email" />
        <AttributeRule attributeID="mail">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="email_verified">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
    </AttributeFilterPolicy>
 
    <AttributeFilterPolicy id="OPENID_SCOPE_PROFILE">
        <PolicyRequirementRule xsi:type="oidc:OIDCScope" value="profile" />
        <AttributeRule attributeID="displayName">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="sn">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="givenName">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="uid">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="gender">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="eduPersonPrincipalName">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
    </AttributeFilterPolicy>
...

Die ID- und UserInfo-Tokens, die bei OIDC 1.0 zum Einsatz kommen, müssen signiert sein und über eine TLS-verschlüsselte Verbindung übertragen werden. Sie darüber hinaus auch noch zu verschlüsseln, ist optional. Wir empfehlen, zunächst ein funktionierendes Setup ohne die optionale Token-Verschlüsselung fertigzustellen und diesen Schritt, so er gewünscht ist, erst dann in Angriff zu nehmen.

Die Token-Verschlüsselung schalten Sie mit dieser Einstellung von conf/oidc.properties an:

/opt/shibboleth-idp/conf/oidc.properties
# Set false to preclude issuing unencrypted ID/UserInfo tokens without specific overrides
# default: idp.oidc.encryptionOptional = true
idp.oidc.encryptionOptional = false

Das Profil OIDC.SSO in conf/relying-party.xml muss um die entsprechende Property ergänzt werden:

/opt/shibboleth-idp/conf/relying-party.xml
    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <list>
                <bean parent="OIDC.SSO" p:postAuthenticationFlows="#{ {'terms-of-use', 'attribute-release'} }" p:encryptionOptional="false" />
                <ref bean="OIDC.UserInfo"/>
                <ref bean="OAUTH2.Revocation"/>
                <ref bean="OAUTH2.Introspection" />
            </list>
        </property>
    </bean>

In einem Setup ohne dynamische Client-Registrierung müssen die hinterlegten RP-Metadaten um Informationen zu Signaturalgorithmen und Schlüsselmaterial ergänzt werden. Bei dynamischer Client-Registrierung sollte eine Relying Party diese Informationen bei der Registrierung selbst an den OP schicken.

/opt/shibboleth-idp/metadata/oidc-client.json
[
    {
        "client_id": "https://rp.example.org",
        "client_secret": "HIER-STEHT-EIN-GEHEIMES-SECRET",
        "response_types": ["code"],
        "grant_types": ["authorization_code"],
        "scope": "openid info profile email",
        "redirect_uris": ["https://rp.example.org/CALLBACK-ENDPOINT"],
        "id_token_signed_response_alg":"RS256",
        "id_token_encrypted_response_alg":"RSA1_5",
        "id_token_encrypted_response_enc":"A256GCM",
        "userinfo_encrypted_response_alg":"RSA1_5",
        "userinfo_encrypted_response_enc":"A256GCM",
        "jwks_uri": "https://rp.example.org/CALLBACK-ENDPOINT?jwks=rsa"
    }
]

FIXME: Funktionierende Syntax für xml-Metadaten fehlt

Achtung: Sollten Sie später die Token-Verschlüsselung wieder aus der Konfiguration herausnehmen, achten Sie darauf, dass auf beiden Seiten auch die Informationen zu Signaturalgorithmen und Schlüsselmaterial aus den Metadaten der Gegenstelle entfernt werden müssen, sonst wird weiterhin versucht, die Tokens zu verschlüsseln.

Das OIDC-Plugin unterstützt bislang noch kein Logout, und die OIDC-RPs werden auch nicht auf der Logout-Seite angezeigt: https://issues.shibboleth.net/jira/browse/JOIDC-13

Die OIDC-Attribute werden im Attribute Release bislang nur mit englischen Beschreibungstexten angezeigt. Zur Korrektur müssen die entsprechenden deutschen Beschreibungstexte unter /opt/shibboleth-idp/conf/attributes/oidc-claim-rules.xml ergänzt werden.

Ihren OpenID Provider können Sie mit unseren öffentlichen Relying Parties testen. Alle unsere Testsysteme sind momentan nur für dynamische Client-Registrierung konfiguriert.

FIXME: to be continued…

  • idp-process.log lesen. DEBUG-Loglevel anschalten.
  • InvalidProfileConfiguration-Fehler? → conf/relying-party.xml muss korrigiert werden (s.o.)
  • standardisierte RequestURI abfragen:
    curl -I https://idp.example.org/.well-known/openid-configuration
    HTTP/1.1 303 See Other
    Location: https://idp.example.org/idp/profile/oidc/configuration
  • eigentliche OP-Metadaten abfragen:
    curl -s https://idp.example.org/idp/profile/oidc/configuration
  • Schlüsselmaterial des OpenID Connect Providers abfragen:
    curl -s https://idp.example.org/idp/profile/oidc/keyset | python -m json.tool
  • Schlüsselmaterial der Relying Party abfragen, URL steht in RP-Metadaten (Datei oder OP-Datenbank):
    curl -s https://rp.example.org/protected/callback?jwks=rsa | python3 -m json.tool
  • Fehler bei Generierung eines validen Sub Claims?
    • genaue Prüfung der Dateien:
    • conf/services.xml → Wird die Attribute Registry benutzt oder nicht? Sind die Sub Claims dann in die Registry importiert?
    • Sind sie in conf/attribute-resolver.xml definiert?
    • Ist in conf/attribute-resolver.xml oben im einleitenden xml-Tag <AttributeResolver …> oidc mit drin?
    • Freigaberegeln in conf/attribute-filter.xml prüfen
    • Log checken: Welcher Sub Claim wird von der RP angefordert? Haben Sie vielleicht nur den anderen Sub Claim erlaubt?
  • Zuletzt geändert: vor 11 Monaten