Skip to content

Commit e5c338f

Browse files
Further improvements
1 parent bf20642 commit e5c338f

File tree

7 files changed

+113
-12
lines changed

7 files changed

+113
-12
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2979,6 +2979,36 @@ public HttpSecurity oauth2ResourceServer(
29792979
return HttpSecurity.this;
29802980
}
29812981

2982+
/**
2983+
* Configures One-Time Token Login Support.
2984+
*
2985+
* <h2>Example Configuration</h2>
2986+
*
2987+
* <pre>
2988+
* &#064;Configuration
2989+
* &#064;EnableWebSecurity
2990+
* public class SecurityConfig {
2991+
*
2992+
* &#064;Bean
2993+
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
2994+
* http
2995+
* .authorizeHttpRequests((authorize) -&gt; authorize
2996+
* .anyRequest().authenticated()
2997+
* )
2998+
* .oneTimeTokenLogin(Customizer.withDefaults());
2999+
* return http.build();
3000+
* }
3001+
* }
3002+
* </pre>
3003+
*
3004+
* Note that a
3005+
* {@link org.springframework.security.authentication.ott.OneTimeTokenSender} is
3006+
* required to enable this support.
3007+
* @param oneTimeTokenLoginConfigurerCustomizer the {@link Customizer} to provide more
3008+
* options for the {@link OneTimeTokenLoginConfigurer}
3009+
* @return the {@link HttpSecurity} for further customizations
3010+
* @throws Exception
3011+
*/
29823012
public HttpSecurity oneTimeTokenLogin(
29833013
Customizer<OneTimeTokenLoginConfigurer<HttpSecurity>> oneTimeTokenLoginConfigurerCustomizer)
29843014
throws Exception {

config/src/main/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurer.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
4747
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationConverter;
4848
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationRequestFilter;
49+
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationRequestResolver;
4950
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationRequestSuccessHandler;
5051
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenAuthenticationRequestSuccessHandler;
52+
import org.springframework.security.web.authentication.ott.RequestParameterOneTimeTokenAuthenticationRequestResolver;
5153
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
5254
import org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;
5355
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
@@ -88,6 +90,8 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
8890

8991
private AuthenticationProvider authenticationProvider;
9092

93+
private OneTimeTokenAuthenticationRequestResolver authenticationRequestResolver = new RequestParameterOneTimeTokenAuthenticationRequestResolver();
94+
9195
public OneTimeTokenLoginConfigurer(ApplicationContext context) {
9296
this.context = context;
9397
}
@@ -143,6 +147,7 @@ private SecurityContextRepository getSecurityContextRepository(H http) {
143147
private void configureOttAuthenticationRequestFilter(H http) {
144148
OneTimeTokenAuthenticationRequestFilter authenticationRequestFilter = new OneTimeTokenAuthenticationRequestFilter(
145149
getOneTimeTokenService(http), getOneTimeTokenSender(http));
150+
authenticationRequestFilter.setAuthenticationRequestResolver(this.authenticationRequestResolver);
146151
authenticationRequestFilter.setAuthenticationRequestSuccessHandler(this.authenticationRequestSuccessHandler);
147152
authenticationRequestFilter.setRequestMatcher(antMatcher(HttpMethod.POST, this.authenticationRequestUrl));
148153
http.addFilter(postProcess(authenticationRequestFilter));
@@ -169,6 +174,19 @@ private AuthenticationProvider getAuthenticationProvider(H http) {
169174
return this.authenticationProvider;
170175
}
171176

177+
/**
178+
* Specifies the {@link OneTimeTokenAuthenticationRequestResolver} to use to resolve a
179+
* {@link org.springframework.security.authentication.ott.OneTimeTokenAuthenticationRequest}.
180+
* Defaults to {@link RequestParameterOneTimeTokenAuthenticationRequestResolver}
181+
* @param authenticationRequestResolver
182+
*/
183+
public OneTimeTokenLoginConfigurer<H> authenticationRequestResolver(
184+
OneTimeTokenAuthenticationRequestResolver authenticationRequestResolver) {
185+
Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
186+
this.authenticationRequestResolver = authenticationRequestResolver;
187+
return this;
188+
}
189+
172190
/**
173191
* Specifies the {@link AuthenticationProvider} to use when authenticating the user.
174192
* @param authenticationProvider

core/src/main/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public OneTimeToken generate(OneTimeTokenAuthenticationRequest request) {
5353

5454
@Override
5555
public OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
56-
OneTimeToken ott = this.oneTimeTokenByToken.remove(authenticationToken.getToken());
56+
OneTimeToken ott = this.oneTimeTokenByToken.remove(authenticationToken.getTokenValue());
5757
if (ott == null || isExpired(ott)) {
5858
return null;
5959
}

core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
5353
throw new InvalidOneTimeTokenException("Invalid token");
5454
}
5555
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
56-
OneTimeTokenAuthenticationToken authenticated = new OneTimeTokenAuthenticationToken(user,
56+
OneTimeTokenAuthenticationToken authenticated = OneTimeTokenAuthenticationToken.authenticated(user,
5757
user.getAuthorities());
5858
authenticated.setDetails(otpAuthenticationToken.getDetails());
5959
return authenticated;

core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
3232

3333
private final Object principal;
3434

35-
private String token;
35+
private String tokenValue;
3636

37-
public OneTimeTokenAuthenticationToken(String token) {
37+
public OneTimeTokenAuthenticationToken(Object principal, String tokenValue) {
3838
super(Collections.emptyList());
39-
this.token = token;
40-
this.principal = null;
39+
this.tokenValue = tokenValue;
40+
this.principal = principal;
41+
}
42+
43+
public OneTimeTokenAuthenticationToken(String tokenValue) {
44+
this(null, tokenValue);
4145
}
4246

4347
public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
@@ -46,13 +50,38 @@ public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends Gr
4650
setAuthenticated(true);
4751
}
4852

49-
public String getToken() {
50-
return this.token;
53+
/**
54+
* Creates an unauthenticated token
55+
* @param principal the principal
56+
* @param tokenValue the one-time token value
57+
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
58+
*/
59+
public static OneTimeTokenAuthenticationToken unauthenticated(Object principal, String tokenValue) {
60+
return new OneTimeTokenAuthenticationToken(principal, tokenValue);
61+
}
62+
63+
/**
64+
* Creates an unauthenticated token
65+
* @param principal the principal
66+
* @param authorities the principal authorities
67+
* @return an authenticated {@link OneTimeTokenAuthenticationToken}
68+
*/
69+
public static OneTimeTokenAuthenticationToken authenticated(Object principal,
70+
Collection<? extends GrantedAuthority> authorities) {
71+
return new OneTimeTokenAuthenticationToken(principal, authorities);
72+
}
73+
74+
/**
75+
* Returns the one-time token value
76+
* @return
77+
*/
78+
public String getTokenValue() {
79+
return this.tokenValue;
5180
}
5281

5382
@Override
5483
public Object getCredentials() {
55-
return this.token;
84+
return this.tokenValue;
5685
}
5786

5887
@Override

web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
/**
2929
* An implementation of {@link AuthenticationConverter} that detects if the request
3030
* contains a {@code token} parameter and constructs a
31-
* {@link OneTimeTokenAuthenticationToken} with it
31+
* {@link OneTimeTokenAuthenticationToken} with it. If there is a {@code username}
32+
* parameter in the request it will be used as the authentication token principal.
3233
*
3334
* @author Marcus da Coregio
3435
* @since 6.4
@@ -45,7 +46,8 @@ public Authentication convert(HttpServletRequest request) {
4546
this.logger.debug("No token found in request");
4647
return null;
4748
}
48-
return new OneTimeTokenAuthenticationToken(token);
49+
String username = request.getParameter("username");
50+
return OneTimeTokenAuthenticationToken.unauthenticated(username, token);
4951
}
5052

5153
}

web/src/test/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverterTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,29 @@ void convertWhenTokenParameterThenReturnOneTimeTokenAuthenticationToken() {
4040
OneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter
4141
.convert(request);
4242
assertThat(authentication).isNotNull();
43-
assertThat(authentication.getToken()).isEqualTo("1234");
43+
assertThat(authentication.getTokenValue()).isEqualTo("1234");
44+
assertThat(authentication.getPrincipal()).isNull();
45+
}
46+
47+
@Test
48+
void convertWhenTokenAndUsernameParameterThenReturnOneTimeTokenAuthenticationTokenWithUsername() {
49+
MockHttpServletRequest request = new MockHttpServletRequest();
50+
request.setParameter("token", "1234");
51+
request.setParameter("username", "josh");
52+
OneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter
53+
.convert(request);
54+
assertThat(authentication).isNotNull();
55+
assertThat(authentication.getTokenValue()).isEqualTo("1234");
56+
assertThat(authentication.getPrincipal()).isEqualTo("josh");
57+
}
58+
59+
@Test
60+
void convertWhenOnlyUsernameParameterThenReturnNull() {
61+
MockHttpServletRequest request = new MockHttpServletRequest();
62+
request.setParameter("username", "josh");
63+
OneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter
64+
.convert(request);
65+
assertThat(authentication).isNull();
4466
}
4567

4668
@Test

0 commit comments

Comments
 (0)