Spring で Google ログインURLを作る方法

# Google Login Urlを作る方法
## 前提
有名な Social Login Provider は Spring によって管理されているため元々は Login URL を作るとかの作業はいらないです。
ですが、今回作るアプリケーションではフロントエンドでは `client_id` も `client_secret` も何も管理していなく全部バックエンドで管理しています。

## 要件
- ユーザが `/a`, `/abc`どちらにいてもログインボタンを押してログインモーダルが表示されてログインしたら、その後、元のページに戻る必要があります。
- 全てバックエンドで制御していますので `redirect` で元のページに戻す必要があります。

## 実装

フロントエンドから currentUr を貰って state にセットします。

```kotlin
package jp.co.kopher.backendkotlin.controller

import jakarta.servlet.http.HttpServletRequest
import jp.co.kopher.backendkotlin.response.AuthLoginResponse
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/v1")
class OAuth2Controller(
    private val clientRegistrationRepository: ClientRegistrationRepository,
) {

    @GetMapping("/login")
    fun login(@RequestParam("google") currentUrl: String, request: HttpServletRequest): AuthLoginResponse {
        val resolver = DefaultOAuth2AuthorizationRequestResolver(
            clientRegistrationRepository,
            DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
        )
        val authRequest = resolver.resolve(request, "google").let {
            OAuth2AuthorizationRequest.from(it)
                .state(currentUrl) // JWTを生成してstateにセットするのをお勧めします。
                .build()
        }

        return AuthLoginResponse(authRequest.authorizationRequestUri)
    }
}
```

上でセットした state の値は google によって call back される時に query parameter としてセットされてきますのでそれを抽出します。

```kotlin
package jp.co.kopher.backendkotlin.controller

import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
import org.springframework.stereotype.Component

@Component
class OAuth2SuccessHandler : AuthenticationSuccessHandler {
    override fun onAuthenticationSuccess(
        request: HttpServletRequest?,
        response: HttpServletResponse?,
        authentication: Authentication?
    ) {
        val currentUri = request?.getParameter("state") ?: "/"
        response?.sendRedirect("http://localhost:3000/$currentUri")
    }
}
```

Google 認証成功後呼ばれる Handler として OAuth2SuccessHandler を登録します。

```kotlin
package jp.co.kopher.backendkotlin.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.AuthenticationSuccessHandler

@Configuration
class SecurityConfig(
    private val oauth2SuccessHandler: AuthenticationSuccessHandler,
) {

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http.oauth2Login {
            it.successHandler(oauth2SuccessHandler)
        }
        return http.build()
    }
}
```

これで終わりです！