두가지 필터가 순서대로 추가되었을 것이다.
1. OAuth2AuthorizationRequestRedirectFilter
2. OAuth2LoginAuthenticationFilter
다음 두가지 필터에 대해 어떻게 OAuth2 login이 이루어지는지 살펴보자.
그리고 앞서 설정했던 redirection URL설정 이유에 대해 알아보자.
This Filter initiates the authorization code grant flow by redirecting the End-User's user-agent to the Authorization Server's Authorization Endpoint.
It builds the OAuth 2.0 Authorization Request, which is used as the redirect URI to the Authorization Endpoint. The redirect URI will include the client identifier, requested scope(s), state, response type, and a redirection URI which the authorization server will send the user-agent back to once access is granted (or denied) by the End-User (Resource Owner).
By default, this Filter responds to authorization requests at the URI /oauth2/authorization/{registrationId} using the default OAuth2AuthorizationRequestResolver. The URI template variable {registrationId} represents the registration identifier of the client that is used for initiating the OAuth 2.0 Authorization Request.
The default base URI /oauth2/authorization may be overridden via the constructor OAuth2AuthorizationRequestRedirectFilter(ClientRegistrationRepository, String), or alternatively, an OAuth2AuthorizationRequestResolver may be provided to the constructor OAuth2AuthorizationRequestRedirectFilter(OAuth2AuthorizationRequestResolver) to override the resolving of authorization requests.
해당 Filter관련 공식문서 내용이다. 중요한 부분은 볼드체로 칠했다.
즉, Resolver를 통해서 authorization request에 대한 응답을 "oauth2/authorization/{registrationID} URI에서 한다는 소리이다.
앞에 어떠한 경로도 붙으면 작동하지 않으니 유의하자. (prefix 경로 설정정보는 붙어야만함)
이제부터 우리는 oauth2/authorization/google로 접속하게 되면, google 로그인창이 뜬다.
그러면 신기하게도, redirect되는 것을 볼 수 있는데, o/oauth2/v2/auth=?? 와 같은 URI로 redirect 된다.
이유는 무엇일까?
지금까지의 과정을 생각하면서 따라왔다면 예측할 수 있겠지만, @Bean으로 수동으로 등록한 ClientRegistrationRepository 설정정보에 적힌 authorizationURI 정보 때문이다.
authorization을 위한 uri는 실제 o/oauth2/v2/auth=?? 로 redirection된다.
이러한 redirection을 도와주는 것이 OAuth2AuthorizationRequestRedirectFilter 이다.
이제 로그인을 성공시켜 보자.
내가 가장 헤맸던 부분이다.
로그인을 성공하게 되면,
http://localhost:8080/api/users/login/oauth2/code/google?state={비밀}&code={비밀}&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=consent
이러한 곳으로 redirect 되었는데, 나보고 어쩌라고! 자꾸 400이 떴다는 것이 가장 큰 원인이었다.
@Configuration
public class OAuth2LoginConfig {
@Value("${spring.security.oauth2.client.registration.google.client-id}")
String clientId;
@Value("${spring.security.oauth2.client.registration.google.client-secret}")
String secretId;
@Value("${spring.security.oauth2.client.registration.google.redirect-uri}")
String redirectUri;
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(Collections.singletonList(this.googleClientRegistration()));
}
private ClientRegistration googleClientRegistration() {
return ClientRegistration.withRegistrationId("google")
.clientId(clientId)
.clientSecret(secretId)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri(redirectUri)
.scope("profile","email")
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
.tokenUri("https://www.googleapis.com/oauth2/v4/token")
.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
.userNameAttributeName(IdTokenClaimNames.SUB)
.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
.clientName("Google")
.build();
}
}
자 여기서 문제가 되는 부분이 어디었을까?
redirectUri
한참을 헤맸었다. 앞선 redirect URI를 보면 /api 라는 prefix 경로 다음에 /users가 보이는 것을 확인할 수 있다.
나는 해당 uri를 통해 구글에서 제공해주는 개인정보들을 controller를 통해 getMapping으로 처리하려고 했었는데, 이것이 화근이었다.
그렇다면, 이유가 무엇이었을까.
바로, 지금 말하고자 하는 OAuth2LoginAuthenticationFilter 때문이다.
일단, 공식 설명 투척
An implementation of an AbstractAuthenticationProcessingFilter for OAuth 2.0 Login.
This authentication Filter handles the processing of an OAuth 2.0 Authorization Response for the authorization code grant flow and delegates an OAuth2LoginAuthenticationToken to the AuthenticationManager to log in the End-User.
The OAuth 2.0 Authorization Response is processed as follows:
Assuming the End-User (Resource Owner) has granted access to the Client, the Authorization Server will append the code and state parameters to the redirect_uri (provided in the Authorization Request) and redirect the End-User's user-agent back to this Filter (the Client).
This Filter will then create an OAuth2LoginAuthenticationToken with the code received and delegate it to the AuthenticationManager to authenticate.
Upon a successful authentication, an OAuth2AuthenticationToken is created (representing the End-User Principal) and associated to the Authorized Client using the OAuth2AuthorizedClientRepository.
Finally, the OAuth2AuthenticationToken is returned and ultimately stored in the SecurityContextRepository to complete the authentication processing.
그리고, 코드를 뜯어보면,
/login/oauth2/code/google 로 URI요청이 들어와야만 해당 filte가 작동하여, 정보들을 건내주게 되어있다.
이제는, redirect uri에서 /users 를 빼면 로그인은 되는 것 처럼 보인다.
그렇다면 로그인을 하고 난 후의 정보들을 받아와서, 회원가입을 하든지, 로그인을 하든지 추가 작업이 필요할 것인데, 다음 파트에서 알아보자.