Skip to main content

Problèmes courants de l'authentification SAML

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.

Important

Si, pour une raison quelconque, un fichier XML de métadonnées IdP mis à jour/nouveau est chargé dans l'interface graphique Blackboard sur la page des paramètres d'authentification SAML de la section Paramètres du fournisseur d'identité pour un fournisseur d'authentification SAML, le SAML B2 et ce fournisseur d'authentification SAML doivent également être désactivés ou disponibles, tout en ayant le statut « Actif » du fournisseur d'authentification SAML, afin de garantir que toutes les métadonnées IdP mises en cache sont effacées et que les métadonnées IdP mises à jour sont complètes. utilisé.

Termes clés

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

  • SAML : langage de balisage des assertions de sécurité

  • IdP : fournisseur d'identité

  • SP : Fournisseur de service

  • ADFS : service de fédération Active Directory

  • GUI : interface utilisateur graphique. Dans le contexte de Blackboard, cela signifie travailler au sein du 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

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 résolution des problèmes d'authentification SAML 2.0, il peut être nécessaire à un moment donné de confirmer/consulter les attributs qui sont réellement publiés par l'IdP et envoyés à Blackboard 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 le nom d'utilisateur n'est pas correctement mappé comme indiqué dans le champ ID utilisateur distant de la section Mapper les attributs SAML de la page Paramètres d'authentification SAML de l'interface graphique de Blackboard, l'événement suivant sera enregistré dans le journal bb-services lors de la tentative de connexion à Blackboard via l'authentification SAML :

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

Une erreur de connexion similaire ! message affiché dans le navigateur : Blackboard n'est actuellement pas en mesure de se connecter à votre compte à l'aide de l'authentification unique. Contactez votre administrateur pour obtenir de l'aide.

Image d'un message d'erreur de connexion affiché dans le navigateur indiquant que Blackboard n'est actuellement pas en mesure de se connecter à votre compte à l'aide de l'authentification unique. Contactez votre administrateur pour obtenir de l'aide.

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]
Résolution

Vous pouvez résoudre le problème de deux façons différentes. Tout d'abord, sélectionnez l'option Créer des comptes s'ils n'existent pas dans le système sur la page Paramètres d'authentification SAML de l'interface graphique Blackboard. 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 associez le nom d'attribut qui possède la valeur d'attribut souhaitée à l'ID utilisateur distant sur la page des paramètres d'authentification SAML de l'interface utilisateur Blackboard.

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

Les utilisateurs ne pourront pas se connecter à Blackboard via 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 de l'interface graphique Blackboard. L'événement suivant sera enregistré dans le journal Bb lorsque vous tenterez de vous connecter à Blackboard via 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]
Résolution
  1. Obtenez le nom d'un utilisateur qui ne parvient pas à se connecter.

  2. Dans l'interface graphique de Blackboard, 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 Soumettre.

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

Si OneLogin est configuré comme IdP pour le fournisseur d'authentification SAML dans Blackboard, une erreur « L'URL donnée n'est pas bien formée » peut s'afficher sur la page après avoir saisi les informations d'identification OneLogin lors de la tentative de connexion à Blackboard.

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
Ré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 des attributs font la distinction entre majuscules et minuscules dans la section Map SAML Attributes de la page des paramètres d'authentification SAML de l'interface graphique Blackboard. Ainsi, si l'ID utilisateur distant a SAMAccountName pour le nom d'attribut sur la page des paramètres et que le SAML POST réel de l'IdP a ceci pour le nom d'attribut dans l'AttributeStatement :

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

L'utilisateur ne pourra pas se connecter. La valeur du nom d'attribut d'ID utilisateur distant sur la page Paramètres d'authentification SAML doit être remplacée par SAMAccountName.

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

Cette section présente certains des problèmes courants qui peuvent empêcher un utilisateur de se connecter à Blackboard via l'authentification SAML avec ADFS lorsque la ressource spécifiée est introuvable, que vous n'êtes pas autorisé à y accéder ou que vous n'êtes pas autorisé à y accéder ou que vous avez commis une erreur de connexion ! le message s'affiche dans l'interface graphique de Blackboard.

