Cette page fournit une présentation générale du Building Block SAML (Security Assertion Markup Language) 2.0 ainsi que des problèmes de connexion unique (SSO) et de dépannage courants pour le fournisseur d'authentification SAML.

Si, pour une raison quelconque, un fichier XML de métadonnées du fournisseur d'identité mis à jour/nouveau est téléchargé dans l'interface graphique de Blackboard Learn sur la page Paramètres d'authentification SAML dans la section Paramètres du fournisseur d'identité pour un fournisseur d'authentification SAML, SAML B2 et ce fournisseur d'authentification doivent également être basculés sur Inactif/Disponible, tandis que le fournisseur d'authentification SAML doit présenter l'état « Actif », pour s'assurer que toutes les métadonnées du fournisseur d'identité mises en cache sont effacées et que les métadonnées du fournisseur d'identité mises à jour sont pleinement utilisées.


Termes clés

Les termes et abréviations suivants sont utilisés dans ce guide :

  • SAML : Abréviation de Security Assertion Markup Language
  • IdP : Fournisseur d'identité
  • SP : Fournisseur de services
  • ADFS : Active Directory Federation Services
  • Interface utilisateur : Interface graphique utilisateur Dans le contexte de Blackboard Learn, cela signifie travailler dans le logiciel.

Modifier les paramètres de configuration SAML

Pour aider à résoudre les problèmes d'authentification SAML, le Building Block SAML a été mis à jour vers la version 3200.2.0 pour inclure ces paramètres et option de configuration :

  • Définir la limite d'âge de la session SAML
  • Choisir un type d'algorithme de signature
  • Régénérer les certificats
  • Modifier la valeur de ResponseSkew

En savoir plus sur la configuration de paramètres dans le Building Block SAML


Erreurs et exceptions

Les erreurs/exceptions liées à SAML sont capturées dans les journaux suivants :

  • /usr/local/blackboard/logs/bb-services-log.txt
  • /usr/local/blackboard/logs/tomcat/stdout-stderr-<date>.log
  • /usr/local/blackboard/logs/tomcat/catalina-log.txt

Lors de la recherche d'un problème d'authentification SAML signalé, ces journaux doivent être examinés avec soin.


SAML Tracer

Avec les itérations de dépannage de l'authentification SAML 2.0, il peut être nécessaire à un moment donné de confirmer/visualiser les attributs qui sont réellement libérés du fournisseur d'identité et envoyés à Learn pendant le processus d'authentification. Si les attributs du fournisseur d'identité NE sont PAS chiffrés dans la réponse SAML, le module complémentaire SAML Tracer du navigateur Firefox ou le module complémentaire SAML Message Decoder du navigateur Chrome permet de visualiser les attributs.


Attribut mappé de façon incorrecte

Si l'attribut contenant userName est mappé de façon incorrecte comme indiqué dans le champ ID utilisateur distant de la section Mapper les attributs SAML de la page Paramètres d'authentification SAML dans l'interface graphique de Blackboard Learn, l'événement suivant est consigné dans le journal bb-services lors de la tentative de connexion à Blackboard Learn au moyen de l'authentification SAML :

2016-06-28 12:48:12 -0400 - userName is null or empty

Un message Erreur de connexion similaire s'affiche dans le navigateur : Blackboard Learn ne peut actuellement pas se connecter à votre compte en utilisant la connexion unique. Contactez votre administrateur pour obtenir de l'aide.

An image of a Sign On Error message displayed in the browser that says Blackboard Learn is currently unable to log into your account using single-sing on. Contact your administrator for assistance.

Une entrée Authentication Failure s'affiche dans le journal bb-services :

2016-06-28 12:48:12 -0400 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Authentication Failure
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.checkAuthenticationResult(BbAuthenticationSuccessHandler.java:81)
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.onAuthenticationSuccess(BbAuthenticationSuccessHandler.java:57)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:245)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
[SNIP]

Solution

Vous pouvez résoudre le problème de deux façons différentes. Sélectionnez tout d'abord l'option Créer des comptes s'ils n'existent pas dans le système dans la page Paramètres d'authentification SAML de l'interface utilisateur de Blackboard Learn. Vous pouvez également essayer d'afficher la valeur des attributs libérés par le fournisseur d'identité via le traceur SAML ou la journalisation du débogage à condition que les attributs NE soient PAS chiffrés :

<saml2:Attribute Name="urn:oid:0.9.2342.19200300.100.1.3">
    <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                          xsi:type="xs:anyType"
                          >bbuser_saml2@bbchjones.net</saml2:AttributeValue>
</saml2:Attribute>

et mapper l'attribut Attribute Name possédant la valeur d'attribut AttributeValue à l'ID utilisateur distant sur la page Paramètres d'authentification SAML de l'interface graphique de Blackboard Learn.


Source de données compatible non sélectionnée

Les utilisateurs ne peuvent pas se connecter à Blackboard Learn au moyen de l'authentification SAML si la source de données des utilisateurs n'est pas sélectionnée dans la section Paramètres du fournisseur de services > Sources de données compatibles de la page Paramètres d'authentification SAML dans l'interface graphique de Blackboard Learn. L'événement suivant sera consigné dans le journal bb-services lors de la tentative de connexion à Blackboard Learn au moyen de l'authentification SAML :

2016-09-23 12:33:13 -0500 - userName is null or empty

Le message Erreur de connexion s'affiche dans le navigateur, tandis que Authentication Failure (Erreur d'authentification) est consigné dans le journal bb-services :

2016-09-23 12:33:13 -0500 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Authentication Failure
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.checkAuthenticationResult(BbAuthenticationSuccessHandler.java:82)
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.onAuthenticationSuccess(BbAuthenticationSuccessHandler.java:58)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:245)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter (SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor3399.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Solution

  1. Obtenez le nom d'un utilisateur qui ne parvient pas à se connecter.
  2. Dans l'interface graphique de Blackboard Learn, accédez à Administrateur système > Utilisateurs et recherchez l'utilisateur.
  3. Copiez la clé de source de données de l'utilisateur.
  4. Accédez à Administrateur système > Authentification > « Nom du fournisseur » > Paramètres SAML > Sources de données compatibles.
  5. Cochez l'option Source de données dans la colonne Nom, puis sélectionnez Valider.

Message d'erreur « Given URL is not well formed » (URL fournie mal formée)

Si OneLogin est configuré en tant que fournisseur d'identité pour le fournisseur d'authentification SAML dans Blackboard Learn, il se peut que le message Given URL is not well formed (URL fournie mal formée) s'affiche dans la page après la saisie des données d'authentification OneLogin lors de la tentative de connexion à Blackboard Learn.

Les entrées ci-dessous sont consignées dans le fichier bb-services-log :

2016-09-16 09:43:40 -0400 - Given URL is not well formed<P><span class="captionText">For reference, the Error ID is 17500f44-7809-4b9f-a272-3bed1d1af131.</span> - java.lang.IllegalArgumentException: Given URL is not well formed
    at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:120)
    at org.opensaml.util.SimpleURLCanonicalizer.canonicalize(SimpleURLCanonicalizer.java:87)
    at org.opensaml.common.binding.decoding.BasicURLComparator.compare(BasicURLComparator.java:57)
    at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.compareEndpointURIs(BaseSAMLMessageDecoder.java:173)
    at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:213)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:80)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.MalformedURLException: no protocol: {recipient}
    at java.net.URL.<init>(URL.java:593)
    at java.net.URL.<init>(URL.java:490)
    at java.net.URL.<init>(URL.java:439)
    at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:77)
        ... 203 more

