This page provides a general overview of the Security Assertion Markup Language (SAML) 2.0 Building Block along with common Single Sign-On (SSO) issues and troubleshooting techniques for the SAML authentication provider.
If for any reason an updated/new IdP metadata XML file is uploaded in the Blackboard Learn GUI on the SAML Authentication Settings page in the Identity Provider Settings section for a SAML authentication provider, the SAML B2 and that SAML authentication provider should also be toggled Inactive/Available, while having the SAML authentication provider in 'Active' status, to ensure any cached IdP metadata is cleared out and the updated IdP metadata is fully utilized.
The following terms and abbreviations are used throughout this guide:
More on how to configure settings in the SAML Building Block
SAML related errors/exceptions are captured in the following logs:
These logs should always be searched when investigating a reported SAML authentication issue.
With SAML 2.0 authentication troubleshooting iterations, at some point it may be necessary to confirm/view the attributes that are actually being released from the IdP and sent to Learn during the authentication process. If the attributes from the IdP are NOT encrypted in the SAML response, the Firefox browser SAML tracer Add-on or Chrome SAML Message Decoder can be used to view the attributes.
If the attribute containing the userName is not properly mapped as specified in the Remote User ID field in the Map SAML Attributes section on the SAML Authentication Settings page in the Blackboard Learn GUI, the following event will be logged in the bb-services log when attempting to login to Blackboard Learn via SAML authentication:
2016-06-28 12:48:12 -0400 - userName is null or empty
A similar Sign On Error! message displayed in the browser: Blackboard Learn is currently unable to log into your account using single-sign on. Contact your administrator for assistance.
An Authentication Failure entry appears in the bb-services log:
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]
You have two options to resolve the issue. First, select the Create accounts if they don't exist in the system option on the SAML Authentication Settings page in the Blackboard Learn GUI. Alternatively, you can attempt to view the value of the attributes released by the IdP via SAML tracer or Debug Logging if the attributes are NOT encrypted:
<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>
and map the Attribute Name that has the desired AttributeValue to the Remote User ID on the SAML Authentication Settings page in the Blackboard Learn GUI.
Users won't be able to login to Blackboard Learn via SAML authentication if the Data Source for the users is not selected in the Services Provider Settings > Compatible Data Sources section on the SAML Authentication Settings page in the Blackboard Learn GUI. The following event will be logged in the bb-services log when attempting to log in to Blackboard Learn via SAML authentication:
2016-09-23 12:33:13 -0500 - userName is null or empty
The Sign On Error! message appears in the browser, as well as the Authentication Failure in the bb-services log:
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]
If OneLogin is configured as the IdP for the SAML authentication provider in Blackboard Learn, a Given URL is not well formed error may be displayed on the page after entering the OneLogin credentials when attempting login to Blackboard Learn.
With the following displayed in the bb-services-log:
2016-09-16 09:43:40 -0400 - Given URL is not well formed<P><span class="captionText">For reference, the Error ID is 17500f44-7809-4b9f-a272-3bed1d1af131.</span> - java.lang.IllegalArgumentException: Given URL is not well formed
at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:120)
at org.opensaml.util.SimpleURLCanonicalizer.canonicalize(SimpleURLCanonicalizer.java:87)
at org.opensaml.common.binding.decoding.BasicURLComparator.compare(BasicURLComparator.java:57)
at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.compareEndpointURIs(BaseSAMLMessageDecoder.java:173)
at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:213)
at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72)
at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:80)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
[SNIP]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.MalformedURLException: no protocol: {recipient}
at java.net.URL.<init>(URL.java:593)
at java.net.URL.<init>(URL.java:490)
at java.net.URL.<init>(URL.java:439)
at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:77)
... 203 more
Review the beginning of the SAML POST event:
<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]
The attribute names are case sensitive in the Map SAML Attributes section on the SAML Authentication Settings page in the Blackboard Learn GUI. So if the Remote User ID has sAMAccountName for the Attribute Name on the settings page and the actual SAML POST from the IdP has this for the Attribute Name in the AttributeStatement :
<AttributeStatement>
<Attribute Name="SamAccountName>
<AttributeValue>Test-User</AttributeValue>
</Attribute>
</AttributeStatement>
The user will not be able to login. The Remote User ID attribute name value on the SAML Authentication Settings page would need to be changed from sAMAccountName to SamAccountName .
This section contains some of the common problems that may prevent a user from logging into Learn via SAML authentication with ADFS when The specified resource was not found, or you do not have permission to access it or Sign On Error! message is displayed in the Blackboard Learn GUI.
After entering the login credentials on the ADFS login page, an error may be displayed after being redirected to the Blackboard Learn GUI: The specified resource was not found, or you do not have permission to access it.
With a corresponding message in the stdout-stderr log:
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'
The problem occurs because the noHandlerFound() method is used in the DispatcherServlet.java code and is unable to locate/map the HTTP SSO request.
/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
"] in DispatcherServlet with name '" + getServletName() + "'");
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
This typically occurs because the Entity ID for the SP configured in the Blackboard Learn GUI is incorrect. This can be resolved by navigating to System Admin > Authentication > SAML Authentication Settings > Service Provider Settings and updating the Entity ID . For ADFS, the default configuration for the Entity ID would be https://[Learn Server Hostname]/auth-saml/saml/SSO .
If a school changes their URL from the default https ://school.blackboard. com to https ://their.school. edu, the Entity ID in the Blackboard Learn GUI on the SAML Authentication Settings page should be updated to https ://their.school .edu/auth-saml/saml/SSO.
After entering the login credentials on the ADFS login page, an error may be displayed after being redirected to the Blackboard Learn GUI: The specified resource was not found, or you do not have permission to access it.
With this corresponding message in the stdout-stderr log:
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'
And this message in the catalina log:
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
And this message displayed in the bb-services log:
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
The problem occurs because by default ADFS encrypts the attributes it sends using AES-256 and the Java runtime used by Blackboard Learn doesn't support AES-256 out of the box.
A universal resolution option is to open a PowerShell on the ADFS server and set the relying party created for Blackboard Learn to send the attributes as unencrypted. As the whole communication is over SSL, this will not reduce the security of the authentication. It also makes debugging of any issues easier as the attributes can be viewed using debugging tools such as the Firefox browser SAML tracer Add-on and a restart of the Blackboard Learn system is not required. To set the relying party created for Blackboard Learn to send the attributes as unencrypted, open a PowerShell and execute the following command, replacing TargetName with the name of the Relying Party Trust that is in the ADFS Management Console under Trust Relationships > Relying Party Trusts .
set-ADFSRelyingPartyTrust –TargetName "yourlearnserver.blackboard.com" –EncryptClaims $False
After this change the ADFS service will need to be restarted with the command: Restart-Service ADFSSRV
After entering the login credentials on the ADFS login page, an error may be displayed after being redirected to the Blackboard Learn GUI: The specified resource was not found, or you do not have permission to access it or Sign On Error!
With either, these similar corresponding SAML related events appear in the stdout-stderr log:
INFO | jvm 1 | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO | jvm 1 | 2016/09/06 20:33:04 | - No HttpSession currently exists
INFO | jvm 1 | 2016/09/06 20:33:04 | - No SecurityContext was available from the HttpSession: null. A new one will be created.
INFO | jvm 1 | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO | jvm 1 | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO | jvm 1 | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO | jvm 1 | 2016/09/06 20:33:04 | - Checking match of request : '/saml/login'; against '/saml/login/**'
INFO | jvm 1 | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 1 in additional filter chain; firing Filter: 'SAMLEntryPoint'
INFO | jvm 1 | 2016/09/06 20:33:04 | - Request for URI http
://www.w3.
org/2000/09/xmldsig#rsa-sha1
INFO | jvm 1 | 2016/09/06 20:33:04 | - Request for URI http
://www.w3.
org/2000/09/xmldsig#rsa-sha1
INFO | jvm 1 | 2016/09/06 20:33:04 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO | jvm 1 | 2016/09/06 20:33:04 | - SecurityContextHolder now cleared, as request processing completed
INFO | jvm 1 | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO | jvm 1 | 2016/09/06 20:33:07 | - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO | jvm 1 | 2016/09/06 20:33:07 | - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@6708a718. A new one will be created.
INFO | jvm 1 | 2016/09/06 20:33:07 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO | jvm 1 | 2016/09/06 20:33:07 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO | jvm 1 | 2016/09/06 20:33:07 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO | jvm 1 | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO | jvm 1 | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO | jvm 1 | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO | jvm 1 | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO | jvm 1 | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO | jvm 1 | 2016/09/06 20:33:07 | - Authentication attempt using org.springframework.security.saml.SAMLAuthenticationProvider
INFO | jvm 1 | 2016/09/06 20:33:07 | - Forwarding to /
INFO | jvm 1 | 2016/09/06 20:33:07 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO | jvm 1 | 2016/09/06 20:33:07 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO | jvm 1 | 2016/09/06 20:33:07 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO | jvm 1 | 2016/09/06 20:33:07 | - Successfully completed request
INFO | jvm 1 | 2016/09/06 20:33:07 | - Skip invoking on
INFO | jvm 1 | 2016/09/06 20:33:07 | - SecurityContextHolder now cleared, as request processing completed
Or these similar SAML exceptions in the bb-services log:
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
The problem occurs when the ADFS server and the Blackboard Learn application server have a time drift close to or beyond the default of 60 seconds.
There are two options to resolve the issue:
Manually syncing the clocks of the Blackboard Learn application servers and the ADFS server. For Blackboard Learn, the current time and time zone of the server can be viewed in a web browser by adding /webapps/portal/healthCheck to the end of a Blackboard Learn URL.
Example: 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
An institution may use the above URL to compare the Blackboard Learn system time zone and clock with that of their ADFS server and then adjust those items as necessary on the ADFS server so that they are in-sync with the Blackboard Learn site.
After entering the login credentials on the ADFS login page, an error may be displayed after being redirected to the Blackboard Learn GUI: The specified resource was not found, or you do not have permission to access it or Sign On Error!
With the following exceptions in the bb-services log:
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]
After entering the login credentials on the ADFS login page, an error may be displayed after being redirected to the Blackboard Learn GUI: The specified resource was not found, or you do not have permission to access it or Sign On Error!
With the following exceptions in the bb-services log:
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
As stated in the above SAML exception, the NameID element is missing from the Subject in the Response message. The problem typically occurs when the NameID is not setup as an Outgoing Claim Type in a Claims Rule for the Relying Party Trust on the institution's ADFS IdP or the Claims Rule for the NameID is not in the proper order for the Relying Party Trust on the institution's ADFS IdP, which in turn causes the missing NameID element in the Subject in the Response message.
<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>
<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>
You can use the Firefox SAML tracer Add-on to view the Subject in the Response message.
There are three methods to resolving this issue.
When logged into Blackboard Learn via SAML authentication, the user attempts to log out by clicking on the Sign Out button on the left side of the page and then clicks the End SSO Session button, a Sign On Error! is immediately displayed.
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].
With the following exception in the bb-services log:
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
The error occurs because of the Single Logout Service Type setting on the SAML Settings page.
The setting needs to be configured in Blackboard Learn and on the ADFS server.
For ADFS as the IdP, select the Post setting only and remove the Redirect endpoint for the Learn instance's Relying Party Trust on the ADFS server.
After making the above changes in Learn and the ADFS server, the End SSO Session logout button will work to properly sign out the user.
After entering the login credentials on the ADFS login page, a Sign On Error! message is displayed when redirected to Learn.
With the following SAML exception in the bb-services log:
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
Beginning in Blackboard Learn 3200.0.0, there is now an option to regenerate the SAML encryption certificate by navigating to System Admin > Building Blocks > Authentication Provider - SAML > Settings > Regenerate Certificate . The Sign On Error! problem may occur if the Regenerate certificate button is selected after the SP metadata is already uploaded to the Relying Party Trust for the Learn site on the ADFS server. To resolve the issue:
If you generate a new certificate under the B2 settings, you need to toggle the SAML B2 to Inactive and then back to Active to force the change. After, you can return to the provider settings and generate the new metadata to import into the IDP. If you don't toggle the settings, the old certificate may still be included when you generate new metadata. The IDP won't be updated and the next time Learn restarts it will present the new certificate. SAML authentication will break because of this mismatch.
With Active Directory Federation Services (ADFS), since the metadata for an ADFS federation typically located in https://[ADFS Server Hostname]/FederationMetadata/2007-06/FederationMetadata.xml includes an element that is incompatible with SAML 2.0, the metadata needs to be edited to delete the incompatible element before it is uploaded to the Identity Provider Settings section on the SAML Authentication Settings page in the Blackboard Learn GUI. If the metadata with the incompatible element is uploaded, an error will occur when selecting the SAML login link on the Blackboard Learn login page: Metadata for entity [entity] and role {} wasn't found. For reference, the Error ID is [error ID].
And the corresponding Java stack trace for the Error ID in the bb-services log has the following:
2016-06-21 11:42:51 -0700 - Metadata for entity https://<Learn Server Hostname>/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn't found<P><span class="captionText">For reference, the Error ID is c99511ae-1162-4941-b823-3dda19fea157.</span> - org.opensaml.saml2.metadata.provider.MetadataProviderException: Metadata for entity https
://ulvsso.laverne.
edu/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn't found
at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntity(SAMLContextProviderImpl.java:319)
at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalContext(SAMLContextProviderImpl.java:216)
at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalAndPeerEntity(SAMLContextProviderImpl.java:126)
at org.springframework.security.saml.SAMLEntryPoint.commence(SAMLEntryPoint.java:146)
at org.springframework.security.saml.SAMLEntryPoint.doFilter(SAMLEntryPoint.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
[SNIP]
Since the default metadata location for an ADFS federation is https://[ADFS server hostname]/FederationMetadata/2007-06/FederationMetadata.xml :
Download this file and open it in a text editor. Carefully delete the section starting
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> ... </X509Data></KeyInfo>
and ending
</ds:Signature>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http
://www.w3.
org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http
://www.w3.
org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_43879f32-9a91-4862-bc87-e98b85b51158">
<ds:Transforms>
<ds:Transform Algorithm="http
://www.w3.
org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http
://www.w3.
org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http
://www.w3.
org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>z1H1[SNIP]jaYM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue> FVj[SNIP]edrfNKWvsvk5A==
</ds:SignatureValue>
<KeyInfo xmlns="http
://www.w3.
org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>
FDdd[SNIP]qTNKdk5F/vf1AocDaX
</X509Certificate>
</X509Data>
</KeyInfo>
</ds:Signature>
If an institution is testing SAML authentication on a Blackboard Learn site and has multiple SAML authentication providers that share the same underlying ADFS IdP metadata XML file on the Blackboard Learn site, even if the other SAML authentication providers are set to Inactive, they will also need to have the updated metadata XML file uploaded in the Blackboard Learn GUI on the SAML Authentication Settings page in the Identity Provider Settings section. The SAML B2 should then be toggled Inactive/Available, while having the SAML authentication provider in 'Active' status, to ensure the updated metadata XML file is recognized system-wide.
After entering the login credentials on the ADFS login page, the user is redirected to the Blackboard Learn GUI, but not logged into Blackboard Learn.
The ONLY SAML authentication related event in the bb-services log is:
2016-10-18 13:03:28 -0600 - userName is null or empty
ADFS tries to add an extra End SSO Session logout button on the End all sessions? page that is displayed after first selecting the logout button at the top right in the Blackboard Learn GUI.
This is done by adding an extra SingleLogoutService to the IdP Metadata file:
<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/"/>
Since that is an optional SAML B2 IdP configuration and the signature being provided in the Redirect Endpoint is not correct, an error will occur when selecting the extra End SSO Session button on the End all sessions? page: Incoming SAML message failed security validation. Validation of request simple signature failed for context issuer. For reference, the error Id is [error ID].
The corresponding Java stack trace for the Error ID in the bb-services log has:
2016-10-17 16:57:44 -0400 - Incoming SAML message failed security validation Validation of request simple signature failed for context issuer<P><span class="captionText">For reference, the Error ID is 930c7767-8710-475e-8415-2077152280e0.</span> - org.opensaml.ws.security.SecurityPolicyException: Validation of request simple signature failed for context issuer
at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.doEvaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:139)
at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.evaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:103)
at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:131)
at org.springframework.security.saml.SAMLLogoutProcessingFilter.doFilter(SAMLLogoutProcessingFilter.java:104)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
[SNIP]
After removing the Redirect endpoint, the End SSO Session button will work properly signing out the user.
When troubleshooting an ADFS SAML authentication issue, it may be necessary to also have an institution review the ADFS application logs in the Event Viewer on their ADFS server for further insight. This is particularly necessary when the SAML response from the ADFS server has a Request Denied status as seen below:
<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>
The SAML response can be viewed by using the Firefox browser SAML tracer Add-on.
The Request Denied status in a response typically indicates a problem occurred when the IdP (ADFS) attempted to understand the response and process the result the SP (Blackboard Learn) provided.
To view the ADFS application logs with the Event Viewer :
Azure AD is Microsoft's (MS) cloud based directory and identity management service.
If an institution is using Azure AD as their IdP and wishes to only have the first part of the Azure AD email username used for the Blackboard Learn username, they can configure their Azure AD IdP to use the special ExtractMailPrefix() function to remove the domain suffix from either the email or the user principal name resulting in only the first part of the username being passed through (e.g. "joesmith" instead of joesmith @example. com).
If the Blackboard Learn Remote User ID is urn:oid:1.3.6.1.4.1.5923.1.1.1.6 , the Attribute setting for the Azure IdP would look like this:
Attribute Name: urn:oid:1.3.6.1.4.1.5923.1.1.1.6
Attribute Value: ExtractMailPrefix()
Mail: user.userprincipalname
So with the example joesmith @example. com email username, it would be passed like this in the SAML assertion from the Azure IdP to Blackboard Learn:
<Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6">
<AttributeValue>joesmith</AttributeValue>
Additional info about using the ExtractMailPrefix() function is available on the MS Azure documentation page .
After entering the login credentials on the MS Azure AD login page, a Sign On Error! may be displayed after being redirected to the Blackboard Learn GUI.
With the following exception in the bb-services log:
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
This is caused by the MS Azure AD IdP updating the certificate, but the metadata XML used by the Blackboard Learn SP not being adjusted to reflect the new certificate.
Microsoft has indicated that that they will be updating certificates every 6 weeks from now on, and that such updates will be unannounced.
If a user first logs into their user portal and then selects the app for their Blackboard Learn site, a new browser tab opens to display a message: The specified resource was not found, or you do not have permission to access it .
With the corresponding SAML related events in the 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: org.apache.catalina.session.StandardSessionFacade@58c53845. A new one will be created.
INFO | jvm 1 | 2016/08/16 10:49:22 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO | jvm 1 | 2016/08/16 10:49:22 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO | jvm 1 | 2016/08/16 10:49:22 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO | jvm 1 | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO | jvm 1 | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO | jvm 1 | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO | jvm 1 | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO | jvm 1 | 2016/08/16 10:49:22 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO | jvm 1 | 2016/08/16 10:49:22 | - Forwarding to /
INFO | jvm 1 | 2016/08/16 10:49:22 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO | jvm 1 | 2016/08/16 10:49:22 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO | jvm 1 | 2016/08/16 10:49:22 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO | jvm 1 | 2016/08/16 10:49:22 | - Successfully completed request
INFO | jvm 1 | 2016/08/16 10:49:22 | - Skip invoking on
INFO | jvm 1 | 2016/08/16 10:49:22 | - SecurityContextHolder now cleared, as request processing completed
The Service Provider Settings section of the SAML Authentication Settings page has changed and the Enable automatic SSO option should be checked to allow a user to access Blackboard Learn from their portal. If it is enabled, the ACS URL will also be changed to include an alias.
After entering the login credentials on the SAML authentication provider login page, a Sign On Error! may be displayed after being redirected to the Blackboard Learn GUI.
With the following DOMException and WRONG_DOCUMENT_ERR in the bb-services log:
2016-11-18 12:27:31 -0600 - WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.<P><span class="captionText">For reference, the Error ID is 86ebb81d-d3a3-4da5-95ab-1c94505f4281.</span> - org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
at org.apache.xerces.dom.NodeImpl.appendChild(Unknown Source)
at org.opensaml.xml.encryption.Decrypter.parseInputStream(Decrypter.java:832)
at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:610)
at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:795)
at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:535)
at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:453)
at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:414)
at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:199)
at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:82)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at sun.reflect.GeneratedMethodAccessor1209.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277)
at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309)
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55)
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191)
at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186)
at blackboard.platform.servlet.DevNonceFilter.doFilter(DevNonceFilter.java:68)
[SNIP]
The reason the problem occurs is another B2/Project changed the system property javax.xml.parsers.DocumentBuilderFactory value from org.apache.xerces.jaxp.DocumentBuilderFactoryImpl to com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.
Until a fix is released, the temporary resolution options are:
When configuring SAML authentication, an institution may notice there is not an option to add a SAML authentication provider in the Provider Order section in Blackboard Learn GUI when navigating to System Admin > Building Blocks: Authentication > Provider Order .
The reason there is not an option to add a SAML authentication provider to the Provider Order is that redirect type providers such as CAS and SAML hand off authentication to the remote authentication source. Those are not listed in the Provider Order as they are considered the authoritative source for authentication and handle their own authentication failures.
Beginning with the Q4 2016 release of Blackboard Learn, there is now an option to test the connection for a SAML provider in the Authentication section in the Blackboard Learn GUI. The connection test will check the following items:
To test the connection for a SAML authentication provider:
The Test Connection feature can be used in lieu of manually enabling SAML debug logging in Blackboard Learn for multiple reasons.
The Identity Provider Entity ID value that is displayed on the Test Connection output page is pulled from the Issuer element in the SAML POST from the IdP to Blackboard Learn after the user has been authenticated:
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http: //bbpdcsi-adfs1.bbpdcsi. local/a...services/trust</Issuer>
The SAML Attribute values displayed on the Test Connection output page in the SAML Response section are pulled from the Subject and AttributeStatement elements in the SAML POST from the IdP to Blackboard Learn after the user has been authenticated:
<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>
Use the steps below to create an Identity Provider (IdP) using Centrify's free SSO authentication solution.
That IdP can then be configured as the SAML authentication provider in a Blackboard Learn Service Provider (SP):
For the Advanced section, add the following line to the bottom of the script used to generate a SAML assertion for the application:
The complete script will be:
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"));
Which will allow the Centrify IdP to release an AttributeStatement with the User ID in the SAML POST.
Example:
<AttributeStatement>
<Attribute Name="NameID"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
>
<AttributeValue>luke.skywalker
@blackboard.
com.47</AttributeValue>
</Attribute>
</AttributeStatement>
More on specifying assertion elements in the Centrify SAML script
The Centrify IdP user that was created can now login to Blackboard Learn via SAML by selecting that authentication provider on the login page, and logout of Blackboard Learn using the extra End SSO Session logout button on the End all sessions? page that is displayed after selecting the logout button at the top right of Blackboard Learn.
An institution may inquire if it is possible to change the text on the End SSO Session logout page. It is possible to change the text on the End SSO Session logout page by editing the Language Pack:
Update the Message Keys:
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