Skip to content

Commit 5c2a977

Browse files
committed
Add MFA to Existing Authentication Mechanisms
1 parent 9027409 commit 5c2a977

File tree

8 files changed

+133
-0
lines changed

8 files changed

+133
-0
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
7373
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
7474

75+
private MfaConfigurer<H> mfa;
76+
7577
/**
7678
* Creates a new instance
7779
* @see HttpSecurity#formLogin(Customizer)
@@ -227,8 +229,20 @@ public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
227229
return this;
228230
}
229231

232+
public FormLoginConfigurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
233+
if (this.mfa == null) {
234+
this.mfa = new MfaConfigurer<>("AUTHN_FORM", this);
235+
this.mfa.authenticationEntryPoint(this::getAuthenticationEntryPoint);
236+
}
237+
customizer.customize(this.mfa);
238+
return this;
239+
}
240+
230241
@Override
231242
public void init(H http) throws Exception {
243+
if (this.mfa != null) {
244+
this.mfa.init(http);
245+
}
232246
super.init(http);
233247
initDefaultLoginFilter(http);
234248
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
8989

9090
private static final String DEFAULT_REALM = "Realm";
9191

92+
private MfaConfigurer<B> mfa;
93+
9294
private AuthenticationEntryPoint authenticationEntryPoint;
9395

9496
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
@@ -161,8 +163,20 @@ public HttpBasicConfigurer<B> securityContextRepository(SecurityContextRepositor
161163
return this;
162164
}
163165

166+
public HttpBasicConfigurer<B> factor(Customizer<MfaConfigurer<B>> customizer) {
167+
if (this.mfa == null) {
168+
this.mfa = new MfaConfigurer<>("AUTHN_BASIC", this);
169+
this.mfa.authenticationEntryPoint(() -> this.authenticationEntryPoint);
170+
}
171+
customizer.customize(this.mfa);
172+
return this;
173+
}
174+
164175
@Override
165176
public void init(B http) {
177+
if (this.mfa != null) {
178+
this.mfa.init(http);
179+
}
166180
registerDefaults(http);
167181
}
168182

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import org.springframework.http.converter.HttpMessageConverter;
2727
import org.springframework.security.authentication.AuthenticationManager;
2828
import org.springframework.security.authentication.ProviderManager;
29+
import org.springframework.security.config.Customizer;
2930
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
3031
import org.springframework.security.core.userdetails.UserDetailsService;
3132
import org.springframework.security.web.access.intercept.AuthorizationFilter;
33+
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
3234
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
3335
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
3436
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@@ -59,6 +61,8 @@
5961
public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
6062
extends AbstractHttpConfigurer<WebAuthnConfigurer<H>, H> {
6163

64+
private MfaConfigurer<H> mfa;
65+
6266
private String rpId;
6367

6468
private String rpName;
@@ -151,6 +155,22 @@ public WebAuthnConfigurer<H> creationOptionsRepository(
151155
return this;
152156
}
153157

158+
public WebAuthnConfigurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
159+
if (this.mfa == null) {
160+
this.mfa = new MfaConfigurer<>("AUTHN_WEBAUTHN", this)
161+
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
162+
}
163+
customizer.customize(this.mfa);
164+
return this;
165+
}
166+
167+
@Override
168+
public void init(H http) throws Exception {
169+
if (this.mfa != null) {
170+
this.mfa.init(http);
171+
}
172+
}
173+
154174
@Override
155175
public void configure(H http) throws Exception {
156176
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
3838
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
3939
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
40+
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
4041

4142
/**
4243
* Adds X509 based pre authentication to an application. Since validating the certificate
@@ -80,6 +81,8 @@
8081
public final class X509Configurer<H extends HttpSecurityBuilder<H>>
8182
extends AbstractHttpConfigurer<X509Configurer<H>, H> {
8283

84+
private MfaConfigurer<H> mfa;
85+
8386
private X509AuthenticationFilter x509AuthenticationFilter;
8487

8588
private X509PrincipalExtractor x509PrincipalExtractor;
@@ -173,12 +176,27 @@ public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
173176
return this;
174177
}
175178

179+
public X509Configurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
180+
if (this.mfa == null) {
181+
this.mfa = new MfaConfigurer<>("AUTHN_X509", this);
182+
}
183+
customizer.customize(this.mfa);
184+
return this;
185+
}
186+
176187
@Override
177188
public void init(H http) {
178189
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
179190
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
180191
http.authenticationProvider(authenticationProvider)
181192
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
193+
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
194+
if (exceptions != null) {
195+
exceptions.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE);
196+
}
197+
if (this.mfa != null) {
198+
this.mfa.init(http);
199+
}
182200
}
183201

184202
@Override

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
4242
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
4343
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
44+
import org.springframework.security.config.annotation.web.configurers.MfaConfigurer;
4445
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
4546
import org.springframework.security.context.DelegatingApplicationListener;
4647
import org.springframework.security.core.Authentication;
@@ -172,6 +173,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
172173

173174
private final UserInfoEndpointConfig userInfoEndpointConfig = new UserInfoEndpointConfig();
174175

176+
private MfaConfigurer<B> mfa;
177+
175178
private String loginPage;
176179

177180
private String loginProcessingUrl = OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
@@ -305,8 +308,20 @@ public OAuth2LoginConfigurer<B> userInfoEndpoint(Customizer<UserInfoEndpointConf
305308
return this;
306309
}
307310

311+
public OAuth2LoginConfigurer<B> factor(Customizer<MfaConfigurer<B>> customizer) {
312+
if (this.mfa == null) {
313+
this.mfa = new MfaConfigurer<>("AUTHN_OAUTH2", this);
314+
this.mfa.authenticationEntryPoint(this::getAuthenticationEntryPoint);
315+
}
316+
customizer.customize(this.mfa);
317+
return this;
318+
}
319+
308320
@Override
309321
public void init(B http) throws Exception {
322+
if (this.mfa != null) {
323+
this.mfa.init(http);
324+
}
310325
OAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(
311326
this.getClientRegistrationRepository(), this.getAuthorizedClientRepository(), this.loginProcessingUrl);
312327
RequestMatcher processUri = getRequestMatcherBuilder().matcher(this.loginProcessingUrl);

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
3737
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
3838
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
39+
import org.springframework.security.config.annotation.web.configurers.MfaConfigurer;
3940
import org.springframework.security.config.http.SessionCreationPolicy;
4041
import org.springframework.security.core.Authentication;
4142
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -161,6 +162,8 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
161162
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
162163
"X-Requested-With", "XMLHttpRequest");
163164

165+
private MfaConfigurer<H> mfa;
166+
164167
private final ApplicationContext context;
165168

166169
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
@@ -249,8 +252,20 @@ public OAuth2ResourceServerConfigurer<H> opaqueToken(Customizer<OpaqueTokenConfi
249252
return this;
250253
}
251254

255+
public OAuth2ResourceServerConfigurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
256+
if (this.mfa == null) {
257+
this.mfa = new MfaConfigurer<>("AUTHN_BEARER", this);
258+
this.mfa.authenticationEntryPoint(() -> this.authenticationEntryPoint);
259+
}
260+
customizer.customize(this.mfa);
261+
return this;
262+
}
263+
252264
@Override
253265
public void init(H http) {
266+
if (this.mfa != null) {
267+
this.mfa.init(http);
268+
}
254269
validateConfiguration();
255270
registerDefaultAccessDeniedHandler(http);
256271
registerDefaultEntryPoint(http);

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@
3535
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3636
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
3737
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
38+
import org.springframework.security.config.annotation.web.configurers.MfaConfigurer;
3839
import org.springframework.security.core.Authentication;
3940
import org.springframework.security.core.userdetails.UserDetailsService;
41+
import org.springframework.security.web.AuthenticationEntryPoint;
4042
import org.springframework.security.web.authentication.AuthenticationConverter;
4143
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4244
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
45+
import org.springframework.security.web.authentication.PostAuthenticationEntryPoint;
4346
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
4447
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
4548
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
@@ -104,6 +107,8 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
104107

105108
private final ApplicationContext context;
106109

110+
private MfaConfigurer<H> mfa;
111+
107112
private OneTimeTokenService oneTimeTokenService;
108113

109114
private String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter.DEFAULT_SUBMIT_PAGE_URL;
@@ -127,6 +132,9 @@ public OneTimeTokenLoginConfigurer(ApplicationContext context) {
127132

128133
@Override
129134
public void init(H http) throws Exception {
135+
if (this.mfa != null) {
136+
this.mfa.init(http);
137+
}
130138
if (getLoginProcessingUrl() == null) {
131139
loginProcessingUrl(OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL);
132140
}
@@ -359,6 +367,20 @@ public OneTimeTokenLoginConfigurer<H> generateRequestResolver(GenerateOneTimeTok
359367
return this;
360368
}
361369

370+
public OneTimeTokenLoginConfigurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
371+
if (this.mfa == null) {
372+
this.mfa = new MfaConfigurer<>("AUTHN_OTT", this);
373+
this.mfa.authenticationEntryPoint(this::getPostAuthenticationEntryPoint);
374+
}
375+
customizer.customize(this.mfa);
376+
return this;
377+
}
378+
379+
private AuthenticationEntryPoint getPostAuthenticationEntryPoint() {
380+
String postUrl = this.tokenGeneratingUrl + "?username={u}";
381+
return new PostAuthenticationEntryPoint(postUrl, Map.of("u", Authentication::getName));
382+
}
383+
362384
private GenerateOneTimeTokenRequestResolver getGenerateRequestResolver() {
363385
if (this.requestResolver != null) {
364386
return this.requestResolver;

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
3434
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
3535
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
36+
import org.springframework.security.config.annotation.web.configurers.MfaConfigurer;
3637
import org.springframework.security.core.Authentication;
3738
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
3839
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
@@ -121,6 +122,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
121122

122123
private static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith("5");
123124

125+
private MfaConfigurer<B> mfa;
126+
124127
private String loginPage;
125128

126129
private String authenticationRequestUri = "/saml2/authenticate";
@@ -256,6 +259,15 @@ public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
256259
return this;
257260
}
258261

262+
public Saml2LoginConfigurer<B> factor(Customizer<MfaConfigurer<B>> customizer) {
263+
if (this.mfa == null) {
264+
this.mfa = new MfaConfigurer<>("AUTHN_SAML2", this);
265+
this.mfa.authenticationEntryPoint(this::getAuthenticationEntryPoint);
266+
}
267+
customizer.customize(this.mfa);
268+
return this;
269+
}
270+
259271
@Override
260272
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
261273
return getRequestMatcherBuilder().matcher(loginProcessingUrl);
@@ -276,6 +288,9 @@ protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingU
276288
*/
277289
@Override
278290
public void init(B http) throws Exception {
291+
if (this.mfa != null) {
292+
this.mfa.init(http);
293+
}
279294
registerDefaultCsrfOverride(http);
280295
relyingPartyRegistrationRepository(http);
281296
this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http));

0 commit comments

Comments
 (0)