Solution

  1. Activez le traceur SAML du navigateur Firefox et répliquez le problème de connexion.
  2. Passez en revue le début de l'événement POST SAML :

    <samlp:Response Destination="{recipient}"
            ID="R8afbfbfee7292613f98ad4ec4115de7c6b385be6"
            InResponseTo="a3g2424154bb0gjh3737ii66dadbff4"
            IssueInstant="2016-09-16T18:49:09Z"
            Version="2.0"
            xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
            xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
            >
        <saml:Issuer>https://app.onelogin.com/saml/metadata/123456</saml:Issuer>
        [SNIP]

  3. À la ligne 1 comportant Response, vous pouvez constater que Destination= est défini sur « recipient » uniquement.
  4. Faites en sorte que le client accède à la section Configuration de leur fournisseur d'identité OneLogin.
  5. Assurez-vous que le champ Recipient est vide.
  6. Copiez la valeur de ACS (Consumer) URL, collez-la dans le champ Recipient et sélectionnez Enregistrer.

Scénarios de problèmes de fournisseur d'identité/fournisseur de services

  1. Si une erreur apparaît avant que vous ne soyez redirigé vers la page de connexion du fournisseur d'identité, les métadonnées du fournisseur d'identité peuvent ne pas être valides.
  2. Si une erreur apparaît avant que vous ne soyez redirigé vers la page de connexion du fournisseur d'identité, cela peut être dû aux raisons suivantes :
    1. Le mappage d'attribut entre le fournisseur de services et le fournisseur d'identité est incorrect ou le fournisseur d'identité n'a pas renvoyé un ID utilisateur distant valide.
    2. La réponse SAML du fournisseur d'identité n'a pas été validée par le fournisseur de services. Les causes possibles de l'erreur sont les suivantes :
      • Le fournisseur d'identité signe la réponse SAML avec un certificat qui n'est pas émis par une autorité de certification valide, et le magasin de clés du fournisseur de services ne contient pas ce certificat.
      • L'horloge système du fournisseur de services est incorrecte.

Active Directory Federation Services (ADFS)

Les noms d'attribut sont sensibles à la casse dans la section Mapper les attributs SAML de la page Paramètres d'authentification SAML de l'interface graphique de Blackboard Learn. Par exemple, si l'ID utilisateur distant présente sAMAccountName comme nom de l'attribut sur la page de paramètres et que la requête POST SAML du fournisseur d'identité indique ce qui suit pour le nom de l'attribut dans AttributeStatement :

<AttributeStatement>
    <Attribute Name="SamAccountName>
        <AttributeValue>Test-User</AttributeValue>
    </Attribute>
</AttributeStatement>

L'utilisateur ne pourra pas se connecter. La valeur du nom d'attribut ID utilisateur distant figurant sur la page Paramètres d'authentification SAML doit passer de sAMAccountName à SamAccountName.

Message d'avertissement « Ressource introuvable » ou « Erreur de connexion »

Cette section contient certains problèmes courants qui peuvent empêcher un utilisateur de se connecter à Learn via l'authentification SAML avec ADFS lorsque le message La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder ou Erreur de connexion s'affiche dans l'interface graphique de Blackboard Learn.

Problème n° 1

Après la saisie des informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder.

A specified resource was not found error message

Le message suivant est consigné dans le journal stdout-stderr :

INFO | jvm 1 | 2016/06/22 06:08:33 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'

Le problème se produit car la méthode noHandlerFound() est utilisée dans le code DispatcherServlet.java et elle est incapable de localiser/mapper la demande d'authentification unique HTTP.

/**
 * No handler found -> set appropriate HTTP response status.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception if preparing the response failed
 */
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
 if (pageNotFoundLogger.isWarnEnabled()) {
  pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
    "] in DispatcherServlet with name '" + getServletName() + "'");
 }
 if (this.throwExceptionIfNoHandlerFound) {
  throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
    new ServletServerHttpRequest(request).getHeaders());
 }
 else {
  response.sendError(HttpServletResponse.SC_NOT_FOUND);
 }
}

Solution

Cela se produit lorsque l'ID d'entité du fournisseur de services configuré dans l'interface graphique de Blackboard Learn est incorrect. Pour y remédier, accédez à Administrateur système > Authentification > Paramètres d'authentification SAML > Paramètres du fournisseur de services et mettez à jour l'ID d'entité. Pour ADFS, la configuration par défaut de l'ID d'entité serait https://[Learn Server Hostname]/auth-saml/saml/SSO.

Si un établissement remplace son URL par défaut https://etablissement.blackboard.com par https://mon.etablissement.edu, l'ID d'entité de l'interface graphique de Blackboard Learn dans la page Paramètres d'authentification SAML doit être mis à jour sur https://mon.etablissement.edu/auth-saml/saml/SSO.

Problème n° 2

Après la saisie des informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder.

Le message suivant est consigné dans le journal stdout-stderr :

INFO  | jvm 1  | 2016/06/22 06:08:33 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'

Le message suivant est consigné dans le journal catalina :

ERROR 2016-06-27 10:47:03,664 connector-6: userId=_2_1, sessionId=62536416FB80462298C92064A7022E50 org.opensaml.xml.encryption.Decrypter - Error decrypting the encrypted data element
org.apache.xml.security.encryption.XMLEncryptionException: Illegal key size
Original Exception was java.security.InvalidKeyException: Illegal key size
    at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(XMLCipher.java:1822)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:596)
    at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:795)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:535)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:453)
    at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:414)
    at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
    at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:199)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:82)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
        [SNIP]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
    at javax.crypto.Cipher.init(Cipher.java:1393)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(XMLCipher.java:1820)
        ... 205 more

Le message suivant est consigné dans le journal bb-services :

2016-06-27 10:47:03 -0400 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor3422.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at sun.reflect.GeneratedMethodAccessor3421.invoke(Unknown Source)
        [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:229)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 229 more

Le problème se produit car, par défaut, ADFS chiffre les attributs qu'il envoie à l'aide d'AES-256 et le moteur d'exécution Java utilisé par Blackboard Learn ne prend pas en charge AES-256 par défaut.

Solution

