1111import jakarta .validation .constraints .NotEmpty ;
1212import lombok .RequiredArgsConstructor ;
1313import org .apache .commons .lang3 .StringUtils ;
14+ import org .slf4j .Logger ;
15+ import org .slf4j .LoggerFactory ;
1416import org .springframework .beans .factory .annotation .Value ;
1517import org .springframework .context .annotation .Configuration ;
1618
2931import java .util .function .Consumer ;
3032import java .util .function .Supplier ;
3133
34+ /**
35+ * According to the 'OAuth2AuthorizationService' implementation,
36+ * When a single value is expected to be returned, there's no need to explicitly end the function name with "One".
37+ * So when multiple values are expected to be returned, I have made the function name end with "List" to distinguish them.
38+ * @author Andrew Kang
39+ * @since 0.0.O
40+ * @see OAuth2Authorization
41+ * @see KnifeAuthorization
42+ */
3243@ Configuration
3344@ RequiredArgsConstructor
3445public class OAuth2AuthorizationServiceImpl implements OAuth2AuthorizationService {
3546
47+ private static final Logger logger = LoggerFactory .getLogger (OAuth2AuthorizationServiceImpl .class );
48+
3649 private final KnifeAuthorizationRepository knifeAuthorizationRepository ;
3750 private final SecurityPointCut securityPointCut ;
3851
@@ -104,9 +117,6 @@ public void save(OAuth2Authorization shouldBeNewAuthorization) {
104117 * 2. R for Read
105118 * */
106119
107- /*
108- * 1) AccessToken Token R + RefreshToken Token R
109- * */
110120 @ Override
111121 public OAuth2Authorization findByToken (@ NotEmpty String tokenValue , @ Nullable OAuth2TokenType tokenType ) {
112122
@@ -120,42 +130,12 @@ public OAuth2Authorization findByToken(@NotEmpty String tokenValue, @Nullable OA
120130 return knifeAuthorizationRepository .findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValueOrUserCodeValueOrDeviceCodeValue (hashedTokenValue ).map (KnifeAuthorization ::getAttributes ).orElse (null );
121131 }
122132 }
123- /*
124- * 2) AccessToken Token R
125- * */
126- private @ Nullable OAuth2Authorization findOAuth2AuthorizationByAccessTokenValueSafely (Supplier <Optional <OAuth2Authorization >> authorizationSupplier , Consumer <Exception > exceptionHandler ) {
127-
128- OAuth2Authorization oAuth2Authorization = null ;
129- try {
130- oAuth2Authorization = authorizationSupplier .get ().orElse (null );
131-
132- } catch (Exception e ) {
133-
134- exceptionHandler .accept (e );
135-
136- // Retry only one more time
137- oAuth2Authorization = authorizationSupplier .get ().orElse (null );
138-
139- }
140-
141- if (oAuth2Authorization != null && oAuth2Authorization .getAccessToken () != null
142- && oAuth2Authorization .getAccessToken ().isExpired ()) {
143- // 만료됨
144- knifeAuthorizationRepository .deleteByAccessTokenValue (oAuth2Authorization .getAccessToken ().getToken ().getTokenValue ());
145-
146- return null ;
147- }
148- return oAuth2Authorization ;
149- }
150-
151133
152-
153134 @ Override
154135 public @ Nullable OAuth2Authorization findById (String id ) {
155136 return knifeAuthorizationRepository .findById (id )
156137 .map (KnifeAuthorization ::getAttributes )
157138 .orElse (null );
158-
159139 }
160140
161141
@@ -165,22 +145,60 @@ public OAuth2Authorization findByToken(@NotEmpty String tokenValue, @Nullable OA
165145 * [IMPORTANT] KEY = Username (principalName) + ClientId + AppToken
166146 * Same ( org.springframework.security.core.userdetails : userName + spring-authorization-server : principalName )
167147 * */
148+ /**
149+ * Returns the {@link OAuth2Authorization} identified by the provided {@code Username (principalName) + ClientId + AppToken}, or
150+ * {@code null} if not found.
151+ * @param userName org.springframework.security.core.userdetails, which is same as principalName
152+ * @param clientId Oauth2 ROPC client_id
153+ * @param appToken See the README
154+ * @return the {@link OAuth2Authorization} if found, otherwise {@code null}
155+ */
168156 public @ Nullable OAuth2Authorization findByUserNameAndClientIdAndAppToken (@ NotEmpty String userName , @ NotEmpty String clientId , @ Nullable String appToken ) {
169157 if (noAppTokenSameAccessToken ) {
170- return findAuthorization (() -> knifeAuthorizationRepository .findValidAuthorizationByPrincipalNameAndClientIdAndNullableAppToken (userName , clientId , appToken ), userName , clientId , appToken );
158+ return findSafelyByPrincipalNameAndClientIdAndAppToken (() -> knifeAuthorizationRepository .findValidAuthorizationByPrincipalNameAndClientIdAndNullableAppToken (userName , clientId , appToken ), userName , clientId , appToken );
171159 } else {
172- return findAuthorization (() -> knifeAuthorizationRepository .findValidAuthorizationByPrincipalNameAndClientIdAndAppToken (userName , clientId , appToken ), userName , clientId , appToken );
160+ return findSafelyByPrincipalNameAndClientIdAndAppToken (() -> knifeAuthorizationRepository .findValidAuthorizationByPrincipalNameAndClientIdAndAppToken (userName , clientId , appToken ), userName , clientId , appToken );
173161 }
174162 }
175163
176- private @ Nullable OAuth2Authorization findAuthorization (Supplier <Optional <KnifeAuthorization >> authorizationSupplier , String userName , String clientId , @ Nullable String appToken ) {
177- return findOAuth2AuthorizationByAccessTokenValueSafely (() -> authorizationSupplier .get ().map (KnifeAuthorization ::getAttributes ),
164+ private @ Nullable OAuth2Authorization findSafelyByPrincipalNameAndClientIdAndAppToken (Supplier <Optional <KnifeAuthorization >> authorizationSupplier , String userName , String clientId , @ Nullable String appToken ) {
165+ return findByAccessTokenValueSafely (() -> authorizationSupplier .get ().map (KnifeAuthorization ::getAttributes ),
178166 e -> {
167+
168+ logger .warn ("Error finding authorization for user: {}, clientId: {}, appToken: {}" , userName , clientId , appToken , e );
169+
170+ // If multiple results are detected or other unexpected errors occur, remove access tokens for the account to prevent login errors.
179171 knifeAuthorizationRepository .findListByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken (userName , clientId , appToken ).ifPresent (knifeAuthorizationRepository ::deleteAll );
180172 knifeAuthorizationRepository .deleteByPrincipalNameAndRegisteredClientIdAndAccessTokenAppToken (userName , clientId , appToken );
181173 });
182174 }
183175
176+ private @ Nullable OAuth2Authorization findByAccessTokenValueSafely (Supplier <Optional <OAuth2Authorization >> authorizationSupplier , Consumer <Exception > exceptionHandler ) {
177+
178+ OAuth2Authorization oAuth2Authorization = null ;
179+ try {
180+ oAuth2Authorization = authorizationSupplier .get ().orElse (null );
181+
182+ } catch (Exception e ) {
183+
184+ exceptionHandler .accept (e );
185+
186+ // Retry only one more time
187+ oAuth2Authorization = authorizationSupplier .get ().orElse (null );
188+
189+ }
190+
191+ if (oAuth2Authorization != null && oAuth2Authorization .getAccessToken () != null
192+ && oAuth2Authorization .getAccessToken ().isExpired ()) {
193+ // 만료됨
194+ knifeAuthorizationRepository .deleteByAccessTokenValue (oAuth2Authorization .getAccessToken ().getToken ().getTokenValue ());
195+
196+ return null ;
197+ }
198+ return oAuth2Authorization ;
199+ }
200+
201+
184202
185203
186204 /*
0 commit comments