Skip to content

Commit efdb29b

Browse files
Merge branch 'normalize-grant-type'
2 parents 2838db7 + 1f37e2d commit efdb29b

File tree

18 files changed

+223
-265
lines changed

18 files changed

+223
-265
lines changed

client/src/main/java/com/patternhelloworld/securityhelper/oauth2/client/config/securityimpl/message/CustomSecurityUserExceptionMessage.java

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public enum CustomSecurityUserExceptionMessage implements ExceptionMessageInterf
2727

2828
// GRANT TYPE
2929
AUTHENTICATION_WRONG_GRANT_TYPE("1Wrong Grant Type detected."),
30+
AUTHENTICATION_WRONG_COMBINATION_OF_GRANT_TYPE_RESPONSE_TYPE("1Grant Type doesn't match response type."),
3031

3132
// OAuth2 : Authorization Code
3233
AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD("1Wrong Authorization Code request."),

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

+32-34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.patternhelloworld.securityhelper.oauth2.client.config.securityimpl.response;
22

33

4+
import com.mysema.commons.lang.Assert;
45
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
56
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
67
import io.github.patternhelloworld.securityhelper.oauth2.api.config.security.response.error.dto.EasyPlusErrorMessages;
@@ -41,8 +42,7 @@
4142
@RequiredArgsConstructor
4243
public class CustomApiAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
4344

44-
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
45-
new OAuth2AccessTokenResponseHttpMessageConverter();
45+
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
4646

4747

4848
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
@@ -56,25 +56,38 @@ public void onAuthenticationSuccess(final HttpServletRequest request, final Http
5656
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
5757
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
5858

59-
if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.PASSWORD.getValue())
60-
|| ((String) additionalParameters.get("grant_type")).equals(OAuth2ParameterNames.CODE)) {
61-
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
62-
.tokenType(accessToken.getTokenType())
63-
.scopes(accessToken.getScopes());
64-
if (accessToken.getExpiresAt() != null) {
65-
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), accessToken.getExpiresAt()));
66-
}
67-
if (refreshToken != null) {
68-
builder.refreshToken(refreshToken.getTokenValue());
59+
if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.PASSWORD.getValue()) || ((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())) {
60+
61+
if (AuthorizationGrantType.PASSWORD.getValue().equals(additionalParameters.get("grant_type")) &&
62+
OAuth2ParameterNames.CODE.equals(additionalParameters.get("response_type"))) {
63+
64+
String code = (String) additionalParameters.get("code");
65+
66+
Assert.notNull(code, "code is required");
67+
68+
String jsonResponse = String.format("{\"code\":\"%s\"}", code);
69+
70+
response.setContentType("application/json");
71+
response.setCharacterEncoding("UTF-8");
72+
response.getWriter().write(jsonResponse);
73+
74+
} else {
75+
76+
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()).tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
77+
if (accessToken.getExpiresAt() != null) {
78+
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), accessToken.getExpiresAt()));
79+
}
80+
if (refreshToken != null) {
81+
builder.refreshToken(refreshToken.getTokenValue());
82+
}
83+
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
84+
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
85+
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
86+
6987
}
70-
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
71-
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
72-
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
7388