Problème n° 1

Après avoir saisi les informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher après avoir été redirigé vers l'interface graphique Blackboard : La ressource spécifiée est introuvable ou vous n'êtes pas autorisé à y accéder.

Message d'erreur « Une ressource spécifiée n'a pas été trouvée »

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);
 }
}
Résolution

Cela se produit généralement parce que l'ID d'entité du SP configuré dans l'interface graphique de Blackboard 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 pour l'ID d'entité serait https://[Blackboard Server Hostname] /auth-SAML/SAML/SSO.

Note

Si un établissement change son URL de l'URL par défaut https://school.blackboard.com à https://their.school.edu, l'ID d'entité dans l'interface graphique Blackboard sur la page des paramètres d'authentification SAML doit être mis à jour à https://their.school.edu/auth-saml/saml/authentification unique.

Problème n° 2

Après avoir saisi les informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher après avoir été redirigé vers l'interface graphique Blackboard : 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 de l'AES-256 et que le moteur d'exécution Java utilisé par Blackboard ne prend pas en charge le format AES-256 par défaut.

Résolution

Une option de résolution universelle consiste à ouvrir un PowerShell sur le serveur ADFS et à configurer la partie de confiance créée pour Blackboard afin qu'elle envoie les attributs comme non chiffrés. Comme la communication s'appuie entièrement sur SSL, cette opération ne réduira pas la sécurité de l'authentification. Cela facilite également le débogage de tout problème, car les attributs peuvent être visualisés à l'aide d'outils de débogage tels que le module complémentaire SAML Tracer du navigateur Firefox et aucun redémarrage du système Blackboard n'est requis. Pour définir la partie de confiance créée pour que Blackboard envoie les attributs comme non chiffrés, ouvrez un PowerShell et exécutez la commande suivante, en remplaçant targetName par le nom de la partie de confiance qui se trouve dans la console de gestion ADFS sous Relations de confiance > Fiducies entre parties fiables.

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

Après cette modification, le service ADFS devra être redémarré avec la commande : Restart-Service ADFSSRV

Problème n° 3

Après avoir saisi les informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher après avoir été redirigé vers l'interface graphique Blackboard : La ressource spécifiée est introuvable, ou vous n'êtes pas autorisé à y accéder ou 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 le serveur ADFS et le serveur d'applications appli Blackboard présentent une dérive temporelle proche ou supérieure à la valeur par défaut de 60 secondes.

Résolution

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

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

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

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

    Note

    Un établissement peut utiliser l'URL ci-dessus pour comparer le fuseau horaire et l'horloge du système Blackboard 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.

Problème n° 4

Après avoir saisi les informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher après avoir été redirigé vers l'interface graphique Blackboard : La ressource spécifiée est introuvable, ou vous n'êtes pas autorisé à y accéder ou 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]
Résolution
  1. Accédez au panneau de configuration de l'administrateur.

  2. Sous Intégrations, 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 de l'algorithme de signature, choisissez 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 avoir saisi les informations de connexion sur la page de connexion ADFS, une erreur peut s'afficher après avoir été redirigé vers l'interface graphique Blackboard : La ressource spécifiée est introuvable, ou vous n'êtes pas autorisé à y accéder ou 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.

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

Note

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

Ré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 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 is currently unable to log into your account using single sign-on. Contact your administrator for assistance.
For reference, the Error ID is [error ID].

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.

Résolution

Le paramètre doit être configuré dans Blackboard 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 Blackboard sur le serveur ADFS.

  1. Dans Blackboard, 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 au Relying Party Trust pour votre instance Blackboard.

  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
Résolution

Il existe une option pour 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 à votre serveur ADFS et téléchargez les nouvelles métadonnées SP dans le Relying Party Trust pour votre site Blackboard.

Note

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. L'IDP ne sera pas mis à jour et le nouveau certificat sera présenté au prochain redémarrage de Blackboard. L'authentification SAML sera interrompue en raison de cette incompatibilité.

Métadonnées de fédération

