LoginSignup
2
3

More than 5 years have passed since last update.

Spring Security 5のOAuth 2.0 LoginでGitHubを利用したときにemailがnullになる問題をなんとかする

Posted at

Spring Security 5のOAuth 2.0 Login

ものすごく大雑把に言ってしまえば、Spring Security 5のOAuth 2.0 Loginは以下だけで外部IdP(下記はGitHub)と連携してログインを実装できます。

####################################################################################################
# spring
####################################################################################################
spring:
  security.oauth2.client.registration:
    github:
      client-id: xxxxx
      client-secret: xxxxx

で、そのときにユーザの情報も取得できるのですが、GoogleとGitHubアカウントでのログインの実装をしたところ、GitHubだけメールアドレスが取得できないという問題が起きました。

なぜ?

CommonOAuth2Providerを見ると、GitHubの場合はhttps://api.github.com/userのAPIを実行することでアカウント情報を取得しているのですが、このエンドポイントではGitHubでpublicになっているemailだけが取得できます。

なので、単純にログインしてOAuth2AuthenticationTokenからemailを取得しようとすると、アカウントがメールアドレスを公開していない場合、emailの値がnullになります。

参考:CommonOAuth2Provider

どうするか

GitHubにはhttps://api.github.com/user/emailsというprivateなemailも取得できるAPIがあります。

なので、以下の流れでGitHubからメールアドレスを取得してみました。

  1. Spring Security 5のOAuth 2.0 Loginでログインし、Authenticationを取得
  2. Authenticationからアクセストークンを取得
  3. トークンをRestTemplateにセットして、https://api.github.com/user/emailsからメールアドレスを取得

参考:
https://stackoverflow.com/questions/35373995/github-user-email-is-null-despite-useremail-scope

実装

application.yml

####################################################################################################
# spring
####################################################################################################
spring:
  security.oauth2.client.registration:
    github:
      client-id: xxxxx
      client-secret: xxxxx
      scope: user:email

追加でscopeとしてuser:emailを指定する必要があります。

Controllerの実装

package work.inabajun.redman.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;


/**
 * Controller for login.
 */
@Controller
@Slf4j
public class LoginController {

    @Autowired
    private OAuth2AuthorizedClientService oAuth2AuthorizedClientService;

    @GetMapping("/")
    @ResponseBody
    public String index(final OAuth2AuthenticationToken authentication) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        // ログイン時に取得した情報を表示(https://api.github.com/user/emails)
        log.info(objectMapper.writeValueAsString(authentication.getPrincipal().getAttributes()));

        OAuth2AuthorizedClient client =
                oAuth2AuthorizedClientService.loadAuthorizedClient(
                        authentication.getAuthorizedClientRegistrationId(),
                        authentication.getName());

        // トークンを取得
        String accessToken = client.getAccessToken().getTokenValue();
        RestTemplate restTemplate = new RestTemplate();

        // RestTemplateにトークンをセット
        restTemplate.getInterceptors()
                .add(getBearerTokenInterceptor(accessToken));

        // メールアドレスを取得(https://api.github.com/user/emails)
        GitHubEmail[] forObject = restTemplate.getForObject("https://api.github.com/user/emails", GitHubEmail[].class);
        log.info(objectMapper.writeValueAsString(forObject));
        return "test";
    }

    private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {
        ClientHttpRequestInterceptor interceptor =
                (request, bytes, execution) -> {
                    request.getHeaders().add("Authorization", "Bearer " + accessToken);
                    return execution.execute(request, bytes);
                };
        return interceptor;
    }

    /**
     * Response for https://api.github.com/user/emails.
     */
    @Value
    static class GitHubEmail{

        private String email;

        private boolean verified;

        private boolean primary;

        private String visibility;
    }
}


トークンを取得してRestTemplateにセットする流れは以下を参考にさせていただきました。

参考:Using Spring Security 5 to integrate with OAuth 2-secured services such as Facebook and GitHub

まとめ

  • Spring Security 5のOAuth 2.0 LoginはGitHubをIdPに指定するとメールアドレスが取得できない可能性がある
  • 別のprivateなメールアドレスを取得できるAPIがあるのでそちらから別途取得可能
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3