IT/Spring

OAuth 2.0: Google OAuth2 SSO

최고영회 2023. 7. 7. 17:04
728x90
반응형
SMALL

Google OAuth2 연동 방법

OAuth 2.0

OAuth 2.0은 권한 부여를 위한 개방형 표준 인증 프로토콜이다.

리소스를 소유하고 있는 사용자 대신 Application에 리소스에 접근할 수 있는 권한을 위임한다.

Flow

출처: guide.ncloud-docs.com

 

 

Google OAuth2 설정

상단의 '콘솔' 클릭

API 및 서비스 클릭

OAuth 동의 화면 클릭

- OAuth 동의 화면: 앱이름, 사용자 지원 이메일, 개발자 연락처 정보 등 필요한 정보 입력 > 저장 후 계속

- 범위: 테스트를 위해 이메일 주소, 개인정보 보기 2개 선택 > 저장 후 계속

테스트 사용자

- ADD USERS > 테스트 할 본인 이메일 주속 입력 > 저장 후 계속

사용자 인증 정보 클릭

- 상단의 + 사용자 인증 정보 만들기 클릭 > OAuth 클라이언트 ID 클릭 > 애플리케이션 유형 선택 (ex. 웹 애플리케이션) & 이름 입력 (자유) & 승인된 리디렉션 URI 추가 (사용자가 Google 에서 인증 받은 후 이 경로로 리디렉션됨, ex) http://localhost:8082/login/oauth2/code/google) > 만들기 버튼 클릭 > OAuth 클라이언트 생성됨 (클라이언트 ID 와 보안 비밀번호 를 Copy 또는 JSON 다운로드하여 보관)

Application 개발

Spring Boot > dependency 에 Security > OAuth2 Client 선택, Web > Spring Web 선택, Developer Tools > Lombok 선택

application.yaml 설정

spring: 
  profiles: 
    active: dev 
server: 
  port: 8082 

oauth2: 
  google: 
    client-id: Google OAuth 설정에서 발급받은 클라이언트 ID 입력 
    client-secret: Google OAuth 설정에서 발급받은 클라이언트 비밀번호 입력
    redirect-uri: Google OAuth 설정에서 리디렉트 URI 입력 
    token-uri: https://oauth2.googleapis.com/token 
    resource-uri: https://www.googleapis.com/oauth2/v2/userinfo 
  kakao: 
    client-id: 
    client-secret: 
    redirect-uri: 
    token-uri: 
    resource-uri: 
  naver: 
    client-id: 
    client-secret: 
    redirect-uri: 
    token-uri: 
    resource-uri:
 

 

Test Controller 생성

@RestController 
@RequestMapping(value = "/login/oauth2", produces = "application/json") 
@RequiredArgsConstructor @Slf4j 
public class LoginCtrl { 
  final LoginService loginService; 
  
  @GetMapping("/code/{company}") 
  public void googleLogin(@RequestParam String code, @PathVariable String company) { 
    loginService.googleLogin(code, company); 
  } 
}
 

 

Test Service 생성

 
@Service @Slf4j @RequiredArgsConstructor 
public class LoginService { 
  private final Environment env; 
  private final RestTemplate restTemplate; 
  
  public void googleLogin(String code, String company) { 
    log.info("where: {}, code: {}", company, code); 
    String accessToken = getAccessToken(code, registrationId); 
    log.info("access token: {}", accessToken); 
    
    JsonNode userResourceNode = getUserInfo(accessToken, registrationId); 
    if (userResourceNode == null) { 
      log.error("Can not get user resource with access-token: {}", accessToken); 
      return; 
    } 
    
    log.info("userResourceNode: {}", userResourceNode); 
  } 
  
  private String getAccessToken(String authorizationCode, String company) { 
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); 
    params.add("code", authorizationCode); 
    params.add("client_id", env.getProperty("oauth2." + registrationId + ".client-id")); 
    params.add("client_secret", env.getProperty("oauth2." + registrationId + ".client-secret")); 
    params.add("redirect_uri", env.getProperty("oauth2." + registrationId + ".redirect-uri")); 
    params.add("grant_type", "authorization_code"); 
    
    HttpHeaders headers = new HttpHeaders(); 
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 
    HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers); 
    assert tokenUri != null; 
    ResponseEntity<JsonNode> responseNode = restTemplate.exchange(tokenUri, HttpMethod.POST, entity, JsonNode.class); 
    JsonNode accessTokenNode = responseNode.getBody(); 
    return accessTokenNode.get("access_token").asText(); 
  } 
  
  private JsonNode getUserInfo(String accessToken, String registrationId) { 
    HttpHeaders headers = new HttpHeaders(); 
    headers.set("Authorization", "Bearer " + accessToken); 
    HttpEntity<JsonNode> request = new HttpEntity<>(headers); 
    return restTemplate.exchange(Objects.requireNonNull(env.getProperty("oauth2." + registrationId + ".resource-uri")), HttpMethod.GET, request, JsonNode.class).getBody(); 
  }
}

Application run 하고

Test

Browser 에서

아래와 같이 URL 호출 - {clientId}, {redirectUri} 는 위에서 발급받은 정보로 치환 - 하면

https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={redirectUri}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile

이렇게 google 인증 화면이 나오고 로그인을 정상적으로 하면

위에서 설정한 redirect-uri 가 호출된다. 즉 내가 만든 application 으로 authorizationCode 가 전달된다.

authorizationCode 와 초기에 발급받은 클라이언트 ID, 클라이언트 패스워드 를 이용해서

Access token 을 받고 Access token 을 이용해서 원하는 리소스 정보를 get 한다.

로그인한 사용자의 ID, 이름, nick-name, email 등의 공개된 리소스 정보를 얻을 수 있다.

위와 같이 얻어온 정보로 SSO 처리를 하거나 필요한 작업을 수행하면 되는데

처음 authorizationCode 가 유출되거나 탈취될 경우 공격자가 원하는 redirect uri 로 로그인 정보(ID/PW)가 유출될 수 있기 때문에 별도의 보안 작업이 필요하다.

이 부분은 (PKCE) 다음 시간에 이와 관련하여 정리해 보기로 한다.

728x90
반응형
LIST