ADFS NullReference exception on SAML logout (event 303) - events

I use Spring-Security-Saml sample app as a SP and ADFS 2.0 as an IdP. I followed instructions described here http://docs.spring.io/autorepo/docs/spring-security-saml/1.0.x-SNAPSHOT/reference/htmlsingle/ . After setting it up I can login into the system, but on global logout ADFS throws NullReferenceException (Event Id 303):
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.IdentityServer.Service.SamlProtocol.SingleLogoutService.LogoutNextSessionParticipant()
at Microsoft.IdentityServer.Service.SamlProtocol.SingleLogoutService.ProcessLogoutRequest(LogoutRequest logoutRequest, Boolean& validLogoutRequest)
at Microsoft.IdentityServer.Service.SamlProtocol.SingleLogoutService.ProcessMessage(SamlMessage samlMessage, BindingInformation bindingInformation, Boolean validResponseMessage, Boolean& validLogoutRequest)

I ran into this same problem, except in my case, I was using ADFS 3.0. Telling Spring Security SAML to sign the logout response fixed it for me. This is configured by setting requireLogoutResponseSigned on the ExtendedMetadata bean to true.
The null reference error message isn't very helpful. When I enabled tracing in the C:\Windows\ADFS\Microsoft.DeviceRegistration.ServiceHost.exe.config file, I got the following error message in the tracing log in Event Viewer.
Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSignatureVerificationException: MSIS7074: SAML authentication request for the WebSSO profile must specify an issuer with no NameQualifier, SPNameQualifier or SPProvidedId properties.
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolManager.ValidateSignatureRequirements(SamlMessage samlMessage)
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolManager.Logout(HttpSamlMessage logoutMessage, String sessionState, String logoutState, Boolean partialLogout, Boolean isUrlTranslationNeeded, HttpSamlMessage& newLogoutMessage, String& newSessionState, String& newLogoutState)
Searching for that gives some more helpful results.
SLO. Error MSIS7074 on ADFS
https://social.msdn.microsoft.com/Forums/vstudio/en-US/85b1a66f-ca0b-4ed6-a32f-c8dca4fdd6c7/slo-error-msis7074-on-adfs?forum=Geneva

Related

Client Registration with Spring-boot Oauth2 - tokenUri vs issuerUri

Sorry folks, this may be a newb question. I'm a little lost.
My Spring-boot environment provides me with keycloak for client authorization, it gives me these.
spring.security.oauth2.resourceserver.jwt.issuer-uri
spring.security.oauth2.client.provider.keycloak.issuer-uri
spring.security.oauth2.client.registration.keycloak.* # client-id, secret, provider, grant-type
I noticed on the ClientRegistration that .issuerUri(String uri) is not avaialbe until Spring-Security v5.4.x. I am using 5.3.5, although I could bump up. I am confused what the difference is. As I would expect, I get an error when I do .tokenUri(issuerUri). I believe they are different modes/API, but I am at a loss as to what I should set in the 5.3.5 API.
Caused by: org.springframework.security.oauth2.client.ClientAuthorizationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 405 Method Not Allowed: [{"error":"RESTEASY003650: No resource method found for POST, return 405 with Allow header"}]
So as a newb, I don't get why I have 4 choices of URI and what they do. Google and javadoc haven't been much help, so I figure I just don't know the right place to look to learn it. The only way I know how to fix this is to manual make my own HTTP call to the URI and get my Authentication token, but that would defeat the purpose of the Oauth2 library.
tokenUri represents the URI for the token endpoint. For example:
https://authz.example.org/auth/realms/myrealms/protocol/openid-connect/token
Whereas issuerUri is the URI that identifies the Authorization Server:
https://authz.example.org/auth
It's quite common for the issuer URI to be the root for more specific URIs like the token URI.
Regarding your specific error, I'd imagine that Keycloak is stating that you cannot POST to https://authz.example.org/auth, which is true. You should be POSTing to the token endpoint.
The issuer-uri Spring Boot property should cause Spring Security to look up the other endpoints and add them to a default ClientRegistration. Because of that, I'm not sure why you are also trying to programmatically configure ClientRegistration. That said, if you do need to programmatically create a ClientRegistration, you can use the issuer URI like so, and Spring Security will do the rest:
#Bean
ClientRegistrationRepository registrations() {
ClientRegistration registration = ClientRegistrations
.forIssuerLocation("https://authz.example.org/auth")
.build();
return new InMemoryClientRegistrationRepository(registration);
}

Spring security exception handler