7489
} else if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.REFRESH_TOKEN.getValue())) {
75-
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
76-
.tokenType(accessToken.getTokenType())
77-
.scopes(accessToken.getScopes());
90+
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()).tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
7891
if (refreshToken.getExpiresAt() != null) {
7992
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), refreshToken.getExpiresAt()));
8093
}
@@ -85,23 +98,8 @@ public void onAuthenticationSuccess(final HttpServletRequest request, final Http
8598
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
8699
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
87100

88-
} else if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())) {
89-
// Authorization Code만 JSON으로 응답
90-
String code = (String) additionalParameters.get("authorization_code");
91-
92-
// JSON 응답 생성 (authorization_code만 포함)
93-
String jsonResponse = String.format("{\"code\":\"%s\"}", code);
94-
95-
// JSON 응답 전송
96-
response.setContentType("application/json");
97-
response.setCharacterEncoding("UTF-8");
98-
response.getWriter().write(jsonResponse);
99-
100101
} else {
101-
throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder()
102-
.message("Wrong grant type from Req : " + (String) additionalParameters.get("grant_type"))
103-
.userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_GRANT_TYPE))
104-
.build());
102+
throw new EasyPlusOauth2AuthenticationException(EasyPlusErrorMessages.builder().message("Wrong grant type from Req : " + (String) additionalParameters.get("grant_type")).userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_GRANT_TYPE)).build());
105103
}
106104
}
107105

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!DOCTYPE html>
22
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
33
<head>
4-
<meta charset="utf-8" />
4+
<meta charset="utf-8"/>
55
<meta name="viewport" content="width=device-width, initial-scale=1">
66
<title>Spring Authorization Server Sample</title>
77
<style>
@@ -11,11 +11,13 @@
1111
border: 1px solid transparent;
1212
border-radius: 4px;
1313
}
14+
1415
.alert-danger {
1516
color: #a94442;
1617
background-color: #f2dede;
1718
border-color: #ebccd1;
1819
}
20+
1921
.alert-success {
2022
color: #3c763d;
2123
background-color: #dff0d8;
@@ -69,7 +71,7 @@ <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
6971
const password = passwordInput.value;
7072

7173
// Extract query parameters from the current URL
72-
const { client_id, state, scope, redirect_uri } = getQueryParameters();
74+
const {client_id, state, scope, redirect_uri} = getQueryParameters();
7375

7476
// Basic Auth header creation
7577
const clientSecret = '12345'; // Enter client secret
@@ -86,7 +88,8 @@ <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
8688
body: new URLSearchParams({
8789
'username': username,
8890
'password': password,
89-
'grant_type': "authorization_code"
91+
'grant_type': "password",
92+
'response_type': "code"
9093
})
9194
});
9295

@@ -99,6 +102,8 @@ <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
99102
const tokenResult = await tokenResponse.json();
100103
const authorizationCode = tokenResult.code; // Extract authorization code
101104

105+
console.log(tokenResult)
106+
102107
// Build the dynamic authorization URL
103108
const authorizeUrl = `/oauth2/authorize?response_type=code&client_id=${client_id}&code=${authorizationCode}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(scope)}&redirect_uri=${encodeURIComponent(redirect_uri)}`;
104109

client/src/test/java/com/patternhelloworld/securityhelper/oauth2/client/integration/auth/CustomerIntegrationTest.java

