RestClientGoogleOAuthClient.java
package com.hwhub.backend.infrastructure.oauth.google;
import com.hwhub.backend.config.GoogleOAuthProperties;
import com.hwhub.backend.domain.oauth.google.GoogleOAuthClient;
import com.hwhub.backend.domain.oauth.google.GoogleTokenResponse;
import com.hwhub.backend.domain.oauth.google.GoogleUserInfo;
import com.hwhub.backend.presentation.rest.common.OAuthIdTokenInvalidException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
@Component
@RequiredArgsConstructor
public class RestClientGoogleOAuthClient implements GoogleOAuthClient {
private final GoogleOAuthProperties props;
private final RestClient restClient = RestClient.create();
@Override
public String buildAuthorizationUrl(String state) {
String base = "https://accounts.google.com/o/oauth2/v2/auth";
// 最小スコープ
String scope = "openid email profile";
return base
+ "?client_id="
+ url(props.getClientId())
+ "&redirect_uri="
+ url(props.getRedirectUri())
+ "&response_type=code"
+ "&scope="
+ url(scope)
+ "&state="
+ url(state)
+ "&prompt=select_account";
}
@Override
public GoogleTokenResponse exchangeCodeForToken(String code) {
Map<String, String> form =
Map.of(
"code",
code,
"client_id",
props.getClientId(),
"client_secret",
props.getClientSecret(),
"redirect_uri",
props.getRedirectUri(),
"grant_type",
"authorization_code");
return restClient
.post()
.uri("https://oauth2.googleapis.com/token")
.contentType(Objects.requireNonNull(MediaType.APPLICATION_FORM_URLENCODED))
.body(Objects.requireNonNull(toFormBody(form)))
.retrieve()
.body(GoogleTokenResponse.class);
}
@Override
public GoogleUserInfo fetchUserInfo(String accessToken) {
return restClient
.get()
.uri("https://www.googleapis.com/oauth2/v3/userinfo")
.header("Authorization", "Bearer " + accessToken)
.retrieve()
.body(GoogleUserInfo.class);
}
@Override
public GoogleUserInfo verifyIdToken(String idToken) {
try {
return restClient
.get()
.uri("https://oauth2.googleapis.com/tokeninfo?id_token=" + url(idToken))
.retrieve()
.body(GoogleUserInfo.class);
} catch (RestClientException e) {
throw new OAuthIdTokenInvalidException();
}
}
private String toFormBody(Map<String, String> form) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (var e : form.entrySet()) {
if (!first) sb.append("&");
first = false;
sb.append(url(e.getKey())).append("=").append(url(e.getValue()));
}
return sb.toString();
}
private String url(String s) {
return URLEncoder.encode(s, StandardCharsets.UTF_8);
}
}