package com.github.dhaval_mehta.savetogoogledrive.controller.rest.oauth; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; import com.github.dhaval_mehta.savetogoogledrive.model.Token; import com.github.dhaval_mehta.savetogoogledrive.model.User; import com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.ResponseHeader; /** * @author Dhaval */ @RestController @RequestMapping("/api/oauth/google") @Api(description = "handles Google OAuth.", produces = "application/json", consumes = "application/json") public class GoogleOauthController { private final static String PROFILE_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; private final static String TOKEN_URL = "https://www.googleapis.com/oauth2/v4/token"; private final static String OAUTH_URL = "https://accounts.google.com/o/oauth2/auth"; private final static String ACCESS_TYPE = "offline"; private final static String RESPONSE_TYPE = "code"; private final static String SCOPE = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"; private final static String GRANT_TYPE = "authorization_code"; private final static String CLIENT_ID = System.getenv("client_id"); private final static String CLIENT_SECRET = System.getenv("client_secret"); private final static String REDIRECT_URI = System.getenv("redirect_uri"); private final static Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); private final HttpSession session; @Autowired public GoogleOauthController(HttpSession session) { this.session = session; } @GetMapping(value = "/redirect") @ResponseStatus(HttpStatus.FOUND) @ApiOperation(value = "redirect client for oauth") @ApiResponses(@ApiResponse(code = 302, message = "server successfully redirected client for oauth", responseHeaders = @ResponseHeader(name = "Location", response = java.net.URL.class))) public void redirectForOauth2(HttpServletResponse response) throws URISyntaxException, IOException { String redirectUrl = new URIBuilder(OAUTH_URL).addParameter("scope", SCOPE) .addParameter("access_type", ACCESS_TYPE).addParameter("redirect_uri", REDIRECT_URI) .addParameter("response_type", RESPONSE_TYPE).addParameter("client_id", CLIENT_ID).build().toString(); response.sendRedirect(redirectUrl); } @GetMapping("/callback") @ResponseStatus(HttpStatus.FOUND) @ApiOperation(value = "handles oauth callback from google", hidden = true) @ApiResponses(@ApiResponse(code = 302, message = "redirect client for session check", responseHeaders = @ResponseHeader(name = "Location", response = java.net.URL.class))) public void handleCallback(@RequestParam("code") String code, HttpServletResponse response) throws IOException, URISyntaxException { Token token = getAccessToken(code); User user = getUser(token); if (user != null) { session.setAttribute("user", user); response.sendRedirect("/"); } else { response.setHeader("Location", ""); response.sendRedirect("/"); } } private Token getAccessToken(@NotNull String code) throws IOException { // Initialize client HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(TOKEN_URL); // add request parameters List<NameValuePair> parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair("code", code)); parameters.add(new BasicNameValuePair("client_id", CLIENT_ID)); parameters.add(new BasicNameValuePair("client_secret", CLIENT_SECRET)); parameters.add(new BasicNameValuePair("redirect_uri", REDIRECT_URI)); parameters.add(new BasicNameValuePair("grant_type", GRANT_TYPE)); httpPost.setEntity(new UrlEncodedFormEntity(parameters)); // send request org.apache.http.HttpResponse response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); InputStream inputStream = response.getEntity().getContent(); if (HttpUtilities.success(statusCode)) return gson.fromJson(new InputStreamReader(inputStream), Token.class); throw new ApiException(HttpStatus.valueOf(statusCode)); } private User getUser(@NotNull Token token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(PROFILE_URL); builder.addParameter("access_token", token.getAccessToken()); HttpClient httpClient = HttpClientBuilder.create().build(); HttpGet httpGet = new HttpGet(builder.build()); org.apache.http.HttpResponse response = httpClient.execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); InputStream inputStream = response.getEntity().getContent(); if (HttpUtilities.success(statusCode)) { User user = gson.fromJson(new InputStreamReader(inputStream), User.class); user.setToken(token); return user; } throw new ApiException(HttpStatus.valueOf(statusCode)); } }