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.
Installation der Plugins
Plugins herunterladen
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
Installation über HTTP-Proxy
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
Installation ohne HTTP-Proxy
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
Konfiguration
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.
Allgemeine Vorbereitung
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 3072 -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 3072 -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üridp.example.org
ausgetauscht werden. (Ja, muss es. Das ist kein Jinja-Template.) In derselben Datei sollten Sie nicht unterstützte Scopes aus dem Abschnittscopes_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
Einzelne Relying Parties auf dem OP registrieren
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:
- eine Client ID (im Beispiel https://rp.example.org)
- das Client Secret
- die Redirect URI für den Callback
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" />
OP mit dynamischer Client-Registrierung
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 = JDBCStorageService
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)
Attribute / Claims
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> ...
Token-Verschlüsselung
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" } ]
: 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.
Weitere Hinweise
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
Ab der Version 4.1 wird jetzt auch OPLogout unterstützt. Näheres dazu findet sich unter https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/3466559581/OPLogout
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.
Teststellung
Ihren OpenID Provider können Sie mit unseren öffentlichen Relying Parties testen. Alle unsere Testsysteme sind momentan nur für dynamische Client-Registrierung konfiguriert.
Troubleshooting
: 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?