Avec Active Directory Federation Services (ADFS), étant donné que les métadonnées d'une fédération ADFS situées généralement dans https://[ADFS [Nom d'hôte du serveur] /FederationMetadata/2007-06/FederationMetadata.xml incluent un élément incompatible avec SAML 2.0, les métadonnées doivent être modifiées pour supprimer l'élément incompatible avant qu'il ne soit chargé dans la section Paramètres du fournisseur d'identité de la page Paramètresd'authentification SAML de l'interface graphique Blackboard. Si les métadonnées contenant l'élément incompatible sont téléchargées, une erreur se produira lors de la sélection du lien de connexion SAML sur la page de connexion Blackboard : les métadonnées de l'entité [entité] et du rôle {} sont introuvables. À titre de référence, l'ID d'erreur est [ID d'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&#39;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]
Résolution

Étant donné que l'emplacement des métadonnées par défaut pour une fédération ADFS est https://[ADFS [nom d'hôte du serveur] /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 ».

Important

Si un établissement teste l'authentification SAML sur un site Blackboard et possède plusieurs fournisseurs d'authentification SAML qui partagent le même fichier XML de métadonnées ADFS IdP sous-jacent sur le site Blackboard, même si les autres fournisseurs d'authentification SAML sont définis sur Inactif, le fichier XML de métadonnées mis à jour doit également être chargé dans l'interface graphique Blackboard 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 avoir saisi les informations de connexion sur la page de connexion ADFS, l'utilisateur est redirigé vers l'interface graphique de Blackboard mais n'est pas connecté à Blackboard.

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
Résolution
  1. Connectez-vous à Blackboard en tant qu'administrateur à l'aide de l'authentification interne Blackboard 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 Code unique de 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 facultative d'IdP SAML B2 et que la signature fournie dans le point de terminaison de redirection n'est pas correcte, une erreur se produira lors de la sélection du bouton supplémentaire Fin de la session SSO sur Fin de toutes les sessions ? page : La validation de sécurité du message SAML entrant a échoué. La validation de la signature simple de la demande a échoué pour l'émetteur du contexte. À titre de référence, l'ID d'erreur est [ID d'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]
Résolution
  1. Accédez au serveur ADFS et accédez au Relying Party Trust pour l'instance Blackboard.

  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>

Note

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

Le statut Requête refusée dans une réponse indique généralement qu'un problème est survenu lorsque l'IdP (ADFS) a tenté de comprendre la réponse et de traiter le résultat fourni par le SP (Blackboard).

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 une institution utilise Azure AD comme IdP et souhaite que seule la première partie du nom d'utilisateur Azure AD soit utilisée comme nom d'utilisateur Blackboard, elle peut configurer son IdP Azure AD pour utiliser la fonction spéciale ExtractMailPrefix () pour supprimer le suffixe de domaine de l'e-mail ou du nom d'utilisateur principal, de sorte que seule la première partie du nom d'utilisateur soit transmise (par exemple « joesmith » au lieu de joesmith@example.com).

Si l'ID utilisateur distant de Blackboard 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 :

Attribute Name:        urn:oid:1.3.6.1.4.1.5923.1.1.1.6
Attribute Value:    ExtractMailPrefix()
Mail:            user.userprincipalname

Ainsi, avec l'exemple de nom d'utilisateur de messagerie joesmith@example.com, il serait transmis comme suit dans l'assertion SAML de l'IdP Azure à Blackboard :

<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 avoir saisi les informations de connexion sur la page de connexion de MS Azure AD, une erreur de connexion s'est produite ! peut s'afficher après avoir été redirigé vers l'interface graphique de Blackboard.

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 l'IdP MS Azure AD met à jour le certificat, mais que les métadonnées XML utilisées par le Blackboard SP ne sont pas ajustées pour refléter le nouveau certificat.

Résolution
  • Le nouveau fichier XML de métadonnées contenant le nouveau certificat devra être mis à jour sur la page des paramètres SAML de l'interface graphique Blackboard du 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 possède plusieurs fournisseurs d'authentification qui partagent le même certificat sous-jacent pour le même identifiant d'entité IdP sous-jacent, TOUS ces fournisseurs d'authentification devront être mis à jour.

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, un nouvel onglet de navigateur s'ouvre et affiche le message suivant : 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 été modifiée et l'option Activer l'authentification unique automatique doit être cochée pour permettre à un utilisateur d'accéder à Blackboard depuis son portail. Si l'option est activée, l'URL ACS sera également mise à jour pour inclure un alias.

Document erroné

Après avoir saisi les informations de connexion sur la page de connexion du fournisseur d'authentification SAML, une erreur de connexion s'est produite ! peut s'afficher après avoir été redirigé vers l'interface graphique de Blackboard.

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]