Une solution consiste à ouvrir un PowerShell sur le serveur ADFS et à régler le programme de confiance créé pour Blackboard Learn afin d'envoyer les attributs sous une forme non chiffrée. Comme la communication s'appuie entièrement sur SSL, cette opération ne réduira pas la sécurité de l'authentification. Elle facilite également le débogage de tous les problèmes, car les attributs peuvent être visualisés en utilisant des outils de débogage tels que le module complémentaire SAML tracer du navigateur Firefox ; le redémarrage du système Blackboard Learn n'est pas nécessaire. Pour définir la partie de confiance créée pour Blackboard Learn afin d'envoyer les attributs en clair, ouvrez un PowerShell et exécutez la commande suivante, en remplaçant TargetName par le nom de Relying Party Trust (Approbation de partie de confiance) figurant dans Trust Relationships > Relying Party Trusts (Relations d'approbation > Approbations de partie de confiance) dans la console de gestion ADFS.

set-ADFSRelyingPartyTrust –TargetName "yourlearnserver.blackboard.com" –EncryptClaims $False

Après cette modification, il est nécessaire de redémarrer le service ADFS à l'aide de la commande suivante : Restart-Service ADFSSRV

Problème n° 3

Après la saisie des informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder ou bien il s'agit d'une erreur de connexion

Avec l'une ou l'autre erreur, des événements SAML similaires à ceux qui figurent ci-dessous sont consignés dans le journal stdout-stderr :

INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - No HttpSession currently exists
INFO   | jvm 1    | 2016/09/06 20:33:04 | - No SecurityContext was available from the HttpSession: null. A new one will be created.
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Checking match of request : '/saml/login'; against '/saml/login/**'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 1 in additional filter chain; firing Filter: 'SAMLEntryPoint'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Request for URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Request for URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContextHolder now cleared, as request processing completed
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/09/06 20:33:07 | - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@6708a718. A new one will be created.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Authentication attempt using org.springframework.security.saml.SAMLAuthenticationProvider
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Forwarding to /
INFO   | jvm 1    | 2016/09/06 20:33:07 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/09/06 20:33:07 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Successfully completed request
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Skip invoking on
INFO   | jvm 1    | 2016/09/06 20:33:07 | - SecurityContextHolder now cleared, as request processing completed

Ou ces exceptions SAML similaires dans le fichier log bb-services :

2016-11-29 09:04:24 -0500 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor853.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at sun.reflect.GeneratedMethodAccessor853.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        [SNIP]
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677)
    at blackboard.tomcat.valves.LoggingRemoteIpValve.invoke(LoggingRemoteIpValve.java:44)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response issue time is either too old or with date in the future, skew 60, time 2016-11-29T14:03:16.634Z
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:126)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 230 more

Le problème se produit lorsque la dérive temporelle du serveur ADFS et du serveur d'applications Blackboard Learn est proche de la valeur par défaut de 60 secondes (ou supérieure à celle-ci).

Solution

Le problème peut être résolu de deux façons différentes :

  1. Synchronisation manuelle des horloges des serveurs d'applications Blackboard Learn et du serveur ADFS. Pour Blackboard Learn, l'heure et le fuseau horaire actuels du serveur peuvent être affichés dans un navigateur Web en ajoutant /webapps/portal/healthCheck à la fin d'une URL Blackboard Learn.

    Exemple : https://mhtest1.blackboard.com//webapps/portal/healthCheck

    Hostname (Nom d'hôte) : ip-10-145-49-11.ec2.internal
    Status: Active - Database connectivity established
    Running since: Sat, Dec 3, 2016 - 05:39:11 PM EST
    Time of request: Thu, Dec 8, 2016 - 05:12:43 PM EST

    Un établissement peut utiliser l'URL ci-dessus pour comparer le fuseau horaire et l'horloge du système Blackboard Learn avec ceux de son serveur ADFS, puis ajuster ces éléments si nécessaire sur le serveur ADFS afin qu'ils soient synchronisés avec le site Blackboard Learn.

Problème n° 4

Après la saisie des informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder ou bien il s'agit d'une erreur de connexion

Les exceptions suivantes sont consignées dans le fichier log bb-services :

2016-11-01 12:47:19 -0500 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor929.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 230 more
 2016-11-01 12:47:19 -0500 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Unsuccessful Authentication
         at blackboard.auth.provider.saml.customization.filter.BbSAMLProcessingFilter.unsuccessfulAuthentication(BbSAMLProcessingFilter.java:31)
         at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
         at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
         at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
         at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
         at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
         at sun.reflect.GeneratedMethodAccessor929.invoke(Unknown Source)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
         at java.lang.reflect.Method.invoke(Method.java:498)
         at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
         at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
         at java.security.AccessController.doPrivileged(Native Method)
         at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
         at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
         at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
         at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
         at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
         at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
         at java.security.AccessController.doPrivileged(Native Method)
         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
         at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
 [SNIP]

Solution
  1. Accédez au panneau de configuration de l'administrateur.
  2. Sous Building Blocks, sélectionnez Building Blocks.
  3. Sélectionnez Outils installés.
  4. Situez Fournisseur d'authentification - SAML dans la liste. Ouvrez le menu et sélectionnez Paramètres.
  5. Sous Paramètres d'algorithme de signature, sélectionnez SHA-256 dans la liste. Lorsque vous avez sélectionné le type d'algorithme de signature, redémarrez le Building Block SAML pour appliquer les nouveaux paramètres.
  6. Sélectionnez Soumettre pour enregistrer vos modifications.

Problème n° 5

Après la saisie des informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder ou bien il s'agit d'une erreur de connexion

Les exceptions suivantes sont consignées dans le fichier log bb-services :

2017-01-04 22:52:58 -0700 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor935.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    [SNIP]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: NameID element must be present as part of the Subject in the Response message, please enable it in the IDP configuration
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:252)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 214 more

Comme indiqué dans l'exception SAML ci-dessus, l'élément NameID est absent de Subject (Objet) dans le message Response (Réponse). Le problème se produit généralement lorsque l'élément NameID n'est pas défini en tant que Outgoing Claim Type (Type de revendication sortante) dans Claims Rule (Règle de revendications) pour Relying Party Trust (Approbation de partie de confiance) sur le fournisseur d'identité ADFS de l'établissement ou bien Claims Rule (Règle de revendications) de l'élément NameID ne figure pas dans l'ordre approprié pour Relying Party Trust (Approbation de partie de confiance) sur le fournisseur d'identité ADFS de l'établissement, lequel à son tour entraîne l'absence de l'élément NameID dans le champ Subject (Objet) du message de réponse.

Par exemple : L'élément NameID est manquant

<Subject>
    <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="a22ai8iig0f75ae22hd28748b12da50"
                                 NotOnOrAfter="2017-01-03T05:57:58.234Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
    </SubjectConfirmation>
</Subject>

Par exemple : L'élément NameID est présent

<Subject>
    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testadfs</NameID>
    <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="a5903d39if463ea87ieiab5135j9ji"
                                 NotOnOrAfter="2017-01-05T04:33:12.715Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
    </SubjectConfirmation>
</Subject>

Le module complémentaire SAML tracer de Firefox permet de visualiser l'objet du message de réponse.

Solution

