Criando uma rota de API em Java para login usando oAuth do GitHub

Ao programar o projeto FitLogr, uma aplicação web de registro de treinos de musculação, decidi por usar como método principal de login a autenticação via protocolo oAuth 2.0 na API.

OAuth é um padrão de autorização amplamente utilizado na autenticação de aplicativos e serviços online. Projetado para conceder acesso seguro a recursos protegidos sem revelar credenciais sensíveis, o OAuth permite que um usuário forneça permissão a um aplicativo de terceiros para acessar seus dados sem compartilhar diretamente sua senha.

Já que a API do FitLogr utiliza o framework Spring, uma das maneiras que tentei implementar o recurso foi usando o Spring Security, que possui suporte a oAuth 2.0 com poucos passos de configuração.

No entanto, isso não foi possível devido por precisar do front end e back end unificados dentro da aplicação em Spring. Para poder implementar o oAuth no projeto, necessitava de uma solução que realizasse o fluxo de autenticação de forma separada.

A solução

A ideia é o front end lidar com a parte interativa do fluxo, como a tela de autorização no provedor oAuth, e depois de autorizado transferir as informações para a API lidar com o login na aplicação. No caso do FitLogr, o login usa a conta do usuário no GitHub.

Fluxo de autenticação utilizando o protocolo oAuth em uma API
Fluxo de autenticação usando o GitHub como provedor de oAuth no projeto FitLogr

Ao clicar no botão de login, o usuário é redirecionado para o GitHub. Caso não tenha sido autorizado ainda, o navegador exibe um prompt da plataforma requisitando a autorização para acessar as informações.

Em seguida, o GitHub redireciona o usuário para uma rota no front end e fornece um código temporário de autenticação. A partir desta rota, a aplicação faz uma requisição à API fornecendo o código.

A API da aplicação usa esse código para se comunicar ao servidor oAuth do GitHub e trocá-lo por um token de acesso, usado para acessar as informações do respectivo usuário na API do GitHub.

A API do GitHub devolve à API da aplicação as informações do usuário e, enfim, depois de consulta ao banco de dados, devolve um token JWT ao front-end finalizando o procedimento de login.

Como obter as informações do usuário

Vamos primeiro criar uma classe chamada OAuthUtil, que será responsável por pegar as informações do GitHub. Ela deverá ter acesso ao Client ID e o Client Secret da aplicação oAuth conforme cadastrado na plataforma. Aqui, usamos variáveis de ambientes pela anotação @Value do Spring Boot.

// Imports ommited

public class OAuthUtil {

   @Value("${clientid}")
   private String clientId;
		
   @Value("${clientsecret}")
   private String clientSecret;
	
   public OAuthUserDTO getOAuthData(OAuthCodeDTO codeDTO){
      String tokenUrl = "https://github.com/login/oauth/access_token";
      String requestUrl = tokenUrl + "?client_id=" + clientId + "&client_secret=" + clientSecret + "&code=" + codeDTO.code();
      var requestToken = new RestTemplate().postForEntity(requestUrl, null, String.class);
      var accessToken = "";
	     
      String[] parts = requestToken.getBody().split("&");
      for (String part : parts) {
         if (part.startsWith("access_token=")) {
            accessToken = part.substring("access_token=".length());
         }
      }
	        
      String userUrl = "https://api.github.com/user";
      var headers = new HttpHeaders();
      headers.set("Authorization", "Bearer " + accessToken);

      try {
         HttpEntity<String> requestEntity = new HttpEntity<>(headers);
         var response = new RestTemplate().exchange(userUrl, HttpMethod.GET, requestEntity, OAuthUserDTO.class);
         return response.getBody();
      } catch (RestClientException e) {
         throw new RuntimeException("Unauthorized access to GitHub: ", e);
      }
   }
}

O método getOAuthData recebe da rota da API (que vamos ver adiante) o código de autenticação do usuário do GitHub. Em seguida, ela faz uma requisição ao servidor oAuth do GitHub para trocar o código de autenticação por um token de acesso.

O token de acesso, por sua vez, é usado no cabeçalho de uma requisição à API do GitHub para acessar as informações do respectivo usuário. A função retornará as informações padronizadas em um Data Transfer Object (DTO).

Lidando com o login na API

Com a troca de informações entre a API e o GitHub programados, podemos focar na rota de login do usuário. Aqui temos a classe AuthController, que será responsável pelas rotas de login.

Mais uma vez, estamos usando anotações do Spring Boot. A rota /auth/oauth da aplicação receberá requisições POST. E terá no corpo de requisição um objeto JSON com um código gerado pelo GitHub ao usuário autorizar o acesso à aplicação.

// Imports ommited

@RestController
@RequestMapping("/auth")
public class AuthController {

   @Autowired
   UserRepository userRepository;
	
   @Autowired
   OAuthUtil oAuthUtil;
	
   @PostMapping("/oauth")
   public ResponseEntity<TokenDTO> oAuthLogin(@RequestBody @Valid OAuthCodeDTO codeDTO) {
		
      var oAuthUser = oAuthUtil.getOAuthData(codeDTO);

      var oauthUserId = oAuthUser.id();
      Optional<User> checkUser = userRepository.findByOauthId(oauthUserId);

      if(checkUser.isEmpty()) {
         var newUser = new User();
         newUser.setOauthId(oAuthUser.id());
         newUser.setName(oAuthUser.name());
         newUser.setAvatarUrl(oAuthUser.avatar_url());
         newUser.setLogin(oAuthUser.login());
         newUser.setJoinedAt(LocalDateTime.now(Clock.systemUTC()));
         userRepository.save(newUser);
         checkUser = userRepository.findByOauthId(oauthUserId);
      }

      // Authenticate user and generate token code

      var response = new TokenDTO(token);

      return ResponseEntity.status(HttpStatus.OK).body(response);
   }
}	

Este código de acesso será o parâmetro do método oAuthLogin via DTO. Primeiramente, ele faz a requisição ao provedor oAuth pelo método que criamos anteriormente. E dele extrai o ID do usuário na plataforma.

Em seguida, ele faz uma requisição à tabela de usuários no banco de dados, para verificar se há algum usuário com o número de identificação única do GitHub.

Se não houver, é porque se trata de um novo usuário que autorizou o acesso à aplicação. Então, precisamos registrá-lo antes com as informações obtidas. Do contrário, apenas realiza o fluxo de autenticação. No caso, a rota retorna ao front end um token JWT.

Neste artigo omitimos importações e linhas desnecessárias como de criação do token da aplicação e de autenticação do Spring Security. A utilização deste código no contexto do FitLogr está disponível no repositório da API no GitHub.

Avatar de Matheus Misumoto

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.