Skip to content

Commit f5859e9

Browse files
committed
Add MultifactorAuthenticationToken
to indicate the state of principal which passed first step of multi step (factor) authentication.
1 parent c0f64aa commit f5859e9

File tree

17 files changed

+779
-27
lines changed

17 files changed

+779
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.authentication.configurers.mfa;
18+
19+
import org.springframework.security.authentication.*;
20+
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
21+
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
22+
23+
/**
24+
* Allows configuring a {@link MultiFactorAuthenticationProvider}
25+
*
26+
* @param <B> the type of the {@link ProviderManagerBuilder}
27+
*
28+
* @author Yoshikazu Nojima
29+
*/
30+
public class MultiFactorAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
31+
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
32+
33+
//~ Instance fields
34+
// ================================================================================================
35+
private AuthenticationProvider authenticationProvider;
36+
private MFATokenEvaluator mfaTokenEvaluator = new MFATokenEvaluatorImpl();
37+
38+
/**
39+
* Constructor
40+
* @param authenticationProvider {@link AuthenticationProvider} to be delegated
41+
*/
42+
public MultiFactorAuthenticationProviderConfigurer(AuthenticationProvider authenticationProvider) {
43+
this.authenticationProvider = authenticationProvider;
44+
}
45+
46+
47+
public static MultiFactorAuthenticationProviderConfigurer multiFactorAuthenticationProvider(AuthenticationProvider authenticationProvider){
48+
return new MultiFactorAuthenticationProviderConfigurer(authenticationProvider);
49+
}
50+
51+
@Override
52+
public void configure(B builder) {
53+
MultiFactorAuthenticationProvider multiFactorAuthenticationProvider = new MultiFactorAuthenticationProvider(authenticationProvider, mfaTokenEvaluator);
54+
multiFactorAuthenticationProvider = postProcess(multiFactorAuthenticationProvider);
55+
builder.authenticationProvider(multiFactorAuthenticationProvider);
56+
}
57+
58+
public MultiFactorAuthenticationProviderConfigurer<B> mfaTokenEvaluator(MFATokenEvaluator mfaTokenEvaluator) {
59+
this.mfaTokenEvaluator = mfaTokenEvaluator;
60+
return this;
61+
}
62+
}

config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.springframework.security.authentication.AuthenticationTrustResolver;
4141
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
4242
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
43+
import org.springframework.security.authentication.MFATokenEvaluator;
44+
import org.springframework.security.authentication.MFATokenEvaluatorImpl;
4345
import org.springframework.security.config.annotation.ObjectPostProcessor;
4446
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
4547
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
@@ -113,6 +115,7 @@ public <T> T postProcess(T object) {
113115
private boolean authenticationManagerInitialized;
114116
private AuthenticationManager authenticationManager;
115117
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
118+
private MFATokenEvaluator mfaTokenEvaluator = new MFATokenEvaluatorImpl();
116119
private HttpSecurity http;
117120
private boolean disableDefaults;
118121

@@ -392,6 +395,11 @@ public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
392395
this.trustResolver = trustResolver;
393396
}
394397