+3-34
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,11 @@ public void test_SameAppTokensUseSameAccessToken_ORIGINAL() throws Exception {
449449
@Test
450450
public void testLoginWithInvalidCredentials_ORIGINAL() throws Exception {
451451

452-
453452
MvcResult result = mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/token")
454-
.header(HttpHeaders.AUTHORIZATION, basicHeader)
453+
.header(HttpHeaders.AUTHORIZATION, "Basic " + DatatypeConverter.printBase64Binary((appUserClientId + "wrongcred:" + appUserClientSecret).getBytes("UTF-8")))
455454
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
456455
.param("grant_type", "password")
457-
.param("username", testUserName + "wrongcredential")
456+
.param("username", testUserName)
458457
.param("password", testUserPassword))
459458
.andExpect(status().isUnauthorized()) // 401
460459
.andDo(document( "{class-name}/{method-name}/oauth-access-token",
@@ -476,37 +475,7 @@ public void testLoginWithInvalidCredentials_ORIGINAL() throws Exception {
476475
JSONObject jsonResponse = new JSONObject(responseString);
477476
String userMessage = jsonResponse.getString("userMessage");
478477

479-
//assertEquals(userMessage, CustomSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE.getMessage());
480-
assertTrue(userMessage.contains("NOT Found"));
481-
482-
483-
result = mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/token")
484-
.header(HttpHeaders.AUTHORIZATION, "Basic " + DatatypeConverter.printBase64Binary((appUserClientId + "wrongcred:" + appUserClientSecret).getBytes("UTF-8")))
485-
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
486-
.param("grant_type", "password")
487-
.param("username", testUserName)
488-
.param("password", testUserPassword))
489-
.andExpect(status().isUnauthorized()) // 401
490-
.andDo(document( "{class-name}/{method-name}/oauth-access-token",
491-
preprocessRequest(new AccessTokenMaskingPreprocessor()),
492-
preprocessResponse(new AccessTokenMaskingPreprocessor(), prettyPrint()),
493-
requestHeaders(
494-
headerWithName(HttpHeaders.AUTHORIZATION).description("Connect the received client_id and client_secret with ':', use the base64 function, and write Basic at the beginning. ex) Basic base64(client_id:client_secret)"),
495-
headerWithName(EasyPlusHttpHeaders.APP_TOKEN).optional().description("Not having a value does not mean you cannot log in, but cases without an App-Token value share the same access_token. Please include it as a required value according to the device-specific session policy.")
496-
),
497-
formParameters(
498-
parameterWithName("grant_type").description("Uses the password method among Oauth2 grant_types. Please write password."),
499-
parameterWithName("username").description("This is the user's email address."),
500-
parameterWithName("password").description("This is the user's password.")
501-
)))
502-
.andReturn();
503-
504-
505-
responseString = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
506-
jsonResponse = new JSONObject(responseString);
507-
userMessage = jsonResponse.getString("userMessage");
508-
509-
assertEquals(userMessage, CustomSecurityUserExceptionMessage.AUTHENTICATION_WRONG_CLIENT_ID_SECRET.getMessage());
478+
assertEquals(userMessage, CustomSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_ERROR.getMessage());
510479

511480

512481

lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/converter/auth/endpoint/AuthorizationCodeAuthorizationRequestConverter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public Authentication convert(HttpServletRequest request) {
5959
throw new EasyPlusOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD));
6060
}
6161

62-
MultiValueMap<String, String> parameters = EasyPlusOAuth2EndpointUtils.getWebParameters(request);
62+
MultiValueMap<String, String> parameters = EasyPlusOAuth2EndpointUtils.getWebParametersContainingEasyPlusHeaders(request);
6363

6464
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
6565
if (!StringUtils.hasText(clientId)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.github.patternhelloworld.securityhelper.oauth2.api.config.security.converter.auth.endpoint;
2+
3+
import io.github.patternhelloworld.securityhelper.oauth2.api.config.util.EasyPlusOAuth2EndpointUtils;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.security.core.Authentication;
7+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
8+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
9+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
10+
import org.springframework.security.web.authentication.AuthenticationConverter;
11+
12+
import java.util.Map;
13+
14+
@RequiredArgsConstructor
15+
public final class OpaqueGrantTypeAccessTokenRequestConverter implements AuthenticationConverter {
16+
17+
@Override
18+
public Authentication convert(HttpServletRequest request) {
19+
20+
Map<String, Object> allParameters = EasyPlusOAuth2EndpointUtils.getApiParametersContainingEasyPlusHeaders(request);
21+
22+
String clientId = allParameters.get("client_id").toString();
23+
24+
// All token requests are "CLIENT_SECRET_BASIC"
25+
ClientAuthenticationMethod clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_BASIC;
26+
Object credentials = null;
27+
28+
OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
29+
.clientId(clientId)
30+
.authorizationUri(request.getRequestURL().toString())
31+
.additionalParameters(allParameters)
32+
.build();
33+
34+
allParameters.put(OAuth2AuthorizationRequest.class.getName(), authorizationRequest);
35+
36+
return new OAuth2ClientAuthenticationToken(
37+
clientId,
38+
clientAuthenticationMethod,
39+
credentials,
40+
allParameters
41+
);
42+
}
43+
44+
45+
46+
}

lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/converter/auth/endpoint/PasswordAccessTokenRequestConverter.java

-35
This file was deleted.

lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/entity/EasyPlusAuthorization.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ public class EasyPlusAuthorization {
141141
private String deviceCodeMetadata;
142142

143143

144-
public void hashSetAuthorizationCodeValue(String authorizationCodeValue) {
144+
/* public void hashSetAuthorizationCodeValue(String authorizationCodeValue) {
145145
this.authorizationCodeValue = CustomAuthenticationKeyGenerator.hashTokenValue(authorizationCodeValue);
146-
}
146+
}*/
147147
public void hashSetAccessTokenValue(String accessTokenValue) {
148148
this.accessTokenValue = CustomAuthenticationKeyGenerator.hashTokenValue(accessTokenValue);
149149
}

lib/src/main/java/io/github/patternhelloworld/securityhelper/oauth2/api/config/security/message/DefaultSecurityUserExceptionMessage.java

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public enum DefaultSecurityUserExceptionMessage implements ExceptionMessageInter
2626

2727
// GRANT TYPE
2828
AUTHENTICATION_WRONG_GRANT_TYPE("Wrong Grant Type detected."),
29+
AUTHENTICATION_WRONG_COMBINATION_OF_GRANT_TYPE_RESPONSE_TYPE("Grant Type doesn't match response type."),
2930

3031
// OAuth2 : Authorization Code
3132
AUTHENTICATION_AUTHORIZATION_CODE_REQUEST_WRONG_METHOD("Wrong Authorization Code request."),

0 commit comments

Comments
 (0)