diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java index 34984b8fa8..a11ed7cff3 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java @@ -60,6 +60,7 @@ import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @@ -210,7 +211,8 @@ public class OidcTests { registeredClient); MvcResult mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user").roles("A", "B"))) + .with(user("user").roles("A", "B") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); @@ -270,7 +272,8 @@ public class OidcTests { registeredClient); MvcResult mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user").roles("A", "B"))) + .with(user("user").roles("A", "B") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); @@ -335,7 +338,8 @@ public class OidcTests { registeredClient); MvcResult mvcResult = this.mvc .perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI)).queryParams(authorizationRequestParameters) - .with(user("user"))) + .with(user("user") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); @@ -388,7 +392,8 @@ public class OidcTests { registeredClient1); MvcResult mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user1"))) + .with(user("user1") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); @@ -424,7 +429,8 @@ public class OidcTests { authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient2); mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user2"))) + .with(user("user2") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); @@ -497,7 +503,8 @@ public class OidcTests { registeredClient); MvcResult mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user"))) + .with(user("user") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); @@ -537,7 +544,8 @@ public class OidcTests { registeredClient); MvcResult mvcResult = this.mvc .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) - .with(user("user"))) + .with(user("user") + .authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)))) .andExpect(status().is3xxRedirection()) .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); diff --git a/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java b/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java index cc400919a8..444a3daad8 100644 --- a/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java +++ b/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java @@ -24,6 +24,9 @@ import java.util.Date; import java.util.UUID; import org.springframework.lang.Nullable; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; @@ -141,7 +144,7 @@ public final class JwtGenerator implements OAuth2TokenGenerator { SessionInformation sessionInformation = context.get(SessionInformation.class); if (sessionInformation != null) { claimsBuilder.claim("sid", sessionInformation.getSessionId()); - claimsBuilder.claim(IdTokenClaimNames.AUTH_TIME, sessionInformation.getLastRequest()); + claimsBuilder.claim(IdTokenClaimNames.AUTH_TIME, getAuthenticationTime(context.getPrincipal())); } } else if (AuthorizationGrantType.REFRESH_TOKEN.equals(context.getAuthorizationGrantType())) { @@ -222,4 +225,17 @@ public final class JwtGenerator implements OAuth2TokenGenerator { this.clock = clock; } + static Date getAuthenticationTime(Authentication authentication) { + Instant authenticationTime = null; + for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { + if (grantedAuthority instanceof FactorGrantedAuthority factorGrantedAuthority) { + if (authenticationTime == null || factorGrantedAuthority.getIssuedAt().isAfter(authenticationTime)) { + authenticationTime = factorGrantedAuthority.getIssuedAt(); + } + } + } + Assert.notNull(authenticationTime, "authenticationTime cannot be null"); + return Date.from(authenticationTime); + } + } diff --git a/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java b/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java index 9ec3a1c554..c536481d1e 100644 --- a/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java +++ b/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java @@ -24,6 +24,9 @@ import java.util.HashMap; import java.util.Map; import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.FactorGrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; @@ -85,6 +88,9 @@ public final class TestOAuth2Authorizations { .additionalParameters(authorizationRequestAdditionalParameters) .state("state") .build(); + Authentication principal = new TestingAuthenticationToken("principal", null, + new SimpleGrantedAuthority("ROLE_A"), new SimpleGrantedAuthority("ROLE_B"), + FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)); OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient) .id("id") .principalName("principal") @@ -93,8 +99,7 @@ public final class TestOAuth2Authorizations { .token(authorizationCode) .attribute(OAuth2ParameterNames.STATE, "consent-state") .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest) - .attribute(Principal.class.getName(), - new TestingAuthenticationToken("principal", null, "ROLE_A", "ROLE_B")); + .attribute(Principal.class.getName(), principal); if (accessToken != null) { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now(), Instant.now().plus(1, ChronoUnit.HOURS)); diff --git a/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java b/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java index 392a897629..9b1456cea7 100644 --- a/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java +++ b/oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java @@ -363,7 +363,7 @@ public class JwtGeneratorTests { SessionInformation sessionInformation = tokenContext.get(SessionInformation.class); assertThat(jwtClaimsSet.getClaim("sid")).isEqualTo(sessionInformation.getSessionId()); assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AUTH_TIME)) - .isEqualTo(sessionInformation.getLastRequest()); + .isEqualTo(JwtGenerator.getAuthenticationTime(tokenContext.getPrincipal())); } else if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.REFRESH_TOKEN)) { OidcIdToken currentIdToken = tokenContext.getAuthorization().getToken(OidcIdToken.class).getToken();