[Spring Boot] Google OAuth2 Login
Spring BootGoogle Cloud Console
If there is no project, create a project.
Set the OAuth consent screen
API & Services > OAuth consent Screen
- User Type: Select External because login is used by all users.
- Add Scope: Add API to use.Select userinfo.email, userinfo, profile, and openid because you only use login.
- Test Users: Register by entering an email to test others while in test mode
- Completion
Creating an OAuth 2.0 Client ID
APIs & Services > Credentials
- Create Credentials > OAuth Client ID
- Register URI: Register URI to receive Google login result code.
- Verify the generated client ID and client security password
API documentation material
Call Google Login Window
Spring Boot Code
- application.yml
google:
client:
id: "Client ID"
secret: "Client secret"
auth:
url: "https://oauth2.googleapis.com"
redirect-url: "Approved redirect URI registered with OAuth client ID"
- Provider
@Component
class GoogleApiProvider(
@Value("\${google.client.id}") private val clientId: String,
@Value("\${google.client.secret}") private val clientSecret: String,
@Value("\${google.auth.url}") private val authUrl: String,
@Value("\${google.auth.redirect-url}") private val redirectUrl: String
) {
/**
* Google login screen URI
*/
fun getGoogleAuthUrl(): String {
return "https://accounts.google.com/o/oauth2/v2/auth?" +
"scope=${URLEncoder.encode("openid email profile", Charsets.UTF_8)}" +
"&access_type=offline" +
"&response_type=code" +
"&redirect_uri=$redirectUrl" +
"&client_id=$clientId"
}
}
- Controller
@RestController
@RequestMapping("/api/auth")
class AuthController(
private val googleApiProvider: GoogleApiProvider
) {
/**
* Call Google Login Window
*/
@GetMapping("/login/google-auth")
fun redirectGoogleAuth(request: HttpServletRequest): ResponseEntity<Any> {
val headers = HttpHeaders()
headers.location = URI.create(googleApiProvider.getGoogleAuthUrl())
return ResponseEntity(headers, HttpStatus.MOVED_PERMANENTLY)
}
}
Testing
- Search for http://localhost:8080/api/auth/login/google-auth in the browser address bar or create a button on the front screen to run it
- When you log in, you will see the cscreen.If you press Continue here, it goes to the redirect_uri sent as a parameter when you call the Google login window.
Redirect Processing / Login Processing
Spring Boot Code
- Receive Token Response DTO
data class GoogleTokenResponseDTO(
@SerializedName("access_token")
val accessToken: String,
@SerializedName("expires_in")
val expiresIn: String,
@SerializedName("refresh_token")
val refreshToken: String,
val scope: String,
@SerializedName("token_type")
val tokenType: String
)
- User Information Response DTO
data class GoogleUserInfoResponseDTO(
val sub: String,
val name: String,
@SerializedName("given_name")
val givenName: String,
val picture: String,
val email: String,
@SerializedName("email_verified")
val emailVerified: String,
val locale: String
)
- Provider
@Component
class GoogleApiProvider {
// ...
/**
* Receive tokens
*/
fun getToken(code: String): GoogleTokenResponseDTO {
val url = "$authUrl/token"
val param = LinkedMultiValueMap<String, String>()
param["code"] = code
param["client_id"] = clientId
param["client_secret"] = clientSecret
param["redirect_uri"] = redirectUrl
param["grant_type"] = "authorization_code"
val response = RestTemplate().postForObject(url, param, String::class.java)
?: throw GoogleApiException("token")
return Gson().fromJson(response, GoogleTokenResponseDTO::class.java)
}
/**
* Get user information
*/
fun getUserInfo(accessToken: String): GoogleUserInfoResponseDTO {
val url = "https://openidconnect.googleapis.com/v1/userinfo?access_token=$accessToken"
val headers = HttpHeaders()
headers.setBearerAuth(accessToken)
val request = HttpEntity<MultiValueMap<String, String>>(LinkedMultiValueMap(), headers)
val response = RestTemplate().exchange(url, HttpMethod.POST, request, String::class.java)
return if (response.statusCode == HttpStatus.OK)
Gson().fromJson(response.body, GoogleUserInfoResponseDTO::class.java)
else throw GoogleApiException("user info")
}
}
- Controller
@RestController
@RequestMapping("/api/auth")
class AuthController(
private val authService: AuthService,
private val googleApiProvider: GoogleApiProvider
) {
// ...
/**
* Called after logging in to Google. Login Processing
*/
@GetMapping("/login/google")
fun loginGoogle(
request: HttpServletRequest,
@RequestParam code: String?,
@RequestParam error: String?
): ResponseEntity<Any> {
return code?.let {
try {
authService.loginGoogle(code)
ResponseEntity.ok(HttpStatus.OK)
} catch (e: Exception) {
ResponseEntity.ok(HttpStatus.BAD_REQUEST)
}
} ?: ResponseEntity.ok(HttpStatus.BAD_REQUEST)
}
}
- Service
@Service
class AuthService(
private val googleApiProvider: GoogleApiProvider
) {
fun loginGoogle(code: String) {
// get token
val token = googleApiProvider.getToken(code)
// Look up user information
val info = googleApiProvider.getUserInfo(token.accessToken)
// Process login
}
}
Testing
- If you press Continue on the consent screen above, you will be logged in.