Granted Authorities gets reset when using ie11 (not when using chrome)

306 views
Skip to first unread message

David Jonsson

unread,
Feb 23, 2015, 1:55:12 AM2/23/15
to waffle...@googlegroups.com
I have my setup like this in spring-security:

<sec:http entry-point-ref="negotiateSecurityFilterEntryPoint">
<sec:intercept-url pattern="/assets/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />

<sec:custom-filter ref="preAuthFilter" before="BASIC_AUTH_FILTER" />
<sec:custom-filter ref="waffleNegotiateSecurityFilter" position="BASIC_AUTH_FILTER" />
<sec:custom-filter ref="postAuthFilter" after="BASIC_AUTH_FILTER" />
</sec:http>


I use the providerCollection as follows (from the Github docs)d

<bean id="waffleSecurityFilterProviderCollection" class="waffle.servlet.spi.SecurityFilterProviderCollection">
<constructor-arg>
<list>
<ref bean="negotiateSecurityFilterProvider" />
<ref bean="basicSecurityFilterProvider" />
</list>
</constructor-arg>
</bean>

To check each time a user logs in I use a preAuthFilter and a postAuthFilter

PreAuth:

WindowsAuthenticationToken authentication = (WindowsAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();

if(authentication != null) {
servletRequest.setAttribute("Authorized", true);
} else {
servletRequest.setAttribute("Authorized", false);
}

PostAuth:

if((Boolean) servletRequest.getAttribute("Authorized") == false) {
publisher.publish();
}

The publisher triggers an AuthenticationSuccessEvent in which we update login-count, last-login date etc as well as adding a role (ROLE_MANAGER) if the user should have this access (this is determined by a 3rd party web-api)

if(isManager(authentication)) {
authentication.getAuthorities().add(new RoleManagerAuthority());
}

Everything works great when using chrome, however, when we user IE11 something strange happens. The first request adds ROLE_MANAGER correctly if the user has it and then the role stays for 4-5 (random) requests (we build our application on top of a RESTFul API which can have multiple requests each page-render) and then all of a sudden the roles gets reset and ROLE_MANAGER is removed (the preauthfilter doesnt detect that the user is unauthenticated). The user then has the standard roles (ROLE_USER + ROLE_<groups>[])

Ive tried to look at the web-traffic to see if a renegotiation occurs but have failed to find such requests. And shouldnt the preAuthFilter detect this if that would be the case?

Thankful for any insight into this issue, as well if you have any other design for customizing the waffle authentication pipeline (like we who are dependent on a 3rd party web-interface for adding a role)

Daniel Doubrovkine

unread,
Feb 23, 2015, 7:09:08 AM2/23/15
to waffle...@googlegroups.com
First, maybe something obvious - your three filters are run after BASIC_AUTH_FILTER, maybe they should be chained after each-other? Maybe in some case they are run out-of-order?

  <sec:custom-filter ref="preAuthFilter" before="BASIC_AUTH_FILTER" />
  <sec:custom-filter ref="waffleNegotiateSecurityFilter" position="preAuthFilter" />
  <sec:custom-filter ref="postAuthFilter" after="waffleNegotiateSecurityFilter" />

Since there's no re-negotiation this must be something related to the session. You're probably making an (incorrect) assumption that doing getAuthorities().add(...) carries across requests, and I bet that it's not the case with IE11 because something we don't understand (for example the request is made on a new thread and the server no longer thinks it's part of the same request). Since you do have all the ROLE_ elements it means the waffle filter is correctly invoked. 

Check whether postAuthFilter is invoked on the first request where you lose the role, at all? And what does the call to isManager() return?

--
You received this message because you are subscribed to the Google Groups "waffle" group.
To unsubscribe from this group and stop receiving emails from it, send an email to waffle-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

David Jonsson

unread,
Feb 23, 2015, 7:55:21 AM2/23/15
to waffle...@googlegroups.com

Im starting to think it has something to do with asynchronous requests being made and that IE11 sends these requests in some other fashion compared to chrome. When I enter the index-page, multiple requests can occur that needs to be authenticated (calls to the backend-api etc). static assets are set to not need authentication right now (js/css etc)

I thought the filters where setup as in PreAuthFilter before BASIC_AUTH_FILTER and PostAuthFilter after BASIC_AUTH_FILTER but I created a filter-chain instead and they do indeed run in the correct order now (I think they did it before too) still same issue though

<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/assets/**" filters="none" />
<sec:filter-chain pattern="/**" filters="
preAuthFilter,
waffleNegotiateSecurityFilter,
postAuthFilter"/>
</sec:filter-chain-map>
</bean>

<sec:http entry-point-ref="negotiateSecurityFilterEntryPoint">
<sec:intercept-url pattern="/assets/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />

  <sec:custom-filter ref="filterChainProxy" position="BASIC_AUTH_FILTER" />
</sec:http>


Ive tried with just trying to reach a single endpoint in our API (the ROLE_MANAGER works there))
When accessing index (which uses multiple authenticated calls) then I think that I get asynchronous requests and there is no way to determine which comes through the waffle-filter first meaning, the waffle-filter can outwrite itself (? if that makes sense). I dont know how to fix this since it seems to be an inherit way how ie11 works. Do you see any other pattern here to use for using external logic together with waffle to build the whole authentication (like adding additional roles)

isManager() as of now only looks at your authentication name against and returns true/false depending if your in the manager list (will be integrated towards 3rd party interface)

Daniel Doubrovkine

unread,
Feb 23, 2015, 8:25:10 AM2/23/15
to waffle...@googlegroups.com
Even if IE makes many concurrent requests, each will go through Waffle and each would get the same treatment, ie. it will negotiate auth if it's not available. 

What you really need to isolate the failed request and find out 1) is Waffle filter hit on the failed request 2) is your filter hit on the failed request 3) how are things different between a failed and a successful request inside those filters. 

David Jonsson

unread,
Feb 24, 2015, 5:36:06 AM2/24/15
to waffle...@googlegroups.com
First of all. Thank you for helping me out here!

It seems random to me, I can see some requests not hitting my PostAuthFilter but gets registered in the PreAuthFilter and those are random. They are just calls to our backend-api, and some go through until randomly (different requests) in which the roles are reset to the default ones. Also, I can go to a page in which a single request is made and the authentication works and the authentication-session is created and accessible through SecurityContextHolder. However when I load another page, the roles gets reset (I thought that the authentication object only needed to be created one time and that the user would be tied to this authentication for the remainder of the session lifetime). Looking at possible solutions right now:
  • Use the GrantedAuthorityFactory instead of a PostAuthFilter to check if the user should have this role
  • Remove the ROLE-based authorization for ROLE_MANAGER and instead always use the 3rd party interface
  • Break my keyboard and take a coffee.

Daniel Doubrovkine

unread,
Feb 24, 2015, 7:00:42 AM2/24/15
to waffle...@googlegroups.com
I don't believe in random, every single time these have turned out to be something very reasonable. I am not sure what to do next though ;)

David Jonsson

unread,
Feb 24, 2015, 7:08:59 AM2/24/15
to waffle...@googlegroups.com
Yeah, I know, I will check all attributes/headers of these requests. However /api/user/list can first work without removing the role, next time it can be the /api/user/list that does indeed remove the role. I will investigate further and post here if I find out what the issue is. As I said, thank you for your help. Really awesome framework btw! It has worked really smoothly in our network for authentication up until this little issue :)

David Jonsson

unread,
Feb 25, 2015, 6:55:30 AM2/25/15
to waffle...@googlegroups.com
 I think I have found why waffle re-authenticates the user, for some requests in IE11 it sends an Authorization header (Authorization: Negotiate TlRMTVNTUAABAA.......) the token looks different in each request. For some it does not send this header. Chrome does never send this header. I saw in the negotiationSecurityFilter this line

if (!authorizationHeader.isNull()
&& this.provider.isSecurityPackageSupported(authorizationHeader.getSecurityPackage())) {

Since chrome never sends this header I guess the request jump over this filter and goes into the BasicAuthFilterProvider. I guess my setup is missing something here or that I need to generate some response-header to ie11. Investigation continues . . .

David Jonsson

unread,
Feb 25, 2015, 6:57:45 AM2/25/15
to waffle...@googlegroups.com
EDIT: Chrome does send this header at the first request
authorization: Negotiate TlRMTVNTUAABAAAAl4II4gAAAAAA...

Daniel Doubrovkine

unread,
Feb 25, 2015, 7:21:19 AM2/25/15
to waffle...@googlegroups.com
Any re-auth is supposed to be triggered by the server with a 401 with WWW-Authenticate: Negotiate, and then the client should send an Authorization: ... header as a response to that. Some clients are trying to be more efficient (I bet IE11 does) and send that stuff upstream anyway if they know the site may require auth. I think Waffle just does nothing in this case, as it probably should, because it already knows that this session is authenticated. However, the filter is hit every time and it should be either setting the principal correctly to whatever is saved or issuing a 401 to the client and forcing it to re-auth.

I would find out whether you're doing Kerberos or NTLM here. How long are those TIRM.... blobs? If they are very long, it's Kerberos, otherwise NTLM. Kerberos is a protocol that doesn't require re-auth on new connections, and NTLM is a connection-oriented protocol and does. It's possible that if you just disabled Kerberos and forced everything to be NTLM the problem would go away (and I would leave it at that), or at least it would be useful to know.



On Wed, Feb 25, 2015 at 6:57 AM, David Jonsson <davi...@gmail.com> wrote:
EDIT: Chrome does send this header at the first request
authorization: Negotiate TlRMTVNTUAABAAAAl4II4gAAAAAA...

--
You received this message because you are subscribed to the Google Groups "waffle" group.
To unsubscribe from this group and stop receiving emails from it, send an email to waffle-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Daniel Doubrovkine

unread,
Feb 25, 2015, 7:22:56 AM2/25/15
to waffle...@googlegroups.com
Also, base64-decode that Authorization header, what does it start with? Maybe it's something we never heard of :) It should be something like NTLMSSP, etc.

Daniel Doubrovkine

unread,
Feb 25, 2015, 7:24:22 AM2/25/15
to waffle...@googlegroups.com
Ok, it's NTLMSSP, we obviously support that. And it means that Waffle should be challenging the client on any new connection with a 401 WWW-Authenticate header. Check that this is the case?

David Jonsson

unread,
Feb 25, 2015, 8:46:52 AM2/25/15
to waffle...@googlegroups.com
I omitted the token. But it does indeed change length, going from NTLM to Kerberos?

First Authorization token: Negotiate TlRMTVNTUAABAA..........AAAAAAAAAAAAAAAGAbEdAAAADw==

After some time IE sends another Authorization token: Negotiate TlRMTVNTUAADAAAAGAAYAIIAAAACAQIBmgAAABAAEABYAAAACgAKAGgAAAAQABAAcgAAABAAEACcAQAAFYKI4gYBsR0AAAAPjnwMtPOnlYj1mC2yNG9+KVAAQQBCAEwATwAtAFAAQwBwAGEAYgBsAG8AUABBAEIATABPAC0AUABDAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...........................................................................................AwAAAAAAAAAAEAAAAAEAAA6nMifuGre7vVBVHMDukE+p48QT/tHhv9Z2qwQSyf6TwKABAAAAAAAAAAAAAAAAAAAAAAAAkAGgBIAFQAVABQAC8AcABhAGIAbABvAC0AUABDAAAAAAAAAAAAAAAAAFIblzcqEp5CSzRL1CF8LWw=

When I Base64-decode the token I can see that it starts with NTLMSSP

I dont get a 401 WWW-Authenticate header. The first time I access a secure end-point I get prompted with a "Windows Security" form dialog. The request is in a "(Pending...)" state when looking at the network traffic (in Developer Tools). How do I disable Kerberos if I should try that?

Daniel Doubrovkine

unread,
Feb 25, 2015, 9:11:04 AM2/25/15
to waffle...@googlegroups.com
I am confused, you said everything was working on all browsers but occasionally not in IE11. What do you mean by "The first time I access a secure end-point I get prompted with a "Windows Security" form dialog.". If SSO works, you never see a dialog.


--
You received this message because you are subscribed to the Google Groups "waffle" group.
To unsubscribe from this group and stop receiving emails from it, send an email to waffle-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

David Jonsson

unread,
Feb 25, 2015, 9:49:24 AM2/25/15
to waffle...@googlegroups.com
hmm, I dont think Ive mentioned SSO but yes, SSO works in our corporate network, however we are developing on a separate network and we setup so that basicSecurityFilter is a fallback

<bean id="waffleSecurityFilterProviderCollection" class="waffle.servlet.spi.SecurityFilterProviderCollection">
<constructor-arg>
<list>
<ref bean="negotiateSecurityFilterProvider" />
<ref bean="basicSecurityFilterProvider" />
</list>
</constructor-arg>
</bean>


We have exactly the same issue on our corporate network with SSO though, ie11 not working but chrome does. 

David Jonsson

unread,
Feb 25, 2015, 10:59:57 AM2/25/15
to waffle...@googlegroups.com
Doing a quickfix with this one until we figure out how to solve it. We can close this thread for now and I can post when we have more time to investigate :)
Is there a reason why the authentication gets overwritten

waffle.spring.NegotiationSecurityFilter:158

protected boolean setAuthentication(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
return true;
}

Shouldnt it suffice to set the authentication if the Authorization header is set and that no authentication exists?


if (!authorizationHeader.isNull()
 
&& this.provider.isSecurityPackageSupported(authorizationHeader.getSecurityPackage())
 
&& SecurityContextHolder.getContext().getAuthentication() == null) {



if(SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
return true;
}
return false;

Or I may be missing something in how Kerberos needs to work. If Spring security already has a succesful authentication, it will hold this until the session invalidates (times out for instance)

Daniel Doubrovkine

unread,
Feb 25, 2015, 1:58:13 PM2/25/15
to waffle...@googlegroups.com
It might just well be valid for Spring. I think you should open an issue in Waffle describing this, maybe even opening a pull request with a code change (and a test)?

--
You received this message because you are subscribed to the Google Groups "waffle" group.
To unsubscribe from this group and stop receiving emails from it, send an email to waffle-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages