Esta página apresenta uma visão geral do Building Block Security Assertion Markup Language (SAML) 2.0 junto com questões comuns de logon único (SSO) e técnicas de resolução de problemas para o provedor de autenticação SAML.

Se, por qualquer motivo, um arquivo XML de metadados de IdP atualizado/novo for enviado para a GUI do Blackboard Learn na página de Configurações de autenticação SAML na seção Configurações do provedor de identidade de um provedor de autenticação SAML, o provedores de autenticação SAML e o SAML B2 também deverão ser alternados para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver como ‘Ativado’, para garantir que os metadados IdP em cache sejam eliminados e os metadados IdP atualizados sejam totalmente utilizados.


Principais termos

Os seguintes termos e abreviações são usados em todo este guia:

  • SAML: Security Assertion Markup Language
  • IdP: provedor de identidade
  • SP: provedor de serviço
  • ADFS: Serviços de Federação do Active Directory
  • GUI: interface gráfica do usuário. No contexto do Blackboard Learn, isso significa trabalhar dentro do software.

Editar configurações de SAML

Para ajudar a solucionar problemas de autenticação SAML, o Building Block SAML foi atualizado na versão 3200.2.0 para incluir estas opções e definições de configuração:

  • Definir o limite de idade da sessão SAML
  • Escolher um tipo de algoritmo de assinatura
  • Gerar novamente os certificados
  • Alterar o valor de ResponseSkew

Saiba mais sobre como definir as configurações no Building Block SAML


Erros e exceções

Erros/exceções relacionados a SAML são capturados nos seguintes registros:

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

Esses registros devem sempre ser pesquisados ao investigar um problema de autenticação SAML relatado.


Rastreador SAML

Com iterações de resolução de problemas de autenticação SAML 2.0, poderá ser necessário, em algum ponto, confirmar/visualizar os atributos que estão de fato sendo liberados do IdP e enviados ao Learn durante o processo de autenticação. Se os atributos do IdP NÃO estiverem criptografados na resposta SAML, o Complemento rastreador de SAML do navegador Firefox ou o Decodificador de mensagens SAML do Chrome poderá ser usado para visualizar os atributos.


Atributo não mapeado adequadamente

Se o atributo contendo o userName não estiver adequadamente mapeado conforme especificado no campo ID do usuário remoto na seção Mapear atributos SAML na página Configurações de autenticação SAML na GUI do Blackboard Learn, o seguinte evento será registrado no log bb-services ao tentar fazer logon no Blackboard Learn via autenticação SAML:

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

Uma mensagem similar de Erro ao efetuar logon! exibida no navegador: O Blackboard Learn está indisponível para fazer login na sua conta no momento usando logon único. Para obter ajuda, entre em contato com seu administrador.

Uma entrada de Falha de autenticação aparece no registro do 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]

Resolução

Você tem duas opções para resolver o problema. Primeiro, selecione a opção Criar contas se elas não existirem no sistema na página Configurações de autenticação SAML na GUI do Blackboard Learn. Como alternativa, você pode tentar visualizar o valor dos atributos liberados pelo IdP via rastreador SAML ou Registro de depuração se os atributos NÃO estiverem criptografados:

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

e mapear o Attribute Name que tem o AttributeValue desejado para a ID de usuário remoto na página Configurações de autenticação SAML na GUI do Blackboard Learn.


Fonte de dados compatível não selecionada

Os usuários não poderão fazer logon no Blackboard Learn usando autenticação SAML se a Fonte de dados para os usuários não estiver selecionada na seção Configurações do provedor de serviços e Fontes de dados compatíveis na página Configurações de autenticação SAML na GUI do Blackboard Learn. O evento a seguir será registrado no bb-services quando for feita uma tentativa de login no Blackboard Learn via autenticação SAML:

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

A mensagem Erro ao efetuar logon! aparecerá no navegador, bem como Falha de autenticação no registro do 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]

Resolução

  1. Obter o nome de usuário para o usuário que não consegue fazer logon.
  2. Na GUI do Blackboard Learn, acesse Administração do sistema > Usuários e busque o usuário.
  3. Copie a Chave da fonte de dados do usuário.
  4. Navegue para Administração do sistema > Autenticação > "Nome do provedor" > Configurações de SAML > Fontes de dados compatíveis.
  5. Coloque uma marca de seleção ao lado daquela Fonte de dados na coluna Nome e selecione Enviar.

Mensagem de erro "O URL fornecido não está bem formado"

Se o OneLogin for configurado como o IdP para o provedor de autenticação SAML no Blackboard Learn, um erro de O URL fornecido não está bem formado poderá ser exibido na página depois de inserir as credenciais do OneLogin ao tentar fazer logon no Blackboard Learn.

Com o seguinte exibido no 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&gt; - 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

