Skip to content

Commit

Permalink
fix : important bugs on App-Token areas
Browse files Browse the repository at this point in the history
  • Loading branch information
patternknife committed Sep 12, 2024
1 parent 7a2b568 commit ed38308
Show file tree
Hide file tree
Showing 14 changed files with 84 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<dependency>
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
</dependency>
```
For v2, using the database tables from Spring Security 5 (only the database tables; follow the dependencies as above):
Expand Down
4 changes: 2 additions & 2 deletions client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.patternknife.securityhelper.oauth2.client</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation-client</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<packaging>jar</packaging>

<properties>
Expand Down Expand Up @@ -48,7 +48,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
<dependency>
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
</dependency>

<!-- DB -->
Expand Down
22 changes: 12 additions & 10 deletions mysql/schema.sql

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">

<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<name>spring-security-oauth2-password-jpa-implementation</name>
<description>The implementation of Spring Security 6 Spring Authorization Server for stateful OAuth2 Password Grant</description>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -90,6 +91,16 @@ public interface KnifeAuthorizationRepository extends JpaRepository<KnifeAuthori


Optional<KnifeAuthorization> findByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken(String principalName, String registeredClientId, String accessTokenAppToken);

@Query("SELECT o FROM KnifeAuthorization o WHERE o.principalName = :principalName AND o.registeredClientId = :registeredClientId AND o.accessTokenAppToken = :accessTokenAppToken AND o.accessTokenExpiresAt > CURRENT_TIMESTAMP")
Optional<KnifeAuthorization> findValidAuthorizationByPrincipalNameAndClientIdAndAppToken(
@Param("principalName") String principalName,
@Param("registeredClientId") String registeredClientId,
@Param("accessTokenAppToken") String accessTokenAppToken
);



Optional<List<KnifeAuthorization>> findListByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken(String principalName, String registeredClientId, String accessTokenAppToken);
@Modifying
@Transactional(rollbackFor=Exception.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ public class KnifeAuthorization {
private String refreshTokenValue;

@Column(name = "refresh_token_issued_at")
private Instant refreshTokenIssuedAt;
private LocalDateTime refreshTokenIssuedAt;

@Column(name = "refresh_token_expires_at")
private Instant refreshTokenExpiresAt;
private LocalDateTime refreshTokenExpiresAt;

@Lob
@Column(name = "refresh_token_metadata")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorMessages;
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.CommonOAuth2AuthorizationCycle;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.CommonOAuth2AuthorizationSaver;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.DefaultOauth2AuthenticationHashCheckService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
import io.github.patternknife.securityhelper.oauth2.api.config.security.token.CustomGrantAuthenticationToken;
Expand All @@ -31,7 +31,7 @@
@AllArgsConstructor
public final class KnifeOauth2AuthenticationProvider implements AuthenticationProvider {

private final CommonOAuth2AuthorizationCycle commonOAuth2AuthorizationCycle;
private final CommonOAuth2AuthorizationSaver commonOAuth2AuthorizationCycle;
private final ConditionalDetailsService conditionalDetailsService;
private final DefaultOauth2AuthenticationHashCheckService oauth2AuthenticationHashCheckService;
private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService;
Expand Down Expand Up @@ -67,7 +67,7 @@ public Authentication authenticate(Authentication authentication)
}


OAuth2Authorization oAuth2Authorization = commonOAuth2AuthorizationCycle.run(userDetails, ((CustomGrantAuthenticationToken) authentication).getGrantType(), clientId, ((CustomGrantAuthenticationToken) authentication).getAdditionalParameters(), null);
OAuth2Authorization oAuth2Authorization = commonOAuth2AuthorizationCycle.save(userDetails, ((CustomGrantAuthenticationToken) authentication).getGrantType(), clientId, ((CustomGrantAuthenticationToken) authentication).getAdditionalParameters(), null);

RegisteredClient registeredClient = oAuth2ClientAuthenticationToken.getRegisteredClient();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
/*
* Create = Build + Persist
* */
public interface CommonOAuth2AuthorizationCycle {
public interface CommonOAuth2AuthorizationSaver {

OAuth2Authorization run(UserDetails userDetails, AuthorizationGrantType authorizationGrantType,
String clientId, Map<String, Object> additionalParameters,
@Nullable Map<String, Object> modifiableAdditionalParameters);
OAuth2Authorization save(UserDetails userDetails, AuthorizationGrantType authorizationGrantType,
String clientId, Map<String, Object> additionalParameters,
@Nullable Map<String, Object> modifiableAdditionalParameters);

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@

@Service
@RequiredArgsConstructor
public class CommonOAuth2AuthorizationCycleImpl implements CommonOAuth2AuthorizationCycle {
public class CommonOAuth2AuthorizationSaverImpl implements CommonOAuth2AuthorizationSaver {

private static final Logger logger = LoggerFactory.getLogger(KnifeSecurityLogConfig.class);

private final OAuth2AuthorizationBuildingService oAuth2AuthorizationBuildingService;
private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService;

@Override
public OAuth2Authorization run(UserDetails userDetails, AuthorizationGrantType authorizationGrantType, String clientId,
Map<String, Object> additionalParameters, Map<String, Object> modifiableAdditionalParameters) {
public OAuth2Authorization save(UserDetails userDetails, AuthorizationGrantType authorizationGrantType, String clientId,
Map<String, Object> additionalParameters, Map<String, Object> modifiableAdditionalParameters) {

OAuth2Authorization oAuth2Authorization = oAuth2AuthorizationService.findByUserNameAndClientIdAndAppToken(userDetails.getUsername(), clientId, (String) additionalParameters.get(KnifeHttpHeaders.APP_TOKEN));
if(authorizationGrantType.getValue().equals(AuthorizationGrantType.PASSWORD.getValue())){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

import java.util.Map;

/*
*
* The term "build" means a "newly created OAuth2Authorization" (no update)
*
* */
@Component
@RequiredArgsConstructor
public class OAuth2AuthorizationBuildingServiceImpl implements OAuth2AuthorizationBuildingService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,61 +42,55 @@ public class OAuth2AuthorizationServiceImpl implements OAuth2AuthorizationServic
/*
1) Remove previous Access & Refresh Tokens for current OAuth2Authorization from Persistence
2) Save Access & Refresh Tokens for current OAuth2Authorization into Persistence
3) Only Insert (shouldBeNewAuthorization)
*/
@Override
public void save(OAuth2Authorization authorization) {

String appTokenValue = authorization.getAttribute(KnifeHttpHeaders.APP_TOKEN);

if (authorization.getRefreshToken() != null) {
if (findByToken(authorization.getRefreshToken().getToken().getTokenValue(), OAuth2TokenType.REFRESH_TOKEN) != null) {
this.remove(authorization);
}
}
if (authorization.getAccessToken() != null) {
if (findByToken(authorization.getAccessToken().getToken().getTokenValue(), OAuth2TokenType.ACCESS_TOKEN) != null) {
this.remove(authorization);
}
}
public void save(OAuth2Authorization shouldBeNewAuthorization) {


KnifeAuthorization knifeAuthorization = new KnifeAuthorization();


knifeAuthorization.setId(authorization.getId());
knifeAuthorization.setId(shouldBeNewAuthorization.getId());

knifeAuthorization.setPrincipalName(authorization.getPrincipalName());
knifeAuthorization.setRegisteredClientId(authorization.getAttribute("client_id"));
knifeAuthorization.setAccessTokenValue(authorization.getAccessToken().getToken().getTokenValue());
knifeAuthorization.setRefreshTokenValue(authorization.getRefreshToken().getToken().getTokenValue());
knifeAuthorization.setPrincipalName(shouldBeNewAuthorization.getPrincipalName());
knifeAuthorization.setRegisteredClientId(shouldBeNewAuthorization.getAttribute("client_id"));
knifeAuthorization.setAccessTokenValue(shouldBeNewAuthorization.getAccessToken().getToken().getTokenValue());
knifeAuthorization.setRefreshTokenValue(shouldBeNewAuthorization.getRefreshToken().getToken().getTokenValue());

String appTokenValue = shouldBeNewAuthorization.getAttribute(KnifeHttpHeaders.APP_TOKEN);
if (appTokenValue != null) {
knifeAuthorization.setAccessTokenAppToken(appTokenValue);
}

String userAgentValue = authorization.getAttribute(KnifeHttpHeaders.USER_AGENT);
String userAgentValue = shouldBeNewAuthorization.getAttribute(KnifeHttpHeaders.USER_AGENT);
if (!StringUtils.isEmpty(userAgentValue)) {
knifeAuthorization.setAccessTokenUserAgent(userAgentValue);
}

String remoteIp = authorization.getAttribute(KnifeHttpHeaders.X_Forwarded_For);
String remoteIp = shouldBeNewAuthorization.getAttribute(KnifeHttpHeaders.X_Forwarded_For);
if (remoteIp != null) {
knifeAuthorization.setAccessTokenRemoteIp(remoteIp);
}

knifeAuthorization.setAttributes(authorization);
knifeAuthorization.setAccessTokenType(authorization.getAuthorizationGrantType().getValue());
knifeAuthorization.setAccessTokenScopes(String.join(",", authorization.getAuthorizedScopes()));

knifeAuthorization.setAttributes(shouldBeNewAuthorization);
knifeAuthorization.setAccessTokenType(shouldBeNewAuthorization.getAuthorizationGrantType().getValue());
knifeAuthorization.setAccessTokenScopes(String.join(",", shouldBeNewAuthorization.getAuthorizedScopes()));

// Token Expiration
knifeAuthorization.setAccessTokenIssuedAt(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));
if (shouldBeNewAuthorization.getAccessToken().getToken().getExpiresAt() != null) {
knifeAuthorization.setAccessTokenExpiresAt(LocalDateTime.ofInstant(shouldBeNewAuthorization.getAccessToken().getToken().getExpiresAt(), ZoneId.systemDefault()));
}

if (authorization.getAccessToken().getToken().getExpiresAt() != null) {
knifeAuthorization.setAccessTokenExpiresAt(LocalDateTime.ofInstant(authorization.getAccessToken().getToken().getExpiresAt(), ZoneId.systemDefault()));
// Token Expiration
knifeAuthorization.setRefreshTokenIssuedAt(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));
if (shouldBeNewAuthorization.getRefreshToken().getToken().getExpiresAt() != null) {
knifeAuthorization.setRefreshTokenExpiresAt(LocalDateTime.ofInstant(shouldBeNewAuthorization.getRefreshToken().getToken().getExpiresAt(), ZoneId.systemDefault()));
}

knifeAuthorizationRepository.save(knifeAuthorization);

knifeAuthorizationRepository.save(knifeAuthorization);

if(securityPointCut != null){
securityPointCut.afterTokensSaved(knifeAuthorization, null);
Expand Down Expand Up @@ -166,7 +160,7 @@ public OAuth2Authorization findByToken(@NotEmpty String tokenValue, @Nullable OA
* Same ( org.springframework.security.core.userdetails : userName + spring-authorization-server : principalName )
* */
public @Nullable OAuth2Authorization findByUserNameAndClientIdAndAppToken(@NotEmpty String userName, @NotEmpty String clientId, @Nullable String appToken) {
return findOAuth2AuthorizationByAccessTokenValueSafely(() -> knifeAuthorizationRepository.findByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken(userName, clientId, appToken).map(KnifeAuthorization::getAttributes),
return findOAuth2AuthorizationByAccessTokenValueSafely(() -> knifeAuthorizationRepository.findValidAuthorizationByPrincipalNameAndClientIdAndAppToken(userName, clientId, appToken).map(KnifeAuthorization::getAttributes),
e -> {
knifeAuthorizationRepository.findListByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken(userName, clientId, appToken).ifPresent(knifeAuthorizationRepository::deleteAll);
knifeAuthorizationRepository.deleteByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken(userName, clientId, appToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,27 @@ private RegisteredClient mapToRegisteredClient(KnifeClient detail) {
.map(AuthorizationGrantType::new)
.collect(Collectors.toSet());

// Assuming getTokenSettings() returns a map-like structure for token settings.
Map<String, Object> tokenSettings = parseMap(detail.getTokenSettings());

// Extract token time-to-live values from tokenSettings (assuming they are stored as strings or numbers)
Duration accessTokenTimeToLive = Duration.ofSeconds(Long.parseLong(tokenSettings.get("access_token_time_to_live").toString()));
Duration refreshTokenTimeToLive = Duration.ofSeconds(Long.parseLong(tokenSettings.get("refresh_token_time_to_live").toString()));


return RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(detail.getClientId())
.clientSecret(detail.getClientSecret())
.clientName(detail.getClientId()) // Adjust according to your needs.
.clientName(detail.getClientId())
.clientAuthenticationMethods(authenticationMethods ->
authenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) // Adjust based on your entity
.authorizationGrantTypes(grantTypes -> grantTypes.addAll(grantTypesSet))
.scopes(scopes -> scopes.addAll(scopesSet))
.redirectUri("")
.tokenSettings(TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
.accessTokenTimeToLive(Duration.ofSeconds(3600))
.refreshTokenTimeToLive(Duration.ofSeconds(9900))
.accessTokenTimeToLive(accessTokenTimeToLive)
.refreshTokenTimeToLive(refreshTokenTimeToLive)
.build())
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) // Adjust accordingly
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.auth.authentication.DefaultAuthenticationFailureHandlerImpl;
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.auth.authentication.DefaultAuthenticationSuccessHandlerImpl;
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.resource.authentication.DefaultAuthenticationEntryPoint;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.CommonOAuth2AuthorizationCycle;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.CommonOAuth2AuthorizationSaver;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.DefaultOauth2AuthenticationHashCheckService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.IOauth2AuthenticationHashCheckService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
Expand Down Expand Up @@ -67,7 +67,7 @@ public OAuth2TokenGenerator<OAuth2Token> tokenGenerator() {
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(
HttpSecurity http,
CommonOAuth2AuthorizationCycle commonOAuth2AuthorizationCycle,
CommonOAuth2AuthorizationSaver commonOAuth2AuthorizationCycle,
OAuth2AuthorizationServiceImpl authorizationService,
ConditionalDetailsService conditionalDetailsService,
DefaultOauth2AuthenticationHashCheckService oauth2AuthenticationHashCheckService,
Expand Down
Loading

0 comments on commit ed38308

Please sign in to comment.