[Spring] Google에게 지속적으로 리프레시 토큰 발급받기

Rupee·2023년 4월 30일
1
post-thumbnail

문제 상황

data class GoogleAccessTokenDto(
    @JsonProperty("access_token") val accessToken: String,
    @JsonProperty("id_token") val idToken: String,
    @JsonProperty("token_type") val tokenType: String,
    @JsonProperty("expires_in") val expiresIn: Int,
    @JsonProperty("scope") val scope: String,
    @JsonProperty("refresh_token") val refreshToken: String // null
)

구글 소셜 로그인 구현중에, 분명히 처음 요청에는 null 에러가 뜨지 않다가 로그인 재요청을 했더니 refreshToken 에 값이 없다며 널포인터 예외가 터져버렸다. 당연히 카카오처럼 매번 리프레시 토큰을 보내줄 것이라 생각하고 ? 을 사용해 널 타입으로 지정해주지 않았기 때문이다.

그렇다면 왜 구글에서 갑자기 리프레시 토큰을 보내주지 않는거지?

모든 답은 공식 문서에 있다 했으므로, 놓쳤던 부분이 있었는지 다시 찾아보았다.

☁️ 원인

구글 소셜 로그인 문서를 보면 갱신 토큰 분실 시, 동의 흐름을 처음부터 반복해야 한다고 한다. 기존에는 맨 처음에만 정보 이용 동의 화면이 나오고 그 이후 부터는 아이디 + 비밀번호만 입력해도 자동으로 로그인에 성공했는데, 다시 로그인을 했을 때 동의 화면을 띄우는 아예 초기로 돌아가야 하는 것이다.

하지만 생각해보면 따로 옵션을 설정해준게 없었는데, 동의 화면을 최초 1회 제외하고 표시하지 않는 옵션이 자동으로 설정되어있었나? 생각이 들어 다시 요청 파라미터들을 살펴보았고 prompt 를 발견했다.

prompt option

prompt 는 사용자 동의 화면에 관련된 옵션인데, 기본값이 none 이기 때문에 프로젝트에서 처음 액세스를 요청할 때만 동의 메시지가 표시되었던 것이다.

☁️ 해결 방안

로그인 할 때마다 사용자에게 동의를 요청하는 consent 를 활용하여, 프론트에서 초기 권한 코드 요청을 할 때 prompt=consent 값을 설정해서 요청 보내면 된다. 그렇게 되면, 혹여나 리프레시 토큰을 분실하더라도 매번 재발급을 받을 수 있다.

설정 정보 분리

하지만 사용자 관점 측면에서 생각하면 로그인 시마다 매번 동의를 클릭해줘야 하여 매우 불편해지는 문제가 발생한다.

사실 우리 서비스에서는 최초 가입시에만 사용자 정보 API가 필요하고, 이후부터는 자체 토큰을 통해 인증을 유지하기 때문에 구글에게 받은 리프레시 토큰을 사용해야 하는 상황을 찾지 못했다. 따라서, null을 받을 수 있게끔 바꾸어주었다.

@JsonProperty("refresh_token") val refreshToken: String?

그렇지만, 만약 지속적으로 구글 API를 사용할 일이 있다면 실제 서비스가 되고 있는 운영 환경의 플랫폼과 개발 환경에서의 플랫폼을 따로 분리해서 아래와 같이 설정해서 액세스와 리프레시 토큰을 관리하면 되겠다.

  1. 운영 환경 : access_type=offline
  2. 개발 환경 : access_type=offline + prompt=consent

참고 자료
구글 소셜 로그인 공식 문서

profile
개인용으로 공부하는 공간입니다. 잘못된 부분은 피드백 부탁드립니다!

0개의 댓글