I have spring oauth2 authorization server with authorization_code and refresh_token grant types client. Sometimes it happens that used refresh_token is not valid, which causes long and ugly exception in logs:
org.springframework.security.oauth2.common.exceptions.InvalidGrantException: Invalid refresh token: xxxxxxxx-yyyy-xxxx-yyyy-xxxxxxxxxxxx
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:142) ~[spring-security-oauth2-2.2.1.RELEASE.jar!/:na]
at org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter.getAccessToken(RefreshTokenGranter.java:47) ~[spring-security-oauth2-2.2.1.RELEASE.jar!/:na]
at org.springframework.security.oauth2.provider.token.AbstractTokenGranter.grant(AbstractTokenGranter.java:65) ~[spring-security-oauth2-2.2.1.RELEASE.jar!/:na]
at org.springframework.security.oauth2.provider.CompositeTokenGranter.grant(CompositeTokenGranter.java:38) ~[spring-security-oauth2-2.2.1.RELEASE.jar!/:na]
[...]
Is it there anything like #RestControllerAdvice which would handle such exceptions?
I already tried using mentioned #RestControllerAdvice, but unfortunately it didn't work.
I am not very familiarized with Spring OAUTH2 Authorization, however my answer might be helpful for you.
#RestControllerAdvice is designed to assist #RestController therefore it works if the request is handled by the DispatcherServlet. However, security-related exceptions occurs before that as it is thrown by Filters. Hence, it is required to insert a custom filter AccessDeniedHandler implementation and AuthenticationEntryPoint implementation) earlier in the chain to catch the exception and return accordingly. These filters can be inserted easily in your web security configurations.
Here you can learn how to detect an Authentication Failure in the Client.
You could also check this tutorial .

Retrieve Entire SAML Response in Spring Security SAML Extension

I have a Spring Boot application that is setup as a Service Provider. My end goal is to be able to call the AWS STS Assume Role with SAML service to generate AWS temporary credentials on behalf of the user with the SAML response used to initially authenticate users of my application.
I found this other question. With that answer I am able to get only the assertion, not the entire response. From my testing, the AWS API call linked above wants the entire response, not just the assertion piece.
I used this Chrome Extension to view the SAML response. When I include everything (outline below)
<samlp:Response>
...
<saml:Assertion>
...
</saml:Assertion>
</samlp:Response>
The AWS STS Assume Role with SAML works. The other related question's answer only provides me the
<saml:Assertion>...</saml:Assertion>
block and the AWS STS Assume Role with SAML fails.
So my question is how do I get the entire SAML Response XML object back in a controller of my Spring Boot application?
I don't know any direct way in spring-security-saml, but maybe you could try to implement your own SAMLProcessingFilter ie simply extending the existing one and overriding the method attemptAuthentication().
Principle:
In this method, you have access to the response returned from the IdP and post back to the SP (at least in a Redirect-POST profile)
You probably have a way to extract what you need from the httpRequest
Then you can store (session, ThreadLocal variable, ...)
And finally you delegate the authentication process to the parent (by calling super.attemptAuthentication())
`
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if ("POST".equalsIgnoreCase(request.getMethod())) {
String samlResponse = request.getParameter("SAMLResponse");
System.out.println("Original SAML Response (base64 decoded) : " + new
String(Base64.getDecoder().decode(samlResponse), StandardCharsets.UTF_8));
}
return super.attemptAuthentication(request, response);
}
`

Spring SecurityContext is null on page not found error

Springs returns null from SecurityContextHolder.getContext().getAuthentication(); on handling eror 404 (when not mapped url entered). But it works fine on exceptions(500) or security errors(403, Spring SecurityContext returning null authentication on error pages) thanks to ExceptionTranslationFilter.
Why is this happening and what i can change to obtains security context?
Solved by storing user principal in session attributes.

Spring Security Sequence of execution

I am not able to find out where and when exactly the authentication manager is executed by spring security. I mean there are certian filters which are executed sequentially as below:
FIRST
- CHANNEL_FILTER
- CONCURRENT_SESSION_FILTER
- SECURITY_CONTEXT_FILTER
- LOGOUT_FILTER
- X509_FILTER
- PRE_AUTH_FILTER
- CAS_FILTER
- FORM_LOGIN_FILTER
- OPENID_FILTER
- BASIC_AUTH_FILTER
- SERVLET_API_SUPPORT_FILTER
- REMEMBER_ME_FILTER
- ANONYMOUS_FILTER
- EXCEPTION_TRANSLATION_FILTER
- SESSION_MANAGEMENT_FILTER
- FILTER_SECURITY_INTERCEPTOR
- SWITCH_USER_FILTER
- LAST
But when exactly authentication provider authenticates the provided username and password, i mean to ask after which these below filters is the authentication provider is executed .
Regards
Jayendra
From Spring Security documentation:
The order that filters are defined in the chain is very important.
Irrespective of which filters you are actually using, the order should
be as follows:
ChannelProcessingFilter, because it might need to redirect to a different protocol
SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and
any changes to the SecurityContext can be copied to the HttpSession
when the web request ends (ready for use with the next web request)
ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality but needs to update the SessionRegistry to reflect
ongoing requests from the principal
Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter,
BasicAuthenticationFilter etc - so that the SecurityContextHolder can
be modified to contain a valid Authentication request token
The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your
servlet container
RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
and the request presents a cookie that enables remember-me services to
take place, a suitable remembered Authentication object will be put
there
AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
an anonymous Authentication object will be put there
ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an
appropriate AuthenticationEntryPoint can be launched
FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied
So the authentication manager is called at step 4. If you look at the source code of UsernamePasswordAuthenticationFilter you will see something like:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// ...
return this.getAuthenticationManager().authenticate(authRequest);
}

Resources