398+
@Autowired(required = false)
399+
public void setMfaTokenEvaluator(MFATokenEvaluator mfaTokenEvaluator){
400+
this.mfaTokenEvaluator = mfaTokenEvaluator;
401+
}
402+
395403
@Autowired(required = false)
396404
public void setContentNegotationStrategy(
397405
ContentNegotiationStrategy contentNegotiationStrategy) {
@@ -421,6 +429,7 @@ private Map<Class<? extends Object>, Object> createSharedObjects() {
421429
sharedObjects.put(ApplicationContext.class, context);
422430
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
423431
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
432+
sharedObjects.put(MFATokenEvaluator.class, mfaTokenEvaluator);
424433
return sharedObjects;
425434
}
426435

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configurers;
1717

18-
import java.util.LinkedHashMap;
19-
18+
import org.springframework.security.authentication.MFATokenEvaluator;
2019
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2120
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2221
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -30,6 +29,8 @@
3029
import org.springframework.security.web.savedrequest.RequestCache;
3130
import org.springframework.security.web.util.matcher.RequestMatcher;
3231

32+
import java.util.LinkedHashMap;
33+
3334
/**
3435
* Adds exception handling for Spring Security related exceptions to an application. All
3536
* properties have reasonable defaults, so no additional configuration is required other
@@ -67,6 +68,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
6768

6869
private AuthenticationEntryPoint authenticationEntryPoint;
6970

71+
private MFATokenEvaluator mfaTokenEvaluator;
72+
7073
private AccessDeniedHandler accessDeniedHandler;
7174

7275
private LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<>();
@@ -151,6 +154,18 @@ public ExceptionHandlingConfigurer<H> authenticationEntryPoint(
151154
return this;
152155
}
153156

157+
/**
158+
* Specifies the {@link MFATokenEvaluator} to be used
159+
*
160+
* @param mfaTokenEvaluator the {@link MFATokenEvaluator} to be used
161+
* @return the {@link ExceptionHandlingConfigurer} for further customization
162+
*/
163+
public ExceptionHandlingConfigurer<H> mfaTokenEvaluator(
164+
MFATokenEvaluator mfaTokenEvaluator) {
165+
this.mfaTokenEvaluator = mfaTokenEvaluator;
166+
return this;
167+
}
168+
154169
/**
155170
* Sets a default {@link AuthenticationEntryPoint} to be used which prefers being
156171
* invoked for the provided {@link RequestMatcher}. If only a single default
@@ -194,6 +209,9 @@ public void configure(H http) throws Exception {
194209
entryPoint, getRequestCache(http));
195210
AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
196211
exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
212+
if (mfaTokenEvaluator != null) {
213+
exceptionTranslationFilter.setMFATokenEvaluator(mfaTokenEvaluator);
214+
}
197215
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
198216
http.addFilter(exceptionTranslationFilter);
199217
}

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

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configurers;
1717

18-
import java.util.ArrayList;
19-
import java.util.Arrays;
20-
import java.util.List;
21-
import javax.servlet.http.HttpServletResponse;
22-
import javax.servlet.http.HttpSession;
23-
2418
import org.springframework.context.ApplicationContext;
2519
import org.springframework.context.ApplicationListener;
2620
import org.springframework.context.event.GenericApplicationListenerAdapter;
2721
import org.springframework.context.event.SmartApplicationListener;
2822
import org.springframework.security.authentication.AuthenticationTrustResolver;
23+
import org.springframework.security.authentication.MFATokenEvaluator;
2924
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
3025
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3126
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -34,26 +29,21 @@
3429
import org.springframework.security.core.session.SessionRegistryImpl;
3530
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
3631
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
37-
import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
38-
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
39-
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
40-
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
41-
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
42-
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
43-
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
32+
import org.springframework.security.web.authentication.session.*;
4433
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
4534
import org.springframework.security.web.context.NullSecurityContextRepository;
4635
import org.springframework.security.web.context.SecurityContextRepository;
4736
import org.springframework.security.web.savedrequest.NullRequestCache;
4837
import org.springframework.security.web.savedrequest.RequestCache;
49-
import org.springframework.security.web.session.ConcurrentSessionFilter;
50-
import org.springframework.security.web.session.InvalidSessionStrategy;
51-
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
52-
import org.springframework.security.web.session.SessionManagementFilter;
53-
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
54-
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
38+
import org.springframework.security.web.session.*;
5539
import org.springframework.util.Assert;
5640

41+
import javax.servlet.http.HttpServletResponse;
42+
import javax.servlet.http.HttpSession;
43+
import java.util.ArrayList;
44+
import java.util.Arrays;
45+
import java.util.List;
46+
5747
/**
5848
* Allows configuring session management.
5949
*
@@ -423,6 +413,11 @@ public void init(H http) throws Exception {
423413
if (trustResolver != null) {
424414
httpSecurityRepository.setTrustResolver(trustResolver);
425415
}
416+
MFATokenEvaluator mfaTokenEvaluator = http
417+
.getSharedObject(MFATokenEvaluator.class);
418+
if (mfaTokenEvaluator != null) {
419+
httpSecurityRepository.setMFATokenEvaluator(mfaTokenEvaluator);
420+
}
426421
http.setSharedObject(SecurityContextRepository.class,
427422
httpSecurityRepository);
428423
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.authentication.configurers.mfa;
18+
19+
import org.junit.Test;
20+
import org.mockito.ArgumentCaptor;
21+
import org.springframework.security.authentication.AuthenticationProvider;
22+
import org.springframework.security.authentication.MFATokenEvaluator;
23+
import org.springframework.security.authentication.MultiFactorAuthenticationProvider;
24+
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.Mockito.mock;
28+
import static org.mockito.Mockito.verify;
29+
import static org.springframework.security.config.annotation.authentication.configurers.mfa.MultiFactorAuthenticationProviderConfigurer.multiFactorAuthenticationProvider;
30+
31+
public class MultiFactorAuthenticationProviderConfigurerTests {
32+
33+
@Test
34+
public void test(){
35+
AuthenticationProvider delegatedAuthenticationProvider = mock(AuthenticationProvider.class);
36+
MFATokenEvaluator mfaTokenEvaluator = mock(MFATokenEvaluator.class);
37+
MultiFactorAuthenticationProviderConfigurer configurer
38+
= multiFactorAuthenticationProvider(delegatedAuthenticationProvider);
39+
configurer.mfaTokenEvaluator(mfaTokenEvaluator);
40+
ProviderManagerBuilder providerManagerBuilder = mock(ProviderManagerBuilder.class);
41+
configurer.configure(providerManagerBuilder);
42+
ArgumentCaptor<AuthenticationProvider> argumentCaptor = ArgumentCaptor.forClass(AuthenticationProvider.class);
43+
verify(providerManagerBuilder).authenticationProvider(argumentCaptor.capture());
44+
MultiFactorAuthenticationProvider authenticationProvider = (MultiFactorAuthenticationProvider) argumentCaptor.getValue();
45+
46+
assertThat(authenticationProvider.getAuthenticationProvider()).isEqualTo(delegatedAuthenticationProvider);
47+
assertThat(authenticationProvider.getMFATokenEvaluator()).isEqualTo(mfaTokenEvaluator);
48+
}
49+
}

core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public class AuthenticationTrustResolverImpl implements AuthenticationTrustResol
3636
private Class<? extends Authentication> anonymousClass = AnonymousAuthenticationToken.class;
3737
private Class<? extends Authentication> rememberMeClass = RememberMeAuthenticationToken.class;
3838

39+
private MFATokenEvaluator mfaTokenEvaluator = new MFATokenEvaluatorImpl();
40+
3941
// ~ Methods
4042
// ========================================================================================================
4143

@@ -52,6 +54,10 @@ public boolean isAnonymous(Authentication authentication) {
5254
return false;
5355
}
5456

57+
if (mfaTokenEvaluator != null && mfaTokenEvaluator.isMultiFactorAuthentication(authentication)) {
58+
return true;
59+
}
60+
5561
return anonymousClass.isAssignableFrom(authentication.getClass());
5662
}
5763

@@ -70,4 +76,8 @@ public void setAnonymousClass(Class<? extends Authentication> anonymousClass) {
7076
public void setRememberMeClass(Class<? extends Authentication> rememberMeClass) {
7177
this.rememberMeClass = rememberMeClass;
7278
}
79+
80+
public void setMFATokenEvaluator(MFATokenEvaluator mfaTokenEvaluator){
81+
this.mfaTokenEvaluator = mfaTokenEvaluator;
82+
}
7383
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authentication;
18+
19+
import org.springframework.security.core.Authentication;
20+
21+
/**
22+
* Evaluates <code>Authentication</code> tokens
23+
*
24+
* @author Yoshikazu Nojima
25+
*/
26+
public interface MFATokenEvaluator {
27+
28+
/**
29+
* Indicates whether the passed <code>Authentication</code> token represents a
30+
* user in the middle of multi factor authentication process.
31+
*
32+
* @param authentication to test (may be <code>null</code> in which case the method
33+
* will always return <code>false</code>)
34+
*
35+
* @return <code>true</code> the passed authentication token represented a principal
36+
* in the middle of multi factor authentication process, <code>false</code> otherwise
37+
*/
38+
boolean isMultiFactorAuthentication(Authentication authentication);
39+
40+
/**
41+
* Indicates whether the principal associated with the <code>Authentication</code>
42+
* token is allowed to login with only single factor.
43+
*
44+
* @param authentication to test (may be <code>null</code> in which case the method
45+
* will always return <code>false</code>)
46+
*
47+
* @return <code>true</code> the principal associated with thepassed authentication
48+
* token is allowed to login with only single factor, <code>false</code> otherwise
49+
*/
50+
boolean isSingleFactorAuthenticationAllowed(Authentication authentication);
51+
}

0 commit comments

Comments
 (0)