Ce problème peut être résolu de trois façons différentes.

  1. Assurez-vous que les instructions du Guide de configuration SAML B2 pour ADFS ont été correctement suivies et effectuez les modifications nécessaires pour transformer une revendication entrante de la partie de confiance relative à l'approbation de la partie de confiance pour leur fournisseur d'identité ADFS :
    1. Sélectionnez Edit Claims Rule (Modifier la règle de revendications).
    2. Sélectionnez Add Rule (Ajouter une règle).
    3. Sur la page Select Rule Template (Sélectionner un modèle de règle), sélectionnez Transform an Incoming Claim (Transformer une revendication entrante) pour le modèle de règle de revendication, puis sélectionnez Next (Suivant).
    4. Sur la page Configure Rule (Configurer la règle), dans le champ Claim rule name (Nom de la règle de revendication), saisissez une valeur dans Transform Email to Name ID (Transformer l'e-mail en ID de nom).
    5. Le type de revendication entrante doit être SamAccountName (doit correspondre au Outgoing Claim Type (Type de revendication sortante) créé initialement dans la règle Transform Username to NameID [Transformer le nom d'utilisateur en ID de nom]).
    6. La valeur de Outgoing claim type (Type de revendication sortante) est Name ID (Code de nom).
    7. Le format Outgoing name ID (ID du nom sortant) est Email (E-mail).
    8. Vérifiez que l'option Pass through all claim values (Transmettre toutes les valeurs des revendications) est sélectionnée, puis sélectionnez Finish (Terminer).
    9. Cliquez une première fois sur OK pour enregistrer la règle, puis une seconde fois pour terminer les mappages d'attributs.
  2. Assurez-vous que l'ordre des Claims Rules (Règles de revendication) utilisées pour le fournisseur d'identité ADFS dont la règle possède l'élément NameID n'a pas de règles optionnelles avant lui.
  3. En cas d'utilisation d'un attribut personnalisé, assurez-vous que l'élément NameID figure dans Relying Party Trust (Approbation de partie de confiance) puisque Learn attend toujours que le fournisseur d'identité ADFS émette une valeur NameID.

Problème n° 6

Lorsque l'utilisateur est connecté à Blackboard Learn via l'authentification SAML, il tente de se déconnecter en cliquant sur le bouton Se déconnecter dans la partie gauche de la page, puis clique sur le bouton Mettre fin à la session SSO ; le message Erreur de connexion s'affiche alors immédiatement.

Sign On Error!
Blackboard Learn ne peut actuellement pas se connecter à votre compte en utilisant la connexion unique. Contactez votre administrateur pour obtenir de l'aide.
Pour information, le code d'erreur est [ID erreur].

L'exception suivante est consignée dans le fichier log bb-services :

2017-05-08 15:10:46 -0400 - BbSAMLExceptionHandleFilter Error Id: f3299757-8d4e-4fab-98cf-49cd99f4891e - javax.servlet.ServletException: Incoming SAML message failed security validation
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:145)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.doFilter(SAMLLogoutProcessingFilter.java:104)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    [SNIP]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.ws.security.SecurityPolicyException: Validation of request simple signature failed for context issuer
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.doEvaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:139)
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.evaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:103)
    at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:131)
    ... 244 more

L'erreur se produit en raison de la présence du paramètre Single Logout Service Type sur la page Paramètres SAML.

Solution

Le paramètre doit être configuré dans Blackboard Learn et sur le serveur ADFS.

Dans le cas d'ADFS en tant que fournisseur d'identité, sélectionnez le paramètre Post (Publier) uniquement et supprimez Redirect endpoint (Point d'arrêt de redirection) de Relying Party Trust (Approbation de partie de confiance) de l'instance Learn sur le serveur ADFS.

  1. Dans Learn, accédez à Administrateur > Authentification > (Nom du fournisseur) > Paramètres SAML > Type de service de déconnexion simple.
  2. Sélectionnez Publier et décochez la case Réorienter.
  3. Dans le serveur ADFS, accédez à Relying Party Trust (Approbation de partie de confiance) pour l'instance de Blackboard Learn.
  4. Sélectionnez Properties > Endpoints (Propriétés > Points d'arrêt). Deux points d'arrêt de déconnexion SAML sont répertoriés.
  5. Supprimez le point d'arrêt Redirect (Redirection). Sélectionnez Remove Endpoint (Supprimer le point d'arrêt) en vue de sa suppression, puis cliquez sur Apply (Appliquer) et OK.

Après que vous avez effectué les modifications ci-dessus dans Learn et le serveur ADFS, le bouton Mettre fin à la session SSO permettra de déconnecter l'utilisateur.

Problème n° 7

Une fois les données d'authentification saisies dans la page de connexion ADFS, le message Erreur de connexions'affiche lors de la redirection vers Learn.

L'exception SAML suivante est consignée dans le fichier log bb-services :

2017-05-26 07:39:30 -0400 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
        at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
        at blackboard.auth.provider.saml.customization.filter.BbSAMLProcessingFilter.attemptAuthentication(BbSAMLProcessingFilter.java:46)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
        at sun.reflect.GeneratedMethodAccessor380.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
        at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
        at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
        at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
        at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
        at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
        at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:37)
    [SNIP]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
        at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113)
        at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:56)
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 247 more

Solution

À partir de Blackboard Learn 3200.0.0, il est désormais possible de régénérer le certificat de chiffrement SAML en accédant à Administrateur système > Building Blocks > Fournisseur d'authentification - SAML > Paramètres > Régénérer le certificat. Le message Erreur de connexion peut survenir si le bouton Régénérer les certificats est sélectionné après le téléchargement des métadonnées du fournisseur de services dans Relying Party Trust (Approbation de partie de confiance) du site Learn sur le serveur ADFS. Pour résoudre le problème :

  1. Accédez à Administrateur système > Authentification > [Nom du fournisseur SAML] > Paramètres SAML.
  2. Sélectionnez Générer en regard de Métadonnées du fournisseur de services pour enregistrer le nouveau fichier de métadonnées.
  3. Accédez au serveur ADFS et téléchargez les métadonnées du nouveau fournisseur de services dans Relying Party Trust (Approbation de partie de confiance) sur votre site Learn.

Si vous générez un nouveau certificat sous les paramètres de B2, vous devez basculer le fournisseur SAML B2 sur Inactif puis de nouveau sur Actif pour imposer la modification. Après quoi, vous pouvez revenir aux paramètres du fournisseur et générer les nouvelles métadonnées à importer dans le fournisseur d'identité. Si vous ne basculez pas les paramètres, l'ancien certificat peut encore être inclus lorsque vous générez de nouvelles métadonnées. Le fournisseur d'identité ne sera pas mis à jour et au prochain redémarrage de Learn il présentera le nouveau certificat. L'authentification SAML sera interrompue en raison de cette incompatibilité.

Métadonnées de fédération

Puisque les métadonnées d'une fédération ADFS (Active Directory Federation Services) généralement situées dans https://[ADFS Server Hostname]/FederationMetadata/2007-06/FederationMetadata.xml comprennent un élément incompatible avec SAML 2.0, il est nécessaire de modifier les métadonnées afin de supprimer l'élément incompatible avant leur téléchargement dans la section Paramètres du fournisseur d'identité de la page Paramètres d'authentification SAML dans l'interface graphique de Blackboard Learn. En cas de téléchargement des métadonnées avec l'élément incompatible, une erreur se produit lors de la sélection du lien de connexion SAML sur la page de connexion de Blackboard Learn : Métadonnées introuvables pour l'entité [entity] et le rôle {}. Pour information, le code d'erreur est [ID erreur].

