Skip to content

Commit b8a137b

Browse files
klieberjgrandja
authored andcommitted
Preserve user authentication details on re-authentication
During a token refresh in the `DefaultTokenServices` the user authentication will be re-authenticated if an `AuthenticationManager` was provided. A `PreAuthenticatedAuthenticationToken` is created based on the user authentication and then passed to the `AuthenticationManager`. However, if there were any details on the user authentication those details are lost because they are not copied to the `PreAuthenticatedAuthenticationToken`. If the `AuthenticationManager` is not provided then this logic is skipped over and the details are correctly preserved. The fix is simply to set the details on the `PreAuthenticatedAuthenticationToken` before passing it to the `AuthenticationManager`. Finally, I added two new tests to `DefaultTokenServicesTests` to validate that the user authentication is built correctly on a refresh. There is one test for the scenario when there is no re-authentication which passes even before these changes and then the other tests the re-authentication scenario which requires this change to pass. Fixes spring-atticgh-823
1 parent 42a6bcd commit b8a137b

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/token/DefaultTokenServices.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,16 @@ public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenReque
155155
if (this.authenticationManager != null && !authentication.isClientOnly()) {
156156
// The client has already been authenticated, but the user authentication might be old now, so give it a
157157
// chance to re-authenticate.
158-
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
159-
user = authenticationManager.authenticate(user);
158+
Authentication userAuthentication = authentication.getUserAuthentication();
159+
PreAuthenticatedAuthenticationToken preAuthenticatedToken = new PreAuthenticatedAuthenticationToken(
160+
userAuthentication,
161+
"",
162+
authentication.getAuthorities()
163+
);
164+
if (userAuthentication.getDetails() != null) {
165+
preAuthenticatedToken.setDetails(userAuthentication.getDetails());
166+
}
167+
Authentication user = authenticationManager.authenticate(preAuthenticatedToken);
160168
Object details = authentication.getDetails();
161169
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
162170
authentication.setDetails(details);

spring-security-oauth2/src/test/java/org/springframework/security/oauth2/provider/token/DefaultTokenServicesTests.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
package org.springframework.security.oauth2.provider.token;
22

3+
import org.junit.Assert;
34
import org.junit.Before;
45
import org.junit.Test;
6+
import org.mockito.ArgumentCaptor;
57
import org.mockito.Mockito;
8+
import org.springframework.security.authentication.AuthenticationManager;
9+
import org.springframework.security.authentication.AuthenticationProvider;
10+
import org.springframework.security.authentication.ProviderManager;
11+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
12+
import org.springframework.security.core.Authentication;
13+
import org.springframework.security.core.authority.AuthorityUtils;
14+
import org.springframework.security.core.userdetails.User;
15+
import org.springframework.security.core.userdetails.UserDetails;
16+
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
17+
import org.springframework.security.core.userdetails.UserDetailsService;
618
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
19+
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
720
import org.springframework.security.oauth2.common.OAuth2AccessToken;
21+
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
822
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
23+
import org.springframework.security.oauth2.provider.OAuth2Authentication;
24+
import org.springframework.security.oauth2.provider.OAuth2Request;
25+
import org.springframework.security.oauth2.provider.TokenRequest;
26+
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
27+
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
28+
29+
import java.util.Arrays;
930

1031
public class DefaultTokenServicesTests {
1132

@@ -29,5 +50,94 @@ public void testAccidentalNullAuthentication() {
2950
.thenReturn(null);
3051
services.loadAuthentication("FOO");
3152
}
53+
54+
@Test
55+
public void testRefreshAccessTokenWithReauthentication() {
56+
UserDetails user = createMockUser("joeuser", "PROCESSOR");
57+
UserDetailsService userDetailsService = Mockito.mock(UserDetailsService.class);
58+
59+
Mockito
60+
.when(tokenStore.readRefreshToken(Mockito.anyString()))
61+
.thenReturn(new DefaultOAuth2RefreshToken("FOO"));
62+
63+
Mockito
64+
.when(tokenStore.readAuthenticationForRefreshToken(Mockito.any(OAuth2RefreshToken.class)))
65+
.thenReturn(createMockOAuth2Authentication("myclient", user, "some more details"));
66+
67+
Mockito
68+
.when(userDetailsService.loadUserByUsername(Mockito.anyString()))
69+
.thenReturn(user);
70+
71+
services.setSupportRefreshToken(true);
72+
services.setAuthenticationManager(createAuthenticationManager(userDetailsService));
73+
74+
OAuth2AccessToken refreshedAccessToken = services.refreshAccessToken("FOO", createMockTokenRequest("myclient"));
75+
76+
ArgumentCaptor<OAuth2Authentication> refreshedAuthenticationCaptor = ArgumentCaptor.forClass(OAuth2Authentication.class);
77+
78+
Mockito.verify(tokenStore).storeAccessToken(Mockito.eq(refreshedAccessToken), refreshedAuthenticationCaptor.capture());
79+
80+
OAuth2Authentication refreshedAuthentication = refreshedAuthenticationCaptor.getValue();
81+
Authentication authentication = refreshedAuthentication.getUserAuthentication();
82+
Assert.assertEquals(user, authentication.getPrincipal());
83+
Assert.assertEquals("some more details", authentication.getDetails());
84+
}
85+
86+
@Test
87+
public void testRefreshAccessTokenWithoutReauthentication() {
88+
89+
UserDetails user = createMockUser("joeuser", "PROCESSOR");
90+
91+
Mockito
92+
.when(tokenStore.readRefreshToken(Mockito.anyString()))
93+
.thenReturn(new DefaultOAuth2RefreshToken("FOO"));
94+
95+
Mockito
96+
.when(tokenStore.readAuthenticationForRefreshToken(Mockito.any(OAuth2RefreshToken.class)))
97+
.thenReturn(createMockOAuth2Authentication("myclient", user, "some more details"));
98+
99+
services.setSupportRefreshToken(true);
100+
services.setAuthenticationManager(null);
101+
102+
OAuth2AccessToken refreshedAccessToken = services.refreshAccessToken("FOO", createMockTokenRequest("myclient"));
32103

104+
ArgumentCaptor<OAuth2Authentication> refreshedAuthenticationCaptor = ArgumentCaptor.forClass(OAuth2Authentication.class);
105+
106+
Mockito.verify(tokenStore).storeAccessToken(Mockito.eq(refreshedAccessToken), refreshedAuthenticationCaptor.capture());
107+
108+
OAuth2Authentication refreshedAuthentication = refreshedAuthenticationCaptor.getValue();
109+
Authentication authentication = refreshedAuthentication.getUserAuthentication();
110+
Assert.assertEquals(user, authentication.getPrincipal());
111+
Assert.assertEquals("some more details", authentication.getDetails());
112+
}
113+
114+
private AuthenticationManager createAuthenticationManager(UserDetailsService userDetailsService) {
115+
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
116+
provider.setPreAuthenticatedUserDetailsService(
117+
new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(userDetailsService)
118+
);
119+
return new ProviderManager(Arrays.<AuthenticationProvider> asList(provider));
120+
}
121+
122+
private TokenRequest createMockTokenRequest(String clientId) {
123+
return new TokenRequest(null, clientId, null, null);
124+
}
125+
126+
private OAuth2Request createMockOAuth2Request(String clientId) {
127+
return new OAuth2Request(null, clientId, null, true, null, null, null, null, null);
128+
}
129+
130+
private OAuth2Authentication createMockOAuth2Authentication(String clientId, UserDetails user, String extraDetails) {
131+
return new OAuth2Authentication(createMockOAuth2Request(clientId), createMockUserAuthentication(user, extraDetails));
132+
}
133+
134+
private UsernamePasswordAuthenticationToken createMockUserAuthentication(UserDetails user, Object extraDetails) {
135+
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, "", user.getAuthorities());
136+
token.setDetails(extraDetails);
137+
return token;
138+
}
139+
140+
private UserDetails createMockUser(String username, String ... roles) {
141+
return new User(username, "", AuthorityUtils.createAuthorityList(roles));
142+
}
33143
}

0 commit comments

Comments
 (0)