|
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