Le problème se produit parce qu'un autre B2/Project a modifié la valeur de la propriété système javax.xml.parsers.DocumentBuilderFactory de org.apache.xerces.jaxp.DocumentBuilderFactoryImpl en 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 remarquer qu'il n'existe aucune option permettant d'ajouter un fournisseur d'authentification SAML dans la section Ordre des fournisseurs de l'interface graphique Blackboard lorsque vous accédez à Administrateur système > Éléments de base : 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

Il existe une option permettant de tester la connexion pour un fournisseur SAML dans la section Authentification de l'interface graphique Blackboard. 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

  • Connectez-vous à Blackboard

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

  1. Connectez-vous à Blackboard en tant qu'administrateur.

  2. Accédez à Administrateur système > Éléments de base : 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 fonction Test Connection peut être utilisée au lieu d'activer manuellement la journalisation du débogage SAML dans Blackboard 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 affichées sur la page de sortie de test de connexion dans la section Réponse SAML sont extraites des éléments Subject et AttributeStatement du SAML POST de l'IdP vers Blackboard une fois l'utilisateur 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.

Cet IdP peut ensuite être configuré en tant que fournisseur d'authentification SAML dans un service Blackboard :

Fournisseur de services Blackboard
  1. Connectez-vous à l'interface graphique Blackboard 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 JIT Provisioning n'est pas sélectionné, l'utilisateur dans Blackboard devra être créé manuellement.

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

Fournisseur d'identité Centrify
  1. Accédez au site Web de Centrify et sélectionnez Commencer 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 Paramètres de l'application, sélectionnez le bouton Charger les métadonnées SP et chargez le fichier créé à l'étape 6 de la section Blackboard SP.

  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 la case Chiffrer l'assertion. Cela permet de visualiser les attributs publiés par l'IdP et envoyés à Blackboard à l'aide du module complémentaire SAML Tracer du navigateur Firefox ou du décodeur de messages SAML de 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. Pour la section Account Mapping, vérifiez que userprincipalname est saisi pour le nom de champ du service d'annuaire.

  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>
  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 Blackboard en tant qu'administrateur, accédez à Administrateur système > Authentification > Nom du fournisseur d'authentification SAML > Paramètres SAML > Paramètres du fournisseur d'identité, chargez le fichier de métadonnées IdP qui a été enregistré sur votre bureau à l'étape 13 et sélectionnez Soumettre.

L'utilisateur Centrify IdP qui a été créé peut désormais se connecter à Blackboard via SAML en sélectionnant ce fournisseur d'authentification sur la page de connexion, puis se déconnecter de Blackboard à l'aide du bouton de déconnexion supplémentaire Fin de session SSO sur la page Fin de toutes les sessions ? page qui s'affiche après avoir sélectionné le bouton de déconnexion en haut à droite de Blackboard.

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 de Blackboard présente des champs de nom d'utilisateur et de mot de passe pour le fournisseur d'authentification par défaut Learn Internal. Lorsque vous activez l'authentification SAML, un petit lien « Connectez-vous en utilisant... » pour SAML apparaît en bas de cette page. Vous souhaiterez peut-être rediriger automatiquement les utilisateurs vers le serveur d'authentification de l'IdP lorsqu'ils accèdent à la page de connexion Blackboard.

Pour ce faire, vous pouvez accéder à Administrateur système > Authentification et définir l'authentification interne par défaut sur Inactive, ce qui signifie qu'aucune page de connexion ne s'affiche 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 l'exemple de code HTML 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 en utilisant l’authentification interne de Learn via la page de connexion par défaut : /webapps/login/?action=default_login or/webapps/login/login.jsp).