|
6 | 6 | import java.util.Map;
|
7 | 7 | import java.util.Set;
|
8 | 8 |
|
| 9 | +import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage; |
| 10 | +import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService; |
9 | 11 | import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
|
| 12 | +import org.apache.commons.logging.Log; |
| 13 | +import org.apache.commons.logging.LogFactory; |
10 | 14 | import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
11 | 15 | import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
12 | 16 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
|
19 | 23 | import org.springframework.ui.Model;
|
20 | 24 | import org.springframework.util.StringUtils;
|
21 | 25 | import org.springframework.web.bind.annotation.GetMapping;
|
| 26 | +import org.springframework.web.bind.annotation.PostMapping; |
22 | 27 | import org.springframework.web.bind.annotation.RequestParam;
|
23 | 28 |
|
24 | 29 | /**
|
25 | 30 | * @author Daniel Garnier-Moiroux
|
26 | 31 | */
|
27 | 32 | @Controller
|
28 | 33 | public class KnifeAuthorizationCodeRequestConverterController {
|
| 34 | + |
| 35 | + private final Log logger = LogFactory.getLog(this.getClass()); |
| 36 | + |
29 | 37 | private final RegisteredClientRepository registeredClientRepository;
|
30 | 38 | private final OAuth2AuthorizationConsentService authorizationConsentService;
|
31 | 39 | private final OAuth2AuthorizationServiceImpl oAuth2AuthorizationService;
|
| 40 | + private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService; |
32 | 41 |
|
33 | 42 | public KnifeAuthorizationCodeRequestConverterController(RegisteredClientRepository registeredClientRepository,
|
34 | 43 | OAuth2AuthorizationConsentService authorizationConsentService,
|
35 |
| - OAuth2AuthorizationServiceImpl oAuth2AuthorizationService) { |
| 44 | + OAuth2AuthorizationServiceImpl oAuth2AuthorizationService, ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService) { |
36 | 45 | this.registeredClientRepository = registeredClientRepository;
|
37 | 46 | this.authorizationConsentService = authorizationConsentService;
|
38 | 47 | this.oAuth2AuthorizationService = oAuth2AuthorizationService;
|
| 48 | + this.iSecurityUserExceptionMessageService = iSecurityUserExceptionMessageService; |
39 | 49 | }
|
40 | 50 |
|
| 51 | + |
| 52 | + @PostMapping("/oauth2/authorization") |
| 53 | + public String authorize(@RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId, |
| 54 | + @RequestParam(OAuth2ParameterNames.STATE) String state, |
| 55 | + @RequestParam(OAuth2ParameterNames.SCOPE) Set<String> scopes, |
| 56 | + @RequestParam(name = OAuth2ParameterNames.CODE, required = false) String authorizationCode, |
| 57 | + @RequestParam(name = "consent_action", required = false) String consentAction, |
| 58 | + Model model) { |
| 59 | + // 예시: 클라이언트의 등록된 콜백 URL 가져오기 |
| 60 | + RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId); |
| 61 | + String redirectUri = registeredClient.getRedirectUris().iterator().next(); |
| 62 | + |
| 63 | + // 승인된 스코프를 바탕으로 Authorization Code 생성 로직 |
| 64 | + // Authorization Code를 생성하여 저장하고 해당 코드를 콜백 URL로 리다이렉트합니다. |
| 65 | + if ("approve".equals(consentAction)) { |
| 66 | + // 실제로는 이곳에서 OAuth2Authorization 객체를 생성하고 저장하는 로직 필요 |
| 67 | + authorizationCode = "generated-authorization-code"; // 실제 생성된 코드로 교체 |
| 68 | + |
| 69 | + // 콜백 URL로 리다이렉트하며 Authorization Code를 전달 |
| 70 | + return "redirect:" + redirectUri + "?code=" + authorizationCode + "&state=" + state; |
| 71 | + } else { |
| 72 | + // 거부한 경우 에러 페이지 혹은 다시 로그인 페이지로 리다이렉트 |
| 73 | + return "redirect:/login?error=access_denied"; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + |
| 78 | + /* |
| 79 | + * code, response_type, client_id, redirect_url |
| 80 | + * */ |
41 | 81 | @GetMapping(value = "/oauth2/authorization")
|
42 |
| - public String consent( Model model, |
| 82 | + public String consent(Model model, |
| 83 | + @RequestParam(name = OAuth2ParameterNames.CODE) String authorizationCode, |
| 84 | + @RequestParam(name = OAuth2ParameterNames.RESPONSE_TYPE) String responseType, |
43 | 85 | @RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,
|
44 |
| - @RequestParam(OAuth2ParameterNames.SCOPE) String scope, |
45 |
| - @RequestParam(OAuth2ParameterNames.STATE) String state, |
46 |
| - @RequestParam(name = OAuth2ParameterNames.CODE, required = false) String authorizationCode, |
47 |
| - @RequestParam(name = OAuth2ParameterNames.USER_CODE, required = false) String userCode) { |
| 86 | + @RequestParam(OAuth2ParameterNames.REDIRECT_URI) String redirectUri, |
| 87 | + @RequestParam(name = OAuth2ParameterNames.SCOPE, required = false) String scope) { |
| 88 | + |
48 | 89 |
|
49 |
| - String principalName; |
50 | 90 | if(authorizationCode == null){
|
51 | 91 | return "login";
|
52 | 92 | }
|
53 | 93 |
|
| 94 | + if (!"code".equals(responseType)) { |
| 95 | + logger.error("message (Invalid Authorization Code): " |
| 96 | + + "authorizationCode=" + authorizationCode + ", " |
| 97 | + + "responseType=" + responseType + ", " |
| 98 | + + "clientId=" + clientId + ", " |
| 99 | + + "redirectUri=" + redirectUri + ", " |
| 100 | + + "scope=" + scope); |
| 101 | + model.addAttribute("userMessage", iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_INVALID_RESPONSE_TYPE)); |
| 102 | + return "error"; |
| 103 | + } |
| 104 | + |
| 105 | + RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId); |
| 106 | + if(registeredClient == null){ |
| 107 | + logger.error("message (Invalid Client ID): " |
| 108 | + + "authorizationCode=" + authorizationCode + ", " |
| 109 | + + "responseType=" + responseType + ", " |
| 110 | + + "clientId=" + clientId + ", " |
| 111 | + + "redirectUri=" + redirectUri + ", " |
| 112 | + + "scope=" + scope + ", "); |
| 113 | + model.addAttribute("userMessage", iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_CLIENT_ID_SECRET)); |
| 114 | + return "error"; |
| 115 | + } |
| 116 | + |
| 117 | + if (!registeredClient.getRedirectUris().contains(redirectUri)) { |
| 118 | + logger.error("message (Invalid redirect URI): " |
| 119 | + + "authorizationCode=" + authorizationCode + ", " |
| 120 | + + "responseType=" + responseType + ", " |
| 121 | + + "clientId=" + clientId + ", " |
| 122 | + + "redirectUri=" + redirectUri + ", " |
| 123 | + + "scope=" + scope + ", " |
| 124 | + + "registeredRedirectUris=" + registeredClient.getRedirectUris().toString()); |
| 125 | + model.addAttribute("userMessage", iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_INVALID_REDIRECT_URI)); |
| 126 | + return "error"; |
| 127 | + } |
| 128 | + |
| 129 | + |
| 130 | + |
54 | 131 | OAuth2Authorization oAuth2Authorization = oAuth2AuthorizationService.findByToken(authorizationCode, new OAuth2TokenType("authorization_code"));
|
55 | 132 | if(oAuth2Authorization == null){
|
56 | 133 | return "login";
|
57 | 134 | }
|
58 |
| - principalName = oAuth2Authorization.getPrincipalName(); |
| 135 | + String principalName = oAuth2Authorization.getPrincipalName(); |
59 | 136 |
|
60 | 137 |
|
61 |
| - // Remove scopes that were already approved |
62 |
| - Set<String> scopesToApprove = new HashSet<>(); |
63 |
| - Set<String> previouslyApprovedScopes = new HashSet<>(); |
64 |
| - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId); |
| 138 | + Set<String> approvedScopes = new HashSet<>(); |
| 139 | + |
65 | 140 | OAuth2AuthorizationConsent currentAuthorizationConsent =
|
66 | 141 | this.authorizationConsentService.findById(registeredClient.getId(), principalName);
|
67 |
| - Set<String> authorizedScopes; |
68 |
| - if (currentAuthorizationConsent != null) { |
69 |
| - authorizedScopes = currentAuthorizationConsent.getScopes(); |
70 |
| - } else { |
71 |
| - authorizedScopes = Collections.emptySet(); |
72 |
| - } |
73 |
| - for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) { |
74 |
| - if (OidcScopes.OPENID.equals(requestedScope)) { |
75 |
| - continue; |
| 142 | + if(currentAuthorizationConsent != null){ |
| 143 | + return "redirect:" + redirectUri + "?code=" + authorizationCode; |
| 144 | + }else{ |
| 145 | + |
| 146 | + Set<String> authorizedScopes = currentAuthorizationConsent.getScopes(); |
| 147 | + |
| 148 | + Set<String> requestedScopes = StringUtils.commaDelimitedListToSet(scope); |
| 149 | + |
| 150 | + if (!authorizedScopes.containsAll(requestedScopes)) { |
| 151 | + logger.error("message (Scopes not approved): " |
| 152 | + + "authorizationCode=" + authorizationCode + ", " |
| 153 | + + "responseType=" + responseType + ", " |
| 154 | + + "clientId=" + clientId + ", " |
| 155 | + + "redirectUri=" + redirectUri + ", " |
| 156 | + + "scope=" + scope + ", " |
| 157 | + + "authorizedScopes=" + authorizedScopes); |
| 158 | + model.addAttribute("userMessage", iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_SCOPES_NOT_APPROVED)); |
| 159 | + return "error"; |
76 | 160 | }
|
77 |
| - if (authorizedScopes.contains(requestedScope)) { |
78 |
| - previouslyApprovedScopes.add(requestedScope); |
79 |
| - } else { |
80 |
| - scopesToApprove.add(requestedScope); |
| 161 | + |
| 162 | + for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) { |
| 163 | + if (OidcScopes.OPENID.equals(requestedScope)) { |
| 164 | + continue; |
| 165 | + } |
| 166 | + if (authorizedScopes.contains(requestedScope)) { |
| 167 | + approvedScopes.add(requestedScope); |
| 168 | + } |
81 | 169 | }
|
82 | 170 | }
|
83 | 171 |
|
| 172 | + |
| 173 | + model.addAttribute("code", authorizationCode); |
84 | 174 | model.addAttribute("clientId", clientId);
|
85 |
| - model.addAttribute("state", state); |
86 |
| - model.addAttribute("scopes", withDescription(scopesToApprove)); |
87 |
| - model.addAttribute("previouslyApprovedScopes", withDescription(previouslyApprovedScopes)); |
| 175 | + model.addAttribute("scopes", withDescription(approvedScopes)); |
88 | 176 | model.addAttribute("principalName", principalName);
|
89 |
| - model.addAttribute("userCode", userCode); |
90 |
| - if (StringUtils.hasText(userCode)) { |
91 |
| - model.addAttribute("requestURI", "/oauth2/device_verification"); |
92 |
| - } else { |
93 |
| - model.addAttribute("requestURI", "/oauth2/authorize"); |
94 |
| - } |
| 177 | + model.addAttribute("requestURI", "/oauth2/authorization"); |
95 | 178 |
|
96 | 179 | return "consent";
|
97 | 180 | }
|
@@ -140,4 +223,5 @@ public static class ScopeWithDescription {
|
140 | 223 | }
|
141 | 224 | }
|
142 | 225 |
|
| 226 | + |
143 | 227 | }
|
0 commit comments