@@ -0,0 +1,68 @@
+package nextstep.oauth2.authentication.token;
+
+import nextstep.oauth2.authentication.OAuth2AccessToken;
+import nextstep.oauth2.endpoint.dto.OAuth2AuthorizationExchange;
+import nextstep.oauth2.registration.ClientRegistration;
+import nextstep.security.authentication.Authentication;
+
+import java.util.Set;
+
+public final class OAuth2AuthorizationCodeAuthenticationToken implements Authentication {
+    private final ClientRegistration clientRegistration;
+    private final OAuth2AuthorizationExchange authorizationExchange;
+    private final OAuth2AccessToken accessToken;
+
+    public OAuth2AuthorizationCodeAuthenticationToken(
+            ClientRegistration clientRegistration,
+            OAuth2AuthorizationExchange authorizationExchange,
+            OAuth2AccessToken accessToken
+    ) {
+        this.clientRegistration = clientRegistration;
+        this.authorizationExchange = authorizationExchange;
+        this.accessToken = accessToken;
+    }
+
+    public OAuth2AuthorizationCodeAuthenticationToken(
+            ClientRegistration clientRegistration,
+            OAuth2AuthorizationExchange authorizationExchange
+    ) {
+        this(clientRegistration, authorizationExchange, null);
+    }
+
+    public ClientRegistration getClientRegistration() {
+        return clientRegistration;
+    }
+
+    public OAuth2AuthorizationExchange getAuthorizationExchange() {
+        return authorizationExchange;
+    }
+
+    public OAuth2AccessToken getAccessToken() {
+        return accessToken;
+    }
+
+    public boolean isValid() {
+        return authorizationExchange.isSameState();
+    }
+
+    @Override
+    public Set<String> getAuthorities() {
+        return Set.of();
+    }
+
+    @Override
+    public Object getCredentials() {
+        return (accessToken != null) ? accessToken.token()
+                : authorizationExchange.authorizationResponse().code();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return clientRegistration.clientId();
+    }
+
+    @Override
+    public boolean isAuthenticated() {
+        return false;
+    }
+}

리뷰: [spring-security 의 OAuth2AuthorizationCodeAuthenticationToken 소스코드를 보면](https://github.com/spring-projects/spring-security/blob/7fc5d50adfe5546ca725f3259e4d65ee6083f7cd/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationToken.java#L112)
principal 로 clientId 값을,
credentials 로 accessToken 혹은 code 값을 사용하고 있습니다.
[authenticated 값 또한 true](https://github.com/spring-projects/spring-security/blob/7fc5d50adfe5546ca725f3259e4d65ee6083f7cd/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationToken.java#L106) 라고 설정되어 있고요.
하지만 저는 principal 과 credentials 를 null 값으로, isAuthenticated 를 false 로 설정하였습니다.

저는 맨 처음에 [OAuth2AuthorizationCodeGrantFilter](https://github.com/spring-projects/spring-security/blob/7fc5d50adfe5546ca725f3259e4d65ee6083f7cd/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java#L234) 처럼 OAuth2AuthorizationCodeAuthenticationToken 를 Authorization 으로 활용하는 필터가 존재하지만,
고급 기능의 필터라서 제가 미처 구현을 못한줄 알았습니다.
하지만 코드를 자세히 읽어보니, clientRegistration, accessToken 등의 값을 가져오는 자료구조로만 활용하고 있을 뿐이지, Authorization 으로서 활용을 하고 있다는 느낌을 받지 못했습니다.

spring-security 의 이슈 히스토리를 추적하다보니 
[**OAuth2LoginAuthenticationProvider 과 OAuth2AuthorizationCodeAuthenticationProvider**](https://github.com/spring-projects/spring-security/issues/5633) 의 로직이 비슷하다고 리팩토링을 요구하는 이슈가 있더군요.

처음에는 OAuth2LoginAuthentication 과 OAuth2AuthorizationCodeAuthentication 이 별개의 Authentication 으로 구현을 하다가, 막상 구현을 하다보니 이 2개의 Provider 로직이 비슷하다는 것을 깨닫게 되어 OAuth2LoginAuthenticationProvider 로직만을 남기고, OAuth2AuthorizationCodeAuthenticationToken 쪽의 존재감을 지워버린게 아닌가 하는 생각이 들었습니다.

그렇다고 이제와서 OAuth2AuthorizationCodeAuthenticationToken 를 Authentication 이 아니도록 다시 리팩토링을 하자니 귀찮아져서 그냥 남겨두었다는 생각도 들었고요.