Resolução

  1. Ligue o rastreador SAML do navegador Firefox e replique o problema de logon.
  2. Revise o início do evento 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. Para a linha 1 com a Resposta, observe que Destino= é definido somente para o destinatário.
  4. Peça para o cliente acessar a seção Configuração do seu IdP do OneLogin.
  5. Verifique se o campo Destinatário está em branco.
  6. Copie o valor do URL do ACS (consumidor), cole-o no campo Destinatário e selecione Salvar.

Cenários de problema de IdP/SP

  1. Se aparecer um erro depois de você ter sido redirecionado à página de logon do IdP, os metadados do IdP poderão ser inválidos.
  2. Se aparecer um erro depois de você fazer login na página do IdP, os motivos poderão ser:
    1. O mapeamento de atributos entre o SP e o IdP está incorreto, ou o IdP não retornou um ID de usuário remoto válido.
    2. A resposta SAML do IdP não foi validada pelo SP. Isso pode ter sido causado por:
      • O IdP assina a resposta SAML com um certificado que não é emitido por uma autoridade de certificação válida, e o repositório de chaves do SP não contém esse certificado.
      • O relógio do sistema do SP está incorreto.

Serviços de Federação do Active Directory (ADFS)

Os nomes de atributo diferenciam maiúsculas e minúsculas na seção Mapear atributos SAML na página Configurações de autenticação SAML na GUI do Blackboard Learn. Assim, se a ID do usuário remoto tiver sAMAccountName para o Nome do atributo na página de configurações e o POST SAML real do IdP tiver isso para o Nome do atributo em AttributeStatement:

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

O usuário não poderá fazer logon. O nome de atributo ID do usuário remoto na página Configurações de autenticação SAML precisaria ser alterado de sAMAccountName para SamAccountName.

Aviso de "Recurso não encontrado" ou "Erro ao efetuar logon!"

Essa seção contém alguns dos problemas comuns que podem impedir que um usuário faça login no Learn via autenticação SAML com o ADFS quando a mensagem O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo ou Erro ao efetuar logon! é exibida na GUI do Blackboard Learn.

Problema nº 1

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado ou você não possui permissão para acessá-lo.

Com uma mensagem correspondente no registro 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'

O problema ocorre porque o método noHandlerFound() é usado no código DispatcherServlet.java e não é capaz de localizar/mapear a solicitação de SSO HTTP.

