Skip to content

Commit d87eb24

Browse files
committed
test(mfa): add WEBAUTHN multi-factor integration test
Add MfaMultiFactorIntegrationTest that exercises the PASSWORD+WEBAUTHN path through setupMfa, resolveFactorAuthorities, and the MFA status endpoint. Verifies both factors appear in required/missing lists and that the authorization manager factory is created for multi-factor configurations.
1 parent e4e3f36 commit d87eb24

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.digitalsanctuary.spring.user.api;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
5+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
6+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
7+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
8+
import java.util.List;
9+
import org.junit.jupiter.api.DisplayName;
10+
import org.junit.jupiter.api.Test;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
13+
import org.springframework.test.context.TestPropertySource;
14+
import org.springframework.test.web.servlet.MockMvc;
15+
import com.digitalsanctuary.spring.user.security.MfaConfigProperties;
16+
import com.digitalsanctuary.spring.user.security.MfaConfiguration;
17+
import com.digitalsanctuary.spring.user.test.annotations.IntegrationTest;
18+
19+
@IntegrationTest
20+
@TestPropertySource(properties = {
21+
"user.mfa.enabled=true",
22+
"user.mfa.factors=PASSWORD,WEBAUTHN",
23+
"user.webauthn.enabled=true",
24+
"user.webauthn.rpId=localhost",
25+
"user.webauthn.rpName=Test App"
26+
})
27+
@DisplayName("MFA Multi-Factor (PASSWORD + WEBAUTHN) Integration Tests")
28+
class MfaMultiFactorIntegrationTest {
29+
30+
@Autowired
31+
private MockMvc mockMvc;
32+
33+
@Autowired
34+
private MfaConfigProperties mfaConfigProperties;
35+
36+
@Autowired
37+
private DefaultAuthorizationManagerFactory<?> mfaAuthorizationManagerFactory;
38+
39+
@Test
40+
@DisplayName("should configure both PASSWORD and WEBAUTHN factors when both are specified")
41+
void shouldConfigureBothFactorsWhenBothAreSpecified() {
42+
assertThat(mfaConfigProperties.isEnabled()).isTrue();
43+
assertThat(mfaConfigProperties.getFactors()).containsExactly("PASSWORD", "WEBAUTHN");
44+
}
45+
46+
@Test
47+
@DisplayName("should create MFA authorization manager factory when MFA is enabled")
48+
void shouldCreateMfaAuthorizationManagerFactoryWhenMfaIsEnabled() {
49+
assertThat(mfaAuthorizationManagerFactory).isNotNull();
50+
}
51+
52+
@Test
53+
@DisplayName("should resolve both factor authorities when both factors are configured")
54+
void shouldResolveBothFactorAuthoritiesWhenBothFactorsAreConfigured() {
55+
List<String> factors = mfaConfigProperties.getFactors();
56+
for (String factor : factors) {
57+
String authority = MfaConfiguration.mapFactorToAuthority(factor);
58+
assertThat(authority).as("Authority for factor %s should not be null", factor).isNotNull();
59+
}
60+
}
61+
62+
@Test
63+
@DisplayName("should return both factors as required in MFA status endpoint")
64+
void shouldReturnBothFactorsAsRequiredInMfaStatusEndpoint() throws Exception {
65+
mockMvc.perform(get("/user/mfa/status").with(user("user@test.com").roles("USER")))
66+
.andExpect(status().isOk())
67+
.andExpect(jsonPath("$.success").value(true))
68+
.andExpect(jsonPath("$.data.mfaEnabled").value(true))
69+
.andExpect(jsonPath("$.data.requiredFactors").isArray())
70+
.andExpect(jsonPath("$.data.requiredFactors.length()").value(2))
71+
.andExpect(jsonPath("$.data.requiredFactors[0]").value("PASSWORD"))
72+
.andExpect(jsonPath("$.data.requiredFactors[1]").value("WEBAUTHN"));
73+
}
74+
75+
@Test
76+
@DisplayName("should report both factors as missing when user has no factor authorities")
77+
void shouldReportBothFactorsAsMissingWhenUserHasNoFactorAuthorities() throws Exception {
78+
mockMvc.perform(get("/user/mfa/status").with(user("user@test.com").roles("USER")))
79+
.andExpect(status().isOk())
80+
.andExpect(jsonPath("$.data.missingFactors.length()").value(2))
81+
.andExpect(jsonPath("$.data.missingFactors[0]").value("PASSWORD"))
82+
.andExpect(jsonPath("$.data.missingFactors[1]").value("WEBAUTHN"))
83+
.andExpect(jsonPath("$.data.fullyAuthenticated").value(false));
84+
}
85+
86+
@Test
87+
@DisplayName("should report both factors as missing for unauthenticated request")
88+
void shouldReportBothFactorsAsMissingForUnauthenticatedRequest() throws Exception {
89+
mockMvc.perform(get("/user/mfa/status"))
90+
.andExpect(status().isOk())
91+
.andExpect(jsonPath("$.data.missingFactors.length()").value(2))
92+
.andExpect(jsonPath("$.data.fullyAuthenticated").value(false));
93+
}
94+
}

0 commit comments

Comments
 (0)