본문 바로가기
IT/Spring

OAuth 2.0: Google OAuth2 SSO

by 최고영회 2023. 7. 7.
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