/**
 * No handler found -&gt; 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);
 }
}

Resolução

Isso geralmente ocorre porque a ID da entidade para o SP configurado na GUI do Blackboard Learn está incorreta. Isso pode ser resolvido acessando Administração do sistema > Autenticação > Configurações de autenticação SAML > Configurações do provedor de serviços e atualizando a ID da entidade. Para ADFS, a configuração padrão para a ID da entidade seria https://[Learn Server Hostname]/auth-saml/saml/SSO.

Se uma escola mudar a URL do https://school.blackboard.com padrão para https://their.school.edu, a ID da entidade na GUI do Blackboard Learn na página Configurações de autenticação SAML deveria ser atualizada para https://their.school.edu/auth-saml/saml/SSO.

Problema nº 2

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado ou você não possui permissão para acessá-lo.

Com esta mensagem correspondente no registro 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'

E esta mensagem no registro 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

E esta mensagem exibida no registro 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

O problema ocorre porque, por padrão, o ADFS criptografa os atributos que ele envia usando AES-256 e o tempo de execução Java usado pelo Blackboard Learn não tem suporte de fábrica para AES-256.

Resolução

Uma opção de resolução universal é abrir um PowerShell no servidor ADFS e definir a parte confiável criada para o Blackboard Learn para enviar os atributos como não criptografados. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação. Também torna a depuração de quaisquer problemas mais fácil, uma vez que os atributos podem ser visualizados usando ferramentas de depuração, como o complemento de rastreador SAML do navegador Firefox e uma reinicialização do sistema Blackboard Learn não é necessária. Para definir a parte confiável criada para o Blackboard Learn enviar os atributos não criptografados, abra um PowerShell e execute o comando a seguir, substituindo TargetName pelo nome da Confiança da terceira parte confiável que está no Console de gerenciamento do ADFS em Relacionamentos de confiança > Confianças de parte confiável.

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

Depois dessa alteração, será preciso reiniciar o serviço do ADFS com o comando: Restart-Service ADFSSRV

Problema nº 3

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com qualquer uma das opções, estes eventos relacionados SAML com correspondências similares aparecem no registro 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 na posição 1 de 10 na cadeia de filtros adicional; acionando o Filtro: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Não existe nenhuma HttpSession no momento
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Nenhum SecurityContext estava disponível na HttpSession: null. Um novo será criado.
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 na posição 2 de 10 na cadeia de filtros adicional; acionando o Filtro: '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 na posição 3 de10 na cadeia de filtros adicional; acionando o Filtro: '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 na posição 4 de 10 na cadeia de filtros adicional; acionando o Filtro: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Verificando correspondência da solicitação: '/saml/login'; com '/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 na posição 1 de 1 na cadeia de filtros adicional; acionando o Filtro: 'SAMLEntryPoint'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Solicitação para URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Solicitação para URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContext está vazio ou os conteúdos são anônimos – o contexto não será armazenado em HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContextHolder agora removido, conforme processamento solicitado concluído
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO na posição 1 de 10 na cadeia de filtros adicional; acionando o Filtro: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - HttpSession retornou um objeto nulo para SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Nenhum SecurityContext estava disponível na HttpSession: org.apache.catalina.session.StandardS[email protected] Um novo será criado.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO na posição 2 de 10 na cadeia de filtros adicional; acionando o Filtro: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO na posição 3 de 10 na cadeia de filtros adicional; acionando o Filtro: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO na posição 4 de 10 na cadeia de filtros adicional; acionando o Filtro: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Verificando a correspondência da solicitação: '/saml/sso'; com '/saml/login/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Verificando a correspondência da solicitação: '/saml/sso'; contra '/saml/logout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Verificando a correspondência da solicitação: '/saml/sso'; contra '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Verificando a correspondência da solicitação: '/saml/sso'; contra '/saml/sso/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO na posição 1 de 1 na cadeia de filtros adicional; acionando o Filtro: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Tentativa de autenticação usando org.springframework.security.saml.SAMLAuthenticationProvider
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Encaminhamento para /
INFO   | jvm 1    | 2016/09/06 20:33:07 | - DispatcherServlet com o nome 'saml' processando a solicitação POST para [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Nenhum mapeamento encontrado para a solicitação HTTP com o URI [/auth-saml/saml/SSO] no DispatcherServlet com o nome 'saml'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - O SecurityContext está vazio ou os conteúdos são anônimos – o contexto não será armazenado na HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Solicitação concluída com sucesso
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Ignorar invocação em
INFO   | jvm 1    | 2016/09/06 20:33:07 | - SecurityContextHolder agora removido, conforme processamento de solicitação concluído

Ou estas exceções SAML similares no registro 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

O problema ocorre quando o servidor ADFS e o servidor de aplicativo Blackboard Learn têm um deslocamento de tempo perto do padrão de 60 segundos ou superior a ele.

Resolução

Existem duas opções para resolver o problema:

  1. Sincronizar manualmente os relógios dos servidores de aplicativo do Blackboard Learn e do servidor ADFS. Para o Blackboard Learn, a hora e o fuso horário atuais do servidor podem ser visualizados em um navegador da web adicionando /webapps/portal/healthCheck ao fim de um URL do Blackboard Learn.

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

    Uma instituição pode usar o URL acima para comparar o relógio e o fuso horário do sistema do Blackboard Learn com aqueles do servidor ADFS e então ajustar esses itens conforme necessário no servidor ADFS para eles estejam sincronizados ao site do Blackboard Learn.

  2. O viés pode ser ajustado em securityContext.xml para contornar o problema:
    1. Faça uma cópia de backup do arquivo:

      /usr/local/blackboard/content/vi/BBLEARN/plugins/bb-auth-provider-saml/webapp/WEB-INF/config/saml/securityContext.xml

    2. Altere:

      <!-- SAML 2.0 WebSSO Assertion Consumer -->
      <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>

      para:

      <!-- SAML 2.0 WebSSO Assertion Consumer -->
      <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl">
      <property name="responseSkew"value="60000"/><!-- 100 hours -->
      </bean>

Problema nº 4

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com as seguintes exceções no registro 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]

Resolução
  1. Navegue até o Painel de administração.
  2. Em Building Blocks, selecione Building Blocks.
  3. Selecione Ferramentas instaladas.
  4. Localize Provedor de autenticação – SAML na lista. Abra o menu e selecione Configurações.
  5. Em Configurações do algoritmo de assinatura, escolha SHA-256 na lista. Depois de selecionar o Tipo de algoritmo de assinatura, reinicie o Building Block SAML para aplicar as novas configurações.
  6. Clique em Enviar para salvar as alterações.

Problema nº 5

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com as seguintes exceções no registro 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

Conforme definido na exceção SAML acima, o elemento NameID está ausente do Assunto na mensagem Resposta. O problema costuma ocorrer quando a NameID não configurada como um Tipo de declaração de saída em uma Regra de declarações para a Confiança da parte confiável no IdP do ADFS da instituição ou a Regra de declarações para a NameID não está na ordem adequada para a Confiança da parte confiável no IdP do ADFS da instituição, o que, por sua vez, faz o elemento NameID estar ausente no Assunto na mensagem de Resposta.

Exemplo: o elemento NameID está ausente

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

Exemplo: o elemento NameID está presente

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

Você pode usar o complemento rastreador de SAML do Firefox para visualizar o Assunto na mensagem de Resposta.

Resolução

Existem três métodos para resolver esse problema.

  1. Confirme se as etapas do Guia de configuração SAML B2 para ADFS foram adequadamente seguidas e faça as alterações necessárias para transformar uma declaração de chegada para a Confiança da terceira parte confiável para o IdP do ADFS:
    1. Selecione Editar regra de declarações.
    2. Selecione Adicionar regra.
    3. Na página Selecionar modelo de regra, selecione Transformar uma declaração de entrada para o modelo de regra de declaração e, em seguida, selecione Avançar.
    4. Na página Configurar regra, no campo Nome de regra de declaração, digite Transformar e-mail em ID de nome.
    5. O tipo de declaração de entrada deve ser SamAccountName (deve corresponder ao Tipo de declaração de saída criado inicialmente na regra Transformar nome de usuário em NameID).
    6. O Tipo de declaração de saída é ID do nome.
    7. O formato ID de nome de saída é E-mail.
    8. Confirme que Passar por todos os valores de declaração esteja selecionado e selecione Concluir.
    9. Selecione OK para salvar a regra e OK novamente para concluir os mapeamentos de atributo.
  2. Para a ordem de Regras de declarações usadas para o IdP do ADFS, garanta que nenhuma regra opcional ocorra antes da regra com o elemento NameID.
  3. Se estiver usando um atributo personalizado, garanta que o elemento NameID esteja na Confiança da terceira parte confiável já que o Learn ainda espera que seu IdP do ADFS libere um valor de NameID.

Problema nº 6

Quando conectado ao Blackboard Learn usando autenticação SAML, o usuário tenta fazer logout clicando no botão Sair no lado esquerdo da página e então clica no botão Encerrar sessão de SSO, um Erro ao efetuar logon! é exibido imediatamente.

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

Com a seguinte exceção no registro 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

O erro ocorre devido à configuração Tipo de serviço de logout único na página Configurações de SAML.

Resolução

A configuração precisa ser feita no Blackboard Learn e no servidor ADFS.

Para ADFS como o IdP, selecione a configuração Publicar somente e remova o ponto de extremidade Redirecionar para a Confiança da terceira parte confiável da instância do Learn no servidor ADFS.

  1. No Learn, navegue para Administração > Autenticação > (Nome do provedor) > Configurações de SAML > Tipo de serviço de logout único.
  2. Selecione Publicar e desmarque a caixa de seleção Redirecionar.
  3. No ADFS Server, vá para a Confiança da terceira parte confiável da sua instância do Learn.
  4. Selecione Propriedades > Pontos de extremidade. Dois pontos de extremidade de logout SAML são listados.
  5. Remova o ponto de extremidade Redirecionar. Selecione Remover ponto de extremidade para removê-lo, então selecione Aplicar e OK.

Depois de fazer as alterações acima no Learn e no ADFS Server, o botão de logout Encerrar sessão de SSO operará para desconectar o usuário adequadamente.

Problema nº 7

Depois de inserir as credenciais de logon na página de logon do ADFS, uma mensagem de Erro ao efetuar logon! é exibida quando redirecionado para o Learn.

Com a seguinte exceção SAML no registro 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

Resolução

A partir do Blackboard Learn 3200.0.0, agora existe uma opção de gerar novamente o certificado de criptografia SAML navegando para Administração do sistema > Building Blocks > Provedor de autenticação – SAML > Configurações > Gerar o certificado novamente. O problema Erro ao efetuar logon! poderá ocorrer se o botão Gerar o certificado novamente for selecionado depois que os metadados do SP já tiverem sido carregados para a Confiança da terceira parte confiável para o site do Learn no servidor ADFS. Para resolver o problema:

  1. Navegue para Administração do sistema > Autenticação > [Nome do provedor SAML] > Configurações de SAML.
  2. Selecione Gerar ao lado de Metadados do provedor de serviços para salvar o novo arquivo de metadados.
  3. Acesse seu servidor ADFS e carregue os novos metadados do SP para a Confiança da terceira parte confiável para seu site do Learn.

Se você gerar um novo certificado nas configurações de B2, será necessário alternar o SAML B2 para Inativo e, então, voltar para Ativo para forçar a alteração. Depois, você pode retornar às configurações do provedor e gerar os novos metadados para importar para o IDP. Se você não alternar as configurações, o certificado antigo ainda poderá ser incluído ao gerar novos metadados. O IDP não será atualizado e, na próxima vez que o Learn o reiniciar, ele apresentará o novo certificado. A autenticação SAML será interrompida por causa dessa incompatibilidade.

Metadados de federação

Com o Active Directory Federation Services (ADFS), uma vez que os metadados para uma federação do ADFS geralmente localizados em https://[ADFS Server Hostname]/FederationMetadata/2007-06/FederationMetadata.xml incluem um elemento incompatível com SAML 2.0, os metadados precisam ser editados para excluir o elemento incompatível antes que ele seja carregado para a seção Configurações do provedor de identidade na página Configurações de autenticação SAML na GUI do Blackboard Learn. Se os metadados com o elemento incompatível forem carregados, ocorrerá um erro ao selecionar o link de logon SAML na página de logon do Blackboard Learn: Metadados para entidade [entity] e função {} não foram encontrados. Para referência, a ID de erro é [error ID].

E o rastreamento de pilha Java correspondente para a ID de erro no registro bb-services tem o seguinte:

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&gt; - 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]

Resolução

Uma vez que o local padrão de metadados para uma federação do ADFS é https://[ADFS server hostname]/FederationMetadata/2007-06/FederationMetadata.xml:

  1. Baixe esse arquivo e abra-o em um editor de texto. Exclua com cuidado a seção que começa com <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"&gt; ... </X509Data></KeyInfo> e termina com </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&gt; 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. Carregue o arquivo XML de metadados atualizado na GUI do Blackboard Learn na página Configurações de autenticação SAML na seção Configurações do provedor de identidade.
  3. Alterne o provedor de autenticação SAML e SAML B2 para Inativo/Disponível, enquanto o provedor de autenticação SAML estiver no status "Ativado".

Se uma instituição estiver testando a autenticação SAML em um site do Blackboard Learn e tiver diversos provedores de autenticação SAML que compartilham o mesmo arquivo XML de metadados do IdP do ADFS no site do Blackboard Learn, mesmo que os outros provedores de autenticação SAML estejam definidos como Inativos, eles também precisarão ter o arquivo XML de metadados atualizado carregado na GUI do Blackboard Learn na página de Configurações de autenticação SAML na seção Configurações do provedor de identidade. O SAML B2, então, deve ser alternado para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver “Ativado”, para garantir que o arquivo XML de metadados atualizado seja reconhecido em todo o sistema.

Método incorreto de pesquisa de usuário

Depois de inserir as credenciais de logon na página de logon do ADFS, o usuário é redirecionado para a GUI do Blackboard Learn, mas não é conectado ao Blackboard Learn.

O ÚNICO evento relacionado a autenticação SAML no registro bb-services é:

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

Resolução

  1. Faça logon no Blackboard Learn como administrador usando a autenticação interna do Blackboard Learn padrão.
  2. Acesse Administração do sistema > "Nome do provedor de autenticação SAML" > Editar.
  3. Altere o Método de pesquisa do usuário de Uid em lote para Nome de usuário.

Botão de logout Encerrar sessão de SSO extra

O ADFS tenta adicionar um botão de logout Encerrar sessão de SSO extra na página Encerrar todas as sessões? que é exibida depois de selecionar primeiro o botão de logout no alto à direita na GUI do Blackboard Learn.

Isso é feito adicionando um SingleLogoutService extra ao arquivo de Metadados do IdP:

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

Uma vez que essa é uma configuração de IdP SAML B2 opcional e a assinatura que está sendo fornecida no Ponto de extremidade de redirecionamento não está correta, ocorrerá um erro ao selecionar o botão Encerrar sessão de SSO na página Encerrar todas as sessões?: A mensagem SAML de entrada falhou na validação de segurança. A validação da assinatura simples da solicitação falhou para o emissor do contexto. Para referência, a ID de erro é [error ID].

O rastreamento de pilha Java correspondente para a ID de erro no registro bb-services tem:

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&gt; - 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]

Resolução

  1. Acesse o ADFS Server e vá para a Confiança da terceira parte confiável para a instância do Blackboard Learn.
  2. Selecione a guia Propriedades > Pontos de extremidade.
  3. Na guia Pontos de extremidade, haverá dois Pontos de extremidade de logout SAML.
  4. Remova o ponto de extremidade Redirecionar.
  5. Selecione Remover ponto de extremidade para removê-lo, então selecione Aplicar e OK.

Depois de remover o ponto de extremidade Redirecionar, o botão Encerrar sessão de SSO funcionará desconectando o usuário adequadamente.

Como visualizar registros de aplicativo com o visualizador de eventos

Ao solucionar problemas de autenticação SAML do ADFS, pode ser necessário que uma instituição também revise os registros de aplicativo do ADFS em Visualização de eventos no servidor ADFS para obter mais informações. Isso é particularmente necessário quando a resposta de SAML do ADFS Server tem um status de Solicitação negada, conforme visto a seguir:

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

A resposta SAML pode ser visualizada usando o complemento de rastreador SAML do navegador Firefox.

O status Solicitação negada em uma resposta geralmente indica que ocorreu um problema quando o IdP (ADFS) tentou entender a resposta e processar o resultado que o SP (Blackboard Learn) forneceu.

Para visualizar os registros de aplicativo ADFS com o Visualizador de eventos:

  1. Abra o Visualizador de eventos no ADFS Server.
  2. No menu Visualizar, selecione Mostrar registros analíticos e de depuração.
  3. Na árvore do console, navegue para Registros de aplicativo e serviço > Rastreamento do AD FS > Depurar.

Azure Active Directory

O Azure AD é o serviço de gerenciamento de identidade e diretório baseado em nuvem da Microsoft (MS).

Enviar primeira parte do e-mail

Se uma instituição estiver usando o Azure AD como IdP e quiser utilizar apenas a primeira parte do nome de usuário de e-mail do Azure AD como nome de usuário do Blackboard Learn, ela poderá configurar o IdP do Azure AD para usar a função especial ExtractMailPrefix() para remover o sufixo de domínio do e-mail ou do nome principal de usuário, fazendo apenas a primeira parte do nome de usuário ser passada (por exemplo. "joesmith", em vez de joesmith@example.com).

Se a ID de usuário remoto do Blackboard Learn for urn:oid:1.3.6.1.4.1.5923.1.1.1.6, a configuração de Atributo para o IdP do Azure seria parecida com esta:

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

Assim, com o nome de usuário de e-mail joesmith@example.com de exemplo, ele seria passado como isto na asserção SAML do IdP do Azure para o Blackboard Learn:

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

Informações adicionais sobre o uso da função ExtractMailPrefix() estão disponíveis na página de documentação do MS Azure.

IdP do Azure AD atualizando o certificado

Depois de inserir as credenciais de logon na página de logon do MS Azure AD, um Erro ao efetuar logon! poderá ser exibido depois do redirecionamento à GUI do Blackboard Learn.

Com a seguinte exceção no registro 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

Isso é causado pela atualização do certificado pelo IdP do MS Azure AD, mas o XML dos metadados usados pelo SP do Blackboard Learn não é ajustado para refletir o novo certificado.

Resolução

  • O novo arquivo XML de metadados com o novo certificado precisará ser atualizado na página Configurações de SAML na GUI do Blackboard Learn para o provedor de autenticação.
  • O SAML B2 e o provedor de autenticação então precisarão ser alternados para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver “Ativado”, para que os metadados sejam atualizados com o novo certificado aplicado.
  • Se um site do Blackboard Learn tiver vários provedores de autenticação que compartilhem o mesmo certificado subjacente para o mesmo código de entidade do IdP subjacente, TODOS esses provedores de autenticação precisarão ser atualizados.

A Microsoft indicou que atualizará os certificados a cada seis semanas a partir de agora, e essas atualizações não serão anunciadas.


Logon único iniciado pelo IdP

Se um usuário fizer login no portal do usuário inicialmente e então selecionar o aplicativo para o site do Blackboard Learn, uma nova guia do navegador será aberta para exibir a mensagem: O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo.

Com os eventos relacionados SAML correspondentes no stdout-stderr.log:

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: [email protected] 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

A seção Configurações do provedor de serviços da página Configurações de autenticação SAML mudou e a opção Habilitar SSO automático deve ser marcada para permitir que um usuário acesse o Blackboard Learn usando seu próprio portal. Se estiver habilitada, o URL do ACS também será alterado para incluir um alias.


Erro de documento incorreto

Depois de inserir as credenciais de logon na página de logon do provedor de autenticação SAML, um Erro ao efetuar logon! poderá ser exibido depois do redirecionamento à GUI do Blackboard Learn.

Com a seguinte DOMException e WRONG_DOCUMENT_ERR no registro 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&gt; - 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]

O motivo para o problema ocorrer é que outro B2/Projeto mudou o valor da propriedade do sistema javax.xml.parsers.DocumentBuilderFactory de org.apache.xerces.jaxp.DocumentBuilderFactoryImpl para com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.

Resolução temporária

Até que uma correção seja liberada, as opções de resolução temporárias são:

  1. Reinicie os serviços Bb em cada nó.
  2. Desative a criptografia de resposta SAML no lado do IdP. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação.

Nenhuma opção para adicionar SAML à ordem do provedor

Ao configurar a autenticação SAML, uma instituição poderá perceber que não existe uma opção de adicionar um provedor de autenticação SAML na seção Ordem do provedor na GUI do Blackboard Learn ao navegar para Administração do sistema > Building Blocks: Autenticação > Ordem do provedor.

O motivo pelo qual não há uma opção de adicionar um provedor de autenticação SAML à Ordem do provedor é que provedores do tipo de redirecionamento como CAS e SAML entregam a autenticação à origem remota. Esses não estão listados na Ordem do provedor porque são considerados a origem autoritativa para autenticação e tratam das próprias falhas de autenticação.


Testar conexão SAML

Começando com a versão Q4 2016 do Blackboard Learn, agora há uma opção de testar a conexão de um provedor SAML na seção Autenticação na GUI do Blackboard Learn. O teste de conexão verificará os seguintes itens:

  • Analisar metadados de IdP
  • Conectar-se ao IdP
  • Receber a resposta SAML
  • Analisar a resposta SAML
  • Correspondência de ID de usuário remoto
  • Login no Blackboard Learn

Para testar a conexão para o provedor de autenticação SAML:

  1. Faça logon no Blackboard Learn como administrador.
  2. Navegue para Administração do sistema > Building Blocks: Autenticação > "Nome do provedor SAML" > Testar conexão.
  3. Insira as credenciais de logon do IdP, se solicitado.

O recurso Testar conexão pode ser usado em lugar de habilitar manualmente o registro de depuração SAML no Blackboard Learn por vários motivos.

O valor da ID da entidade do provedor de identidade exibido na página de saída de Testar conexão é obtido do elemento Emissor no SAML POST do IdP para o Blackboard Learn depois que o usuário tiver sido autenticado:

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

Os valores de Atributo SAML exibidos na página de saída Testar conexão na seção Resposta SAML são extraídos dos elementos Assunto e AttributeStatement no SAML POST do IdP para o Blackboard Learn depois que o usuário foi autenticado:

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


Crie um provedor de autenticação SAML e um IdP para teste

Siga as etapas abaixo para criar um provedor de identidade (IdP) usando a solução de autenticação SSO gratuita da Centrify.

Esse IdP então pode ser configurado como o provedor de autenticação SAML em um provedor de serviço (SP) do Blackboard Learn:

Provedor de serviço do Blackboard Learn

  1. Faça logon na GUI do Blackboard Learn como administrador e navegue para Administração do sistema > Autenticação.
  2. Selecione Criar provedor > SAML.
  3. Insira as seguintes configurações:
    • Nome > SAML ou o que você quiser.
    • Provedor de autenticação > Inativo (por enquanto).
    • Método de pesquisa do usuário > Nome de usuário
    • Restringir por nome de host > Usar este provedor para qualquer nome de host
    • Texto do link > Login Centrify SAML
  4. Selecione Salvar e configurar.
  5. No campo ID da entidade, defina isso para qualquer valor que quiser (porém, se alterá-lo, você deverá fornecer os metadados do provedor de serviços atualizados para o provedor de identidade). Simplesmente copie/cole o URL do ACS.
  6. Em Metadados do provedor de serviços, selecione Gerar e salve o arquivo na área de trabalho.
  7. Em Fonte de dados, recomendamos criar uma nova origem de dados para esse CENTRIFY nomeado, caso contrário, use SYSTEM ou o que você escolher
  8. Ao lado de Habilitar provisionamento JIT, marque essa caixa para que uma conta seja criada automaticamente ao tentar fazer logon usando esse provedor de autenticação SAML se o usuário não existir. Se o provisionamento JIT não estiver selecionado, o usuário no Blackboard Learn precisará ser criado manualmente.
  9. Na lista Fontes de dados compatíveis, selecione as fontes de dados com as quais esse provedor de autenticação deve ser compatível.
  10. Selecione Provedor de identidade de ponto para o Tipo de provedor de identidade.
  11. Ignore os Metadados do provedor de identidade por enquanto; você carregará o arquivo depois que ele tiver sido criado na seção do IdP Centrify.
  12. Para a seção Mapear atributos SAML, use NameID para a ID do usuário remoto.
  13. Selecione Enviar.

Provedor de identidade Centrify

  1. Acesse https://www.centrify.com/express/identity-service e selecione Iniciar agora na coluna Express na parte inferior da página.
  2. Insira suas informações para inscrever-se e selecione Iniciar agora.
  3. Você receberá um e-mail de boas-vindas com suas credenciais de administrador. Use-as para fazer login no https://cloud.centrify.com.
  4. Selecione Ignorar na janela Bem-vindo ao Centrify Identify Service.
  5. Na guia Aplicativos na parte superior da página, selecione o botão Adicionar aplicativos da web.
  6. Na guia Personalizado, role para baixo e selecione o botão Adicionar para SAML. Selecione Sim.
  7. Selecione Fechar na parte inferior da janela Adicionar aplicativos da web.
  8. Vá para a guia Aplicativos. Na seção Configurações do aplicativo, selecione o botão Carregar metadados do SP e carregue o arquivo criado na Etapa 6 da seção do SP do Blackboard Learn.
  9. O URL de serviço do consumidor de asserção deve ser preenchido automaticamente depois do carregamento dos metadados do SP.
  10. Desmarque Criptografar asserção. Isso permite que os atributos que estão sendo liberados do IdP e enviados ao Blackboard Learn sejam visualizados usando o complemento rastreador SAML do navegador Firefox ou o Decodificador de mensagens SAML do Chrome. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação.
  11. Role para baixo e selecione Baixar metadados SAML do provedor de identidade. Salve o arquivo em seu computador.
  12. Selecione Salvar e vá para a próxima seção.
  13. Insira um nome para a seção Descrição. Selecione Salvar e vá para a próxima seção.
  14. Na seção Acesso do usuário, selecione Todos e Administrador do sistema. Selecione Salvar.
  15. Não faça nenhuma seleção na seção Política.
  16. Para a seção Mapeamento da conta, confirme que userprincipalname foi inserido para o nome do campo Serviço de diretório.
  17. Para a seção Avançado, adicione a seguinte linha à parte inferior do script usado para gerar uma asserção SAML para o aplicativo:

    O script completo será:

    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"));

    Isso permitirá ao IdP Centrify liberar a AttributeStatement com a ID do usuário no POST SAML.

    Exemplo:

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

    Saiba mais sobre como especificar elementos de asserção no script SAML Centrify

  18. Selecione Salvar.
  19. Não será preciso fazer qualquer alteração às seções restantes (Gateway de aplicativo, Changelog e Fluxo de trabalho).
  20. Na guia Aplicativos, confirme que o aplicativo SAML tenha sido implantado de modo automático.
  21. Na guia Usuários, selecione Adicionar usuários, insira as informações da conta e selecione Criar usuário.
  22. Faça login novamente na GUI do Blackboard Learn como administrador, navegue para Administração do sistema > Autenticação > Nome do provedor de autenticação SAML > Configurações de SAML > Configurações do provedor de identidade, carregue o arquivo de Metadados do IdP salvo na sua área de trabalho na etapa 13 e selecione Enviar.

O usuário do IdP Centrify que foi criado agora pode fazer logon no Blackboard Learn via SAML selecionando o provedor de autenticação na página de logon e logout do Blackboard Learn usando o botão de logout Encerrar sessão de SSO extra na página Encerrar todas as sessões? exibida depois de selecionar o botão de logout no alto à direita do Blackboard Learn.


Altere o texto na página de logout Encerrar sessão de SSO

Uma instituição pode consultar se é possível alterar o texto na página de logout da Encerrar sessão de SSO. É possível alterar o texto na página de logout Encerrar sessão de SSO editando o pacote de idioma:

  1. Abra o arquivo do pacote de idioma.
  2. Vá para auth-provider-saml/src/main/webapp/WEB-INF/bundles/bb-manifest-en_US.properties.
  3. Atualize as chaves de mensagem:

    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


Redirecione os usuários à página de logon do IdP

A página de logon padrão do Blackboard Learn apresenta campos de nome de usuário e senha para o provedor de autenticação do Learn interno. Ao habilitar a autenticação SAML, um pequeno link "Fazer logon usando…" para SAML aparecerá na parte inferior dessa página, assim, talvez você queira redirecionar os usuários para o servidor de autenticação do IdP automaticamente quando eles acessarem a página de logon do Learn.

Uma opção para fazer isso é navegar para Administração do sistema > Autenticação e definir a autenticação do Learn interno como Inativa, o que significa que uma página de logon não será mais exibida e o usuário será imediatamente redirecionado ao logon do SAML. O problema com essa opção é que ela substitui o URL de logon padrão e impede qualquer usuário não SAML de fazer logon.

Para evitar esse problema e atingir quase o mesmo resultado, use uma página de logon personalizada. Os usuários são redirecionados à página de logon do IdP do provedor de autenticação SAML do provedor, mas o link de logon padrão também deve poder ser utilizado.

  1. Verifique se a autenticação do Learn interno está ativa
  2. Na página de logon padrão, copie o logo do redirecionamento do provedor, por exemplo, Entrar usando… SAML. Clique com o botão direito do mouse e selecione Copiar local do link.
  3. Navegue para Administração do sistema > Comunidades > Configurações temáticas e temas > Personalizar página de logon.
  4. Selecione Baixar ao lado da Página de logon padrão para baixar o arquivo JSP de logon padrão.
  5. Abra o arquivo de JSP com um editor de texto. Adicione o seguinte HTML de amostra ao arquivo JSP de logon e substitua o texto do URL pelo URL que foi copiado na Etapa 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&gt; if you are not automatically redirected.
    </BODY>
    </HTML>

  6. Navegue para Personalizar página de logon no Learn mais uma vez. Selecione Usar página personalizada e então carregue o arquivo JSP de logon atualizado.
  7. Depois de fazer as alterações, selecione Visualização em Personalizar página de logon para confirmar que o redirecionamento está funcionando adequadamente.

Os usuários que forem para o URL principal agora serão redirecionados para a página de logon para o provedor de autenticação SAML. Os administradores ainda podem fazer logon usando a autenticação interna do Learn por meio da página de logon padrão: /webapps/login/?action=default_login ou /webapps/login/login.jsp).

Saiba mais sobre como personalizar a página de login na experiência Ultra