La trace de pile Java correspondante pour l'ID d'erreur est la suivante dans le journal bb-services :

2016-06-21 11:42:51 -0700 - Metadata for entity https://<Learn Server Hostname>/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn't found<P><span class="captionText">For reference, the Error ID is c99511ae-1162-4941-b823-3dda19fea157.</span> - org.opensaml.saml2.metadata.provider.MetadataProviderException: Metadata for entity https://ulvsso.laverne.edu/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn't found
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntity(SAMLContextProviderImpl.java:319)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalContext(SAMLContextProviderImpl.java:216)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalAndPeerEntity(SAMLContextProviderImpl.java:126)
    at org.springframework.security.saml.SAMLEntryPoint.commence(SAMLEntryPoint.java:146)
    at org.springframework.security.saml.SAMLEntryPoint.doFilter(SAMLEntryPoint.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Solution

Étant donné que l'emplacement de métadonnées par défaut pour une fédération ADFS est https://[ADFS server hostname]/FederationMetadata/2007-06/FederationMetadata.xml :

  1. Téléchargez ce fichier, puis ouvrez-le dans un éditeur de texte. Supprimez soigneusement la section commençant par <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> ... </X509Data></KeyInfo> et se terminant par </ds:Signature>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <ds:Reference URI="#_43879f32-9a91-4862-bc87-e98b85b51158">
       <ds:Transforms>
        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
       </ds:Transforms>
       <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
       <ds:DigestValue>z1H1[SNIP]jaYM=</ds:DigestValue>
      </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue> FVj[SNIP]edrfNKWvsvk5A==
      </ds:SignatureValue>
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
       <X509Data>
        <X509Certificate>
        FDdd[SNIP]qTNKdk5F/vf1AocDaX
        </X509Certificate>
       </X509Data>
      </KeyInfo>
    </ds:Signature>

  2. Téléchargez le fichier XML de métadonnées mis à jour dans l'interface graphique de Blackboard sur la page Paramètres d'authentification SAML dans la section Paramètres du fournisseur d'identité.
  3. Basculez le fournisseur d'authentification SAML et SAML B2 sur Inactif/Disponible, et assurez-vous que le fournisseur d'authentification SAML présente le statut « Actif ».

Si un établissement teste l'authentification SAML sur un site Blackboard Learn et que plusieurs fournisseurs d'authentification SAML partagent le même fichier XML de métadonnées du fournisseur d'identité ADFS sous-jacent sur le site Blackboard Learn, même si les autres fournisseurs d'authentification SAML sont désactivés, ils doivent également disposer du fichier XML de métadonnées mis à jour et téléchargé dans l'interface graphique de Blackboard Learn sur la page Paramètres d'authentification SAML de la section Paramètres du fournisseur d'identité. SAML B2 doit alors être basculé sur Inactif/Disponible, et le fournisseur d'authentification SAML doit présenter le statut « Actif » pour garantir que le fichier XML de métadonnées mis à jour est reconnu dans le système.

Méthode de recherche des utilisateurs incorrecte

Après la saisie des informations de connexion sur la page de connexion ADFS, l'utilisateur est redirigé vers l'interface graphique de Blackboard Learn mais n'est pas connecté à Blackboard Learn.

L'événement d'authentification SAML uniquement est consigné dans le fichier log bb-services :

2016-10-18 13:03:28 -0600 - userName is null or empty

Solution

  1. Connectez-vous à Blackboard Learn en tant qu'administrateur en utilisant l'authentification Blackboard Learn interne par défaut.
  2. Accédez à Administration système > « Nom du fournisseur d'authentification SAML » > Modifier.
  3. Changez le type de méthode de recherche des utilisateurs de la valeur Identifiant batch à Nom d'utilisateur.

Bouton supplémentaire de déconnexion Mettre fin à la session SSO

ADFS tente d'ajouter un bouton supplémentaire Mettre fin à la session SSO à la page Mettre fin à toutes les sessions ? qui s'affiche après la sélection préalable du bouton de déconnexion en haut à droite de l'interface graphique de Blackboard Learn.

Cela est possible par l'ajout d'un élément SingleLogoutService au fichier de métadonnées du fournisseur d'identité :

<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://your.server.name/adfs/ls/"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://your.server.name/adfs/ls/"/>

Comme il s'agit d'une configuration du fournisseur d'identité SAML B2 facultative et que la signature fournie dans le point d'arrêt de redirection n'est pas correcte, une erreur se produira lors de la sélection du bouton Mettre fin à la session d'authentification unique supplémentaire dans la page Mettre fin à toutes les sessions ? : Échec de la validation de sécurité pour le message SAML entrant. La validation de la signature simple de la demande a échoué pour l'émetteur du contexte. Pour information, le code d'erreur est [Code erreur].

La trace de pile Java correspondante pour l'ID d'erreur est la suivante dans le journal bb-services :

2016-10-17 16:57:44 -0400 - Incoming SAML message failed security validation Validation of request simple signature failed for context issuer<P><span class="captionText">For reference, the Error ID is 930c7767-8710-475e-8415-2077152280e0.</span> - org.opensaml.ws.security.SecurityPolicyException: Validation of request simple signature failed for context issuer
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.doEvaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:139)
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.evaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:103)
    at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:131)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.doFilter(SAMLLogoutProcessingFilter.java:104)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Solution

  1. Accédez au serveur ADFS et naviguez jusqu'à Relying Party Trust (Approbation de partie de confiance) pour l'instance de Blackboard Learn.
  2. Sélectionnez l'onglet Properties > Endpoints (Propriétés > Points d'arrêt).
  3. L'onglet Endpoints (Points d'arrêt) contient 2 points d'arrêt de déconnexion SAML.
  4. Supprimez le point d'arrêt Redirect (Redirection).
  5. Sélectionnez Remove Endpoint (Supprimer le point d'arrêt) en vue de sa suppression, puis cliquez sur Apply (Appliquer) et OK.

Une fois le point d'arrêt Redirect (Redirection) supprimé, le bouton Mettre fin à la session SSO ne fonctionnera pas correctement pour déconnecter l'utilisateur.

Affichage de journaux d'applications avec l'observateur d'événements

Lors du dépannage d'un problème d'authentification SAML ADFS, il peut être nécessaire de demander à un établissement de consulter les journaux d'application ADFS dans la Visionneuse d'événements sur son serveur ADFS en vue d'une analyse ultérieure. Ceci est particulièrement nécessaire lorsque la réponse SAML du serveur ADFS a pour statut Request Denied (Demande refusée) comme indiqué ci-dessous :

<samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:RequestDenied" />
    </samlp:StatusCode>
</samlp:Status>

La réponse SAML peut être visionnée à l'aide du module complémentaire SAML tracer de Firefox.

Le statut Request Denied (Demande refusée) d'une réponse indique qu'un problème est survenu lorsque le fournisseur d'identité (ADFS) a tenté de comprendre la réponse et de traiter le résultat que le fournisseur de services (Blackboard Learn) a fourni.

Pour afficher les journaux d'application ADFS à l'aide de l'Observateur d'événements :

  1. Ouvrez l'Observateur d'événements sur le serveur ADFS.
  2. Dans le menu Affichage, sélectionnez Afficher les journaux d'analyse et de débogage.
  3. Dans l'arborescence de la console, accédez à Journaux des applications et des services > Suivi AD FS > Débogage.

Azure Active Directory

Azure AD est le service de gestion d'annuaires et d'identités basé sur le cloud de Microsoft (MS).

Envoyer la première partie d'un e-mail

Si un établissement utilise Azure AD en tant que fournisseur d'identité et souhaite ne disposer que de la première partie du nom d'utilisateur Azure AD utilisé pour le nom d'utilisateur Blackboard Learn, il peut configurer son fournisseur d'identité Azure AD pour qu'il utilise la fonction ExtractMailPrefix() afin de supprimer le suffixe de domaine de l'e-mail ou du nom principal de l'utilisateur, ce qui entraîne la transmission de la première partie du nom d'utilisateur (par exemple, « joesmith » au lieu de joesmith@exemple.com).

Si l'ID utilisateur distant de Blackboard Learn est urn:oid:1.3.6.1.4.1.5923.1.1.1.6, le paramètre Attribut du fournisseur d'identité Azure ressemblerait à ceci :

Nom de l'attribut :urn:oid:1.3.6.1.4.1.5923.1.1.1.6
Valeur de l'attribut :    ExtractMailPrefix()
E-mail :user.userprincipalname

Dans l'exemple du nom d'utilisateur d'e-mail joesmith@exemple.com, les informations seraient transmises ainsi dans l'assertion SAML entre le fournisseur d'identité Azure et Blackboard Learn :

<Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6">
    <AttributeValue>joesmith</AttributeValue>

Des informations supplémentaires sur l'utilisation de la fonction ExtractMailPrefix() sont disponibles sur la page de documentation MS Azure.

Certificat de mise à jour du fournisseur d'identité Azure AD

Après la saisie des informations de connexion sur la page de connexion MS Azure AD, un message Erreur de connexion peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn.

L'exception suivante est consignée dans le fichier log bb-services :

2016-10-13 12:03:23 +0800 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
 at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
 at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
 at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
 at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
 at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
 at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
 at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
 at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
 at sun.reflect.GeneratedMethodAccessor854.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
 at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
 at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
 at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
 at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
 at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
 at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
 at java.security.AccessController.doPrivileged(Native Method)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
 at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
 [SNIP]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:229)
 at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
 at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
 ... 230 more
