Skip to content

Commit 38cbfa8

Browse files
feature : add the validator to the authorization code
1 parent 91c1603 commit 38cbfa8

File tree

19 files changed

+308
-223
lines changed

19 files changed

+308
-223
lines changed

client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/response/CustomWebAuthenticationFailureHandlerImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
44
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException;
5-
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.ErrorCodeConstants;
5+
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.EasyPlusErrorCodeConstants;
66
import jakarta.servlet.ServletException;
77
import jakarta.servlet.http.HttpServletRequest;
88
import jakarta.servlet.http.HttpServletResponse;
@@ -43,13 +43,13 @@ public void onAuthenticationFailure(HttpServletRequest request, HttpServletRespo
4343
// Extract error messages if the exception is of type EasyPlusOauth2AuthenticationException
4444
if (exception instanceof EasyPlusOauth2AuthenticationException) {
4545
oauth2Exception = (EasyPlusOauth2AuthenticationException) exception;
46-
errorMessage = oauth2Exception.getErrorMessages().getUserMessage();
46+
errorMessage = oauth2Exception.getErrorMessages().getUserMessage() + "(" + oauth2Exception.getErrorMessages().getErrorCode() + ")";
4747

48-
if(oauth2Exception.getError().getErrorCode().equals(ErrorCodeConstants.REDIRECT_TO_LOGIN)){
48+
if(oauth2Exception.getError().getErrorCode().equals(EasyPlusErrorCodeConstants.REDIRECT_TO_LOGIN)){
4949
request.getRequestDispatcher("/login").forward(request, response);
5050
return;
5151
}
52-
if(oauth2Exception.getError().getErrorCode().equals(ErrorCodeConstants.REDIRECT_TO_CONSENT)){
52+
if(oauth2Exception.getError().getErrorCode().equals(EasyPlusErrorCodeConstants.REDIRECT_TO_CONSENT)){
5353
// Construct full URL
5454
String fullURL = request.getRequestURL().toString();
5555
if (request.getQueryString() != null) {

client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/response/CustomWebAuthenticationSuccessHandlerImpl.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
import jakarta.servlet.http.HttpServletRequest;
77
import jakarta.servlet.http.HttpServletResponse;
88
import lombok.RequiredArgsConstructor;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
911
import org.springframework.beans.factory.annotation.Qualifier;
1012
import org.springframework.context.annotation.Configuration;
1113
import org.springframework.context.annotation.Primary;
1214
import org.springframework.security.core.Authentication;
1315
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
16+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
1417
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
1518

1619
import java.io.IOException;
@@ -21,16 +24,25 @@
2124
@RequiredArgsConstructor
2225
public class CustomWebAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
2326

27+
private static final Logger logger = LoggerFactory.getLogger(CustomWebAuthenticationSuccessHandlerImpl.class);
28+
2429
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
2530

2631
@Override
2732
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
28-
OAuth2AuthorizationCodeAuthenticationToken oAuth2AuthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
29-
30-
String redirectUri = oAuth2AuthorizationCodeAuthenticationToken.getRedirectUri();
31-
String authorizationCode = oAuth2AuthorizationCodeAuthenticationToken.getCode();
32-
String state = oAuth2AuthorizationCodeAuthenticationToken.getAdditionalParameters().get("state").toString();
33-
34-
response.sendRedirect(redirectUri+"?code="+authorizationCode+"&state="+state);
33+
if(authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken) {
34+
request.getRequestDispatcher("/login").forward(request, response);
35+
}else if(authentication instanceof OAuth2AuthorizationCodeAuthenticationToken) {
36+
OAuth2AuthorizationCodeAuthenticationToken oAuth2AuthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
37+
38+
String redirectUri = oAuth2AuthorizationCodeAuthenticationToken.getRedirectUri();
39+
String authorizationCode = oAuth2AuthorizationCodeAuthenticationToken.getCode();
40+
String state = oAuth2AuthorizationCodeAuthenticationToken.getAdditionalParameters().get("state").toString();
41+
42+
response.sendRedirect(redirectUri+"?code="+authorizationCode+"&state="+state);
43+
}else{
44+
logger.error("Wrong Authentication Type : {}", authentication.getClass());
45+
request.getRequestDispatcher("/login").forward(request, response);
46+
}
3547
}
3648
}

client/src/main/resources/templates/login.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
7373
const password = passwordInput.value;
7474

7575
// Extract query parameters from the current URL
76-
const {client_id, state, scope, redirect_uri, code_challenge, code_challenge_method} = getQueryParameters();
76+
const {client_id, response_type, state, scope, redirect_uri, code_challenge, code_challenge_method} = getQueryParameters();
7777

7878
// Basic Auth header creation
7979
const clientSecret = '12345'; // Enter client secret
@@ -83,7 +83,7 @@ <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
8383
'username': username,
8484
'password': password,
8585
'grant_type': "password",
86-
'response_type': "code",
86+
'response_type': response_type,
8787
'scope': scope
8888
};
8989

Lines changed: 20 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,45 @@
77
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages;
88
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.exception.EasyPlusOauth2AuthenticationException;
99
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
10+
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.validator.endpoint.authorization.CodeValidationResult;
11+
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.EasyPlusErrorCodeConstants;
1012
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.EasyPlusOAuth2EndpointUtils;
11-
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.ErrorCodeConstants;
1213
import jakarta.servlet.http.HttpServletRequest;
1314
import lombok.RequiredArgsConstructor;
1415
import org.springframework.http.HttpMethod;
15-
import org.springframework.security.authentication.AnonymousAuthenticationToken;
1616
import org.springframework.security.core.Authentication;
17-
import org.springframework.security.core.authority.AuthorityUtils;
1817
import org.springframework.security.core.context.SecurityContextHolder;
1918
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
2019
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
2120
import org.springframework.security.oauth2.core.oidc.OidcScopes;
2221
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
2322
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
2423
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
24+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
2525
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
2626
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
27-
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
2827
import org.springframework.security.web.authentication.AuthenticationConverter;
2928
import org.springframework.security.web.util.matcher.AndRequestMatcher;
3029
import org.springframework.security.web.util.matcher.RequestMatcher;
30+
import org.springframework.util.Assert;
3131
import org.springframework.util.MultiValueMap;
3232
import org.springframework.util.StringUtils;
3333

34-
import java.util.*;
34+
import java.util.Map;
35+
import java.util.function.Function;
3536

3637
@RequiredArgsConstructor
37-
public final class AuthorizationCodeAuthorizationRequestConverter implements AuthenticationConverter {
38+
public final class CodeAuthorizationConditionalConverter implements AuthenticationConverter {
39+
40+
private Function<MultiValueMap<String, String>, CodeValidationResult> authenticationValidator;
3841

39-
private final RegisteredClientRepository registeredClientRepository;
4042
private final EasyPlusAuthorizationConsentRepository easyPlusAuthorizationConsentRepository;
4143
private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService;
4244

4345
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
4446

4547
private final String consentYN;
4648

47-
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous",
48-
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
49-
50-
private static final RequestMatcher OIDC_REQUEST_MATCHER = createOidcRequestMatcher();
51-
5249
/*
5350
* Why is the validation check done here?
5451
* - Because if an OAuth2AuthenticationException is thrown in the CustomizedProvider,
@@ -62,56 +59,19 @@ public Authentication convert(HttpServletRequest request) {
6259

6360
MultiValueMap<String, String> parameters = EasyPlusOAuth2EndpointUtils.getWebParametersContainingEasyPlusHeaders(request);
6461

65-
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
66-
if (!StringUtils.hasText(clientId)) {
67-
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_CLIENT_ID_MISSING));
68-
}
69-
String redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);
70-
if (!StringUtils.hasText(redirectUri)) {
71-
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_REDIRECT_URI_MISSING));
72-
}
73-
74-
Map<String, Object> additionalParameters = new HashMap<>();
62+
CodeValidationResult codeValidationResult = this.authenticationValidator.apply(parameters);
7563

76-
parameters.forEach((key, value) -> {
77-
additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));
78-
});
79-
80-
81-
String state = parameters.getFirst(OAuth2ParameterNames.STATE);
82-
if (!StringUtils.hasText(state)) {
83-
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_STATE_MISSING));
84-
}
85-
86-
Set<String> requestedScopes = new HashSet<>(parameters.getOrDefault(OAuth2ParameterNames.SCOPE, Collections.emptyList()));
64+
Map<String, Object> additionalParameters = EasyPlusOAuth2EndpointUtils.convertMultiValueMapToMap(parameters);
8765

8866
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
8967
if (principal == null) {
90-
setClientAuthenticationContext(clientId);
68+
setClientAuthenticationContext(codeValidationResult.getRegisteredClient());
9169
principal = SecurityContextHolder.getContext().getAuthentication();
9270
}
9371

94-
RegisteredClient registeredClient = ((OAuth2ClientAuthenticationToken) principal).getRegisteredClient();
95-
96-
if (registeredClient == null) {
97-
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_REGISTERED_CLIENT_NOT_FOUND));
98-
}
99-
100-
if (!registeredClient.getRedirectUris().contains(redirectUri)) {
101-
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_INVALID_REDIRECT_URI));
102-
}
103-
104-
Set<String> registeredScopes = registeredClient.getScopes(); // Scopes from the RegisteredClient
105-
106-
if (!registeredScopes.containsAll(requestedScopes)) {
107-
throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder().userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_ERROR))
108-
.message("Invalid scopes: " + requestedScopes + ". Allowed scopes: " + registeredScopes).build());
109-
}
110-
11172
String code = parameters.getFirst(OAuth2ParameterNames.CODE);
11273
if (!StringUtils.hasText(code)) {
113-
throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder().userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_AUTHORIZATION_CODE_MISSING))
114-
.errorCode(ErrorCodeConstants.REDIRECT_TO_LOGIN).build());
74+
return new OAuth2AuthorizationCodeRequestAuthenticationToken(request.getRequestURL().toString(), codeValidationResult.getClientId(), principal, codeValidationResult.getRedirectUri(), codeValidationResult.getState(), codeValidationResult.getScope(), additionalParameters);
11575
}
11676

11777
// Check Consent
@@ -129,15 +89,15 @@ public Authentication convert(HttpServletRequest request) {
12989
} else {
13090
// This means the user should check authorization consent OK
13191
throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder().userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_AUTHORIZATION_CODE_MISSING))
132-
.errorCode(ErrorCodeConstants.REDIRECT_TO_CONSENT).build());
92+
.errorCode(EasyPlusErrorCodeConstants.REDIRECT_TO_CONSENT).build());
13393
}
13494
}
13595
}
13696

13797
return new OAuth2AuthorizationCodeAuthenticationToken(
13898
code,
13999
principal,
140-
redirectUri,
100+
codeValidationResult.getRedirectUri(),
141101
additionalParameters
142102
);
143103
}
@@ -153,12 +113,7 @@ private static RequestMatcher createOidcRequestMatcher() {
153113
return new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher, openidScopeMatcher);
154114
}
155115

156-
public void setClientAuthenticationContext(String clientId) {
157-
RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId);
158-
if (registeredClient == null) {
159-
throw new IllegalArgumentException("Invalid client ID");
160-
}
161-
116+
public void setClientAuthenticationContext(RegisteredClient registeredClient) {
162117
OAuth2ClientAuthenticationToken clientAuthenticationToken = new OAuth2ClientAuthenticationToken(
163118
registeredClient,
164119
ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
@@ -168,5 +123,9 @@ public void setClientAuthenticationContext(String clientId) {
168123
SecurityContextHolder.getContext().setAuthentication(clientAuthenticationToken);
169124
}
170125

126+
public void setAuthenticationValidator(Function<MultiValueMap<String, String>, CodeValidationResult> authenticationValidator) {
127+
Assert.notNull(authenticationValidator, "authenticationValidator cannot be null");
128+
this.authenticationValidator = authenticationValidator;
129+
}
171130
}
172131

lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/provider/auth/endpoint/authorization/AuthorizationCodeAuthenticationProvider.java

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)