Caused by: org.opensaml.xml.validation.ValidationException: Signature is not trusted or invalid
 at org.springframework.security.saml.websso.AbstractProfileBase.verifySignature(AbstractProfileBase.java:272)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertionSignature(WebSSOProfileConsumerImpl.java:419)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertion(WebSSOProfileConsumerImpl.java:292)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:214)
 ... 232 more

Cela est dû au fait que le fournisseur d'identité MS Azure AD met à jour le certificat, mais que les métadonnées XML utilisées par le fournisseur de services Blackboard Learn ne reflètent pas le nouveau certificat.

Solution

  • Le nouveau fichier XML de métadonnées avec le nouveau certificat devra être mis à jour sur la page Paramètres SAML dans l'interface graphique de Blackboard Learn pour le fournisseur d'authentification.
  • SAML B2 et le fournisseur d'authentification doivent ensuite être basculés sur Inactif/Disponible, et le fournisseur d'authentification SAML doit présenter le statut « Actif » pour permettre la mise en œuvre du nouveau certificat avec les métadonnées mises à jour.
  • Si un site Blackboard Learn possède plusieurs fournisseurs d'authentification partageant le même certificat sous-jacent pour le même ID d'entité du fournisseur d'identité sous-jacent, TOUS ces fournisseurs d'authentification devront être mis à jour.

Microsoft a indiqué que les certificats seront désormais mis à jour toutes les 6 semaines, sans annonce préalable.


Connexion unique initiée par le fournisseur d'identité

Si un utilisateur se connecte d'abord à son portail utilisateur, puis sélectionne l'application pour son site Blackboard Learn, le message suivant s'affiche dans un nouvel onglet du navigateur : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder.

Avec les événements liés à SAML correspondants dans le fichier log bb-services :

INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/08/16 10:49:22 | - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@58c53845. A new one will be created.
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Forwarding to /
INFO   | jvm 1    | 2016/08/16 10:49:22 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/08/16 10:49:22 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Successfully completed request
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Skip invoking on
INFO   | jvm 1    | 2016/08/16 10:49:22 | - SecurityContextHolder now cleared, as request processing completed

La section Paramètres du fournisseur de services de la page Paramètres d'authentification SAML a changé et l'option Activer la connexion unique automatique doit être sélectionnée pour permettre aux utilisateurs d'accéder à Blackboard Learn depuis leur portail. Si l'option est activée, l'URL ACS sera également mise à jour pour inclure un alias.


Document erroné

Après la saisie des informations de connexion sur la page de connexion du fournisseur d'authentification, un message Erreur de connexion peut s'afficher car une redirection a eu lieu vers l'interface graphique de Blackboard Learn.

Les exceptions DOMException et WRONG_DOCUMENT_ERR sont consignées dans le journal bb-services :

2016-11-18 12:27:31 -0600 - WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.<P><span class="captionText">For reference, the Error ID is 86ebb81d-d3a3-4da5-95ab-1c94505f4281.</span> - org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
    at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
    at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
    at org.apache.xerces.dom.NodeImpl.appendChild(Unknown Source)
    at org.opensaml.xml.encryption.Decrypter.parseInputStream(Decrypter.java:832)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:610)
    at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:795)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:535)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:453)
    at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:414)
    at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
    at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:199)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:82)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1209.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186)
    at blackboard.platform.servlet.DevNonceFilter.doFilter(DevNonceFilter.java:68)
    [SNIP]

La raison pour laquelle le problème se produit est qu'un autre building block/projet a modifié la valeur de propriété système javax.xml.parsers.DocumentBuilderFactory de org.apache.xerces.jaxp.DocumentBuilderFactoryImpl à com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.

Solution temporaire

Dans l'attente de la parution d'un correctif, vous pouvez recourir à l'une des solutions temporaires ci-dessous :

  1. Redémarrez les services Bb sur chaque nœud.
  2. Désactivez le chiffrement de la réponse SAML sur le fournisseur d'identité. Comme la communication s'appuie entièrement sur SSL, cette opération ne réduira pas la sécurité de l'authentification.

Pas d'option d'ajout de SAML à Ordre des fournisseurs

Lors de la configuration de l'authentification SAML, un établissement peut constater qu'il n'est pas possible d'ajouter un fournisseur d'authentification SAML dans la section Ordre des fournisseurs de l'interface graphique de Blackboard Learn lors de l'accès à Administrateur système > Building Blocks : Authentification > Ordre des fournisseurs.

En effet, il n'existe pas d'option d'ajout d'un fournisseur d'authentification SAML à la liste Ordre des fournisseurs afin de rediriger les types de fournisseurs tels que l'authentification de transfert CAS et SAML vers la source d'authentification distante. Ils ne sont pas répertoriés dans la liste Ordre des fournisseurs, car ils sont considérés comme la source d'authentification la plus fiable et ils gèrent leurs propres échecs d'authentification.


Tester la connexion SAML

À compter de la version de Blackboard Learn du quatrième trimestre 2016, il est désormais possible de tester la connexion d'un fournisseur SAML dans la section Authentification de l'interface graphique de Blackboard Learn. Le test de connexion portera sur les éléments suivants :

  • Analyse des métadonnées du fournisseur d'identité
  • Connexion au fournisseur d'identité
  • Réception de la réponse SAML
  • Analyse de la réponse SAML
  • Correspondance de l'ID utilisateur distant
  • Connexion à Blackboard Learn

Pour tester la connexion d'un fournisseur d'authentification SAML :

  1. Connectez-vous à Blackboard Learn en tant qu'administrateur.
  2. Accédez à Administrateur système > Building Blocks : Authentification > « Nom du fournisseur SAML » > Tester la connexion.
  3. Entrez les données d'authentification du fournisseur d'identité si vous y êtes invité.

La fonctionnalité Tester la connexion peut être utilisée au lieu d'activer manuellement la journalisation de débogage SAML dans Blackboard Learn, et ce, pour plusieurs raisons.

La valeur de ID d'entité du fournisseur d'identité visible dans la page de sortie Tester la connexion est extraite de l'élément Émetteur dans la requête POST SAML du fournisseur d'identité vers Blackboard Learn une fois que l'utilisateur s'est authentifié :

<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://bbpdcsi-adfs1.bbpdcsi.local/a...services/trust</Issuer>

Les valeurs d'attribut SAML visibles dans la page de sortie Tester la connexion de la section Réponse SAML sont extraites des éléments Subject et AttributeStatement de la requête POST SAML du fournisseur d'identité et copiées dans Blackboard Learn une fois que l'utilisateur s'est authentifié :

<Subject>
    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">luke.skywalker</NameID>
    [SNIP]
</Subject>
<AttributeStatement>
    <Attribute Name="SamAccountName">
        <AttributeValue>luke.skywalker</AttributeValue>
    </Attribute>
    <Attribute Name="urn:oid:2.5.4.42">
        <AttributeValue>Luke</AttributeValue>
    </Attribute>
    <Attribute Name="urn:oid:2.5.4.4">
        <AttributeValue>Skywalker</AttributeValue>
    </Attribute>
</AttributeStatement>


Créer un fournisseur d'authentification SAML et un fournisseur d'identité à des fins de test

Procédez comme suit pour créer un fournisseur d'identité à l'aide de la solution d'authentification SSO gratuite de Centrify.

Ce fournisseur d'identité peut ensuite être configuré en tant que fournisseur d'authentification SAML dans un fournisseur de services Blackboard Learn :

Fournisseur de services Blackboard Learn

  1. Connectez-vous à l'interface graphique de Blackboard Learn en tant qu'administrateur et accédez à Administrateur système > Authentification.
  2. Sélectionnez Créer un fournisseur > SAML.
  3. Entrez les paramètres suivants :
    • Nom > SAML ou valeur de votre choix.
    • Fournisseur d'authentification > Inactif (pour le moment).
    • Méthode de recherche des utilisateurs > Nom d'utilisateur
    • Restreindre par nom d'hôte > Utiliser ce fournisseur pour les noms d'hôtes
    • Texte du lien > Connexion à SAML Centrify
  4. Sélectionnez Enregistrer et configurer.
  5. Dans le champ ID d'entité, indiquez la valeur de votre choix (mais si vous la modifiez, vous devez transmettre au fournisseur d'identité les métadonnées du fournisseur de services mises à jour). Effectuez un copier-coller de l'URL ACS.
  6. Sous Métadonnées du fournisseur de services, sélectionnez Générer et enregistrez le fichier sur votre ordinateur de bureau.
  7. Sous Source de données, il est recommandé de créer une nouvelle source de données pour ce CENTRIFY nommé. Il est également possible d'utiliser SYSTEM ou une valeur de votre choix.
  8. En regard de Activer le provisionnement JIT, cochez cette case pour qu'un compte soit créé automatiquement lors de la tentative de connexion via ce fournisseur d'authentification SAML si l'utilisateur n'existe pas. Si le provisionnement JIT n'est pas sélectionné, l'utilisateur devra être créé manuellement dans Blackboard Learn.
  9. Dans la liste Sources de données compatibles, assurez-vous de sélectionner les sources de données avec lesquelles le fournisseur d'authentification doit être compatible.
  10. Sélectionnez Fournisseur d'identité de point comme Type de fournisseur d'identité.
  11. Ignorez les Paramètres du fournisseur d'identité pour le moment, car vous téléchargerez le fichier après l'avoir créé dans la section Fournisseur d'identité Centrify.
  12. À la section Mapper les attributs SAML, utilisez ID de nom comme ID utilisateur distant.
  13. Sélectionnez Valider.

Fournisseur d'identité Centrify

  1. Accédez au site Web Centrify et sélectionnez Démarrer maintenant.
  2. Entrez vos informations d'inscription et sélectionnez Démarrer maintenant.
  3. Vous recevez ensuite un e-mail de bienvenue avec vos informations d'identification d'administrateur. Utilisez-les pour vous connecter à https://cloud.centrify.com.
  4. Sélectionnez Skip (Ignorer) dans la fenêtre de bienvenue du service d'identification Centrify.
  5. Dans l'onglet Applications en haut de la page, sélectionnez le bouton Add Web Apps (Ajouter des applications Web).
  6. Dans l'onglet Custom (Personnalisé), faites défiler et sélectionnez le bouton Add (Ajouter) associé à SAML. Sélectionnez Oui.
  7. Sélectionnez Close (Fermer) dans le bas de la fenêtre Add Web Apps (Ajouter des applications Web).
  8. Accédez à l'onglet Applications. Dans la section Application Settings (Paramètres d'application), sélectionnez le bouton Upload SP Metadata (Télécharger les métadonnées du fournisseur de services) et téléchargez le fichier créé à l'étape 6 de la section Fournisseur de services Blackboard Learn.
  9. Assertion Consumer Service URL (URL du service client d'assertion) doit être complétée automatiquement après le téléchargement des métadonnées du fournisseur de services.
  10. Décochez Encrypt Assertion (Chiffrer l'assertion). Cela permet de libérer les attributs du fournisseur d'identité et de les envoyer à Blackboard Learn à l'aide du module complémentaire SAML tracer du navigateur Firefox ou SAML Message Decoder du navigateur Chrome. Comme la communication s'appuie entièrement sur SSL, cette opération ne réduira pas la sécurité de l'authentification.
  11. Faites défiler la page et sélectionnez Download Identity Provider SAML Metadata (Télécharger les métadonnées SAML du fournisseur d'identité). Enregistrer le fichier sur votre ordinateur de bureau.
  12. Sélectionnez Save (Enregistrer) et passez à la section suivante.
  13. Entrez un nom dans la section Description. Sélectionnez Save (Enregistrer) et passez à la section suivante.
  14. Dans la section User Access (Accès utilisateur), sélectionnez Everybody (Tout le monde) et System Administrator (Administrateur système). Sélectionnez Enregistrer.
  15. N'effectuez aucune sélection dans la section Policy (Stratégie).
  16. Dans la section Account Mapping (Mappage des comptes), assurez-vous que userprincipalname figure dans le champ Directory Service (Service d'annuaires).
  17. Dans la section Advanced (Avancé), ajoutez la ligne suivante en bas du script utilisé pour générer une assertion SAML pour l'application :

    Le script complet se présentera ainsi :

    setIssuer(Issuer);
    setSubjectName(UserIdentifier);
    setAudience('https://YourLearnServer.blackboard.c...saml/saml/SSO');
    setRecipient(ServiceUrl);
    setHttpDestination(ServiceUrl);
    setSignatureType('Assertion');
    setNameFormat('emailaddress');
    setAttribute("NameID", LoginUser.Get("userprincipalname"));

    Cela permettra au fournisseur d'identité Centrify d'émettre un élément AttributeStatement comprenant l'ID utilisateur dans la requête POST SAML.

    Exemple :

    <AttributeStatement>
        <Attribute Name="NameID"
                   NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
                   >
            <AttributeValue>luke.skywalker@blackboard.com.47</AttributeValue>
        </Attribute>
    </AttributeStatement>

    En savoir plus sur la spécification d'éléments d'assertion dans le script SAML de Centrify

  18. Sélectionnez Enregistrer.
  19. Il est inutile de modifier les sections restantes (App Gateway [Passerelle d'application], Changelog et Workflow).
  20. Dans l'onglet Applications, assurez-vous que l'application SAML a été déployée automatiquement.
  21. Dans l'onglet Users (Utilisateurs), sélectionnez Add Users (Ajouter des utilisateurs), entrez les informations relatives au compte d'un utilisateur, puis sélectionnez Create User (Créer un utilisateur).
  22. Reconnectez-vous à l'interface graphique de Blackboard Learn en tant qu'administrateur, accédez à Administrateur système > Authentification > Nom du fournisseur d'authentification > Paramètres SAML > Paramètres du fournisseur d'identité, téléchargez le fichier de métadonnées du fournisseur d'identité enregistré sur votre ordinateur de bureau à l'étape 13 et sélectionnez Valider.

L'utilisateur du fournisseur d'identité Centrify créé précédemment peut à présent se connecter à Blackboard Learn via SAML en sélectionnant ce fournisseur d'authentification sur la page de connexion et se déconnecter de Blackboard Learn à l'aide du bouton supplémentaire Mettre fin à la session d'authentification unique à la page Mettre fin à toutes les sessions ? qui s'affiche après la sélection du bouton de déconnexion en haut à droite de l'interface graphique de Blackboard Learn.


Modifier le texte dans la page de déconnexion Mettre fin à la session SSO

Un établissement peut demander s'il est possible de modifier le texte sur la page de fin de la session SSO. Il est possible de modifier le texte de la page de déconnexion Mettre fin à la session SSO en éditant le pack langue :

  1. Ouvrez le fichier de pack langue.
  2. Accédez à auth-provider-saml/src/main/webapp/WEB-INF/bundles/bb-manifest-en_US.properties.
  3. Mettez à jour les clés de message :

    saml.single.logout.warning.conent.description // the first line
    saml.single.logout.warning.conent.recommend // second line
    saml.single.logout.warning.endsso.title // third line
    saml.single.logout.warning.endsso.button // the button
    saml.single.logout.warning.backtolearn // the cancel button


Réorienter les utilisateurs vers la page de connexion du fournisseur d'identité

La page de connexion standard Blackboard Learn comporte les champs Nom d'utilisateur et Mot de passe pour le fournisseur d'authentification Learn interne par défaut. Lorsque vous activez l'authentification SAML, le lien « Se connecter avec… » pour SAML apparaît dans le bas de cette page, de sorte qu'il est possible de réorienter les utilisateurs vers le serveur d'authentification du fournisseur d'identité automatiquement lorsque ces derniers accèdent à la page de connexion de Learn.

Pour y parvenir, une solution consiste à accéder à Administrateur système > Authentification et à définir l'authentification Learn interne par défaut sur Inactif, ce qui signifie que la page de connexion n'est plus affichée et que l'utilisateur est immédiatement redirigé vers la connexion SAML. Cette option présente l'inconvénient de remplacer l'URL de connexion par défaut et d'empêcher les utilisateurs autres que SAML de se connecter.

Pour éviter ce problème et fournir quasiment le même résultat, utilisez une page de connexion personnalisée. Les utilisateurs sont réorientés vers la page de connexion du fournisseur d'identité du fournisseur d'authentification SAML, mais le lien de connexion par défaut est également utilisable.

  1. Assurez-vous que l'authentification Learn interne est active.
  2. Sur la page de connexion par défaut, copiez l'emplacement de la redirection du fournisseur, par ex. Connexion utilisant... SAML. Cliquez avec le bouton droit de la souris sur le lien et sélectionnez Copier l'emplacement du lien.
  3. Accédez à Administrateur système > Communautés > Marques et thèmes > Personnaliser la page de connexion.
  4. Sélectionnez Télécharger en regard de Page de connexion par défaut afin de télécharger le fichier JSP de connexion par défaut.
  5. Ouvrez le fichier JSP dans un éditeur de texte. Ajoutez le code HTML d'exemple suivant au fichier JSP de connexion et remplacez le texte de l'URL par l'URL copiée à l'étape 2.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head>
    <title>Blackboard Learn - Redirect</title>
    <meta http-equiv="REFRESH" content="0;url=https://URL_Goes_Here"></HEAD>
    <BODY style="font-family: arial,sans-serif;font-size: small; color: grey; padding: 1em; ">
    Redirecting... <a style="color:grey" href="https://URL_Goes_Here">Go to login page</a> if you are not automatically redirected.
    </BODY>
    </HTML>

  6. Accédez à Personnaliser la page de connexion dans Learn une fois de plus. Sélectionnez Utiliser la page personnalisée et téléchargez le fichier JSP de connexion mis à jour.
  7. Après avoir effectué les modifications, sélectionnez Aperçu dans Personnaliser la page de connexion pour confirmer que la redirection fonctionne correctement.

Les utilisateurs accédant à l'URL principale seront désormais réorientés vers la page de connexion du fournisseur d'authentification SAML. Les administrateurs peuvent toujours se connecter à l'aide de l'authentification en interne via la page de connexion par défaut : /webapps/login/?action=default_login ou /webapps/login/login.jsp.

En savoir plus sur la personnalisation de la page de connexion dans l'expérience Ultra