본문 바로가기

Study/24-2 GDSC Frontend Session

소셜 로그인 구현하기

*https://brunch.co.kr/@drawhatha/75

개인 정보 처리 방침

- 수집한 개인정보를 어떻게 이용하고 보호할 것인지 규정한 문서.

- 홈페이지 첫 페이지에(주로 footer)에 공개되어 있어야 하며 변경 시, 사용자에게 알려야함

개인 정보 수집 / 이용 동의서

- 회원 가입 시 사용

- 수집 목적, 수집항목, 보유 및 이용 기간, 동의를 거부할 권리가 있다는 사실과  동의를 거부할 경우 불이익 존재(선택) 


*https://velog.io/@hang_kem_0531

기존의 로그인 기능 절차

  1. [프론트엔드] ID와 비밀번호를 준다.
  2. [백엔드] ID와 비밀번호를 검증하고 AccessToken과 RefreshToken, AccessToken의 만료시간을 반환해준다. 이 때 생성한 RefreshToken은 DB에 {ID,RefreshToken}으로 저장한다.
  3. [프론트엔드] 반환받은 AccessToken을 매 api 호출마다 헤더에 붙여서 전송한다.
  4. [백엔드] api호출시 헤더의 AccessToken을 확인하고 유효한지, 만료기간이 지났는지를 체크 후 api를 동작시킨다.
  5. [프론트엔드] AccessToken의 만료 기간이 지나거나, 30초 미만으로 남았다면, 백엔드에 RefreshToken을 붙여 Reissue 요청을 보낸다.
  6. [백엔드] Reissue요청이 들어올 경우, RefreshToken이 DB에 있는 것인지 확인한 후, 맞다면 AccessToken과 새로운 AccessToken 만료 시간을 반환한다.
  7. [프론트엔드] Reissue결과 반환된 AccessToken과 만료기간을 저장하여 다음 api호출에 사용한다.

*https://guide.ncloud-docs.com/docs/b2bpls-oauth2

OAuth2.0이란?

 

  OAuth2.0 개념 

Open Authorization 2.0 혹은 OAuth2.0은 웹 및 애플리케이션 인증 및 권한 부여를 위한 개방형 표준 프로토콜(컴퓨터 내부에서, 또는 컴퓨터 사이에서 데이터의 교환 방식을 상호 정의하는 규칙 체계) 입니다. 이 프로토콜에서는 third-party 애플리케이션이 사용자의 리소스에 접근하기 위한 절차를 정의하고 서비스 제공자의 API를 사용할 수 있는 권한을 부여합니다.

 

  OAuth2.0 구성요소(역할)

리소스 소유자(Resource Owner): 사용자

: OAuth 2.0 프로토콜을 사용하여 보호되는 리소스에 대한 액세스 권한을 부여하는 사용자(엔티티)입니다. 클라이언트를 인증(Authorize)하는 역할을 수행합니다. 예를 들어 네이버 로그인에서 네이버 아이디를 소유하고 third-party 애플리케이션(클라이언트)에 네이버 아이디로 소셜 로그인 인증을 하는 사용자를 의미합니다.

클라이언트(Client): 내가 만든 애플리케이션

: OAuth 2.0을 사용하여 리소스에 접근하려는 third-party 애플리케이션이나 서비스입니다.

권한 서버(Authorization Server): 사용자 인증을 통해 내가 만든 애플리케이션에게 데이터를 제공받을 권한(토큰)을 제공하는 서버

: 권한 서버는 클라이언트가 리소스 소유자의 권한을 얻을 수 있도록 도와주는 서버입니다. 권한 서버는 사용자 인증, 권한 부여 및 토큰 발급을 관리합니다.

리소스 서버(Resource Server): 데이터를 직접적으로 제공하는 서버

: 리소스 서버는 보호되는 리소스를 호스팅하는 서버로, 액세스를 허용하거나 거부합니다. 이 서버는 OAuth 2.0 토큰을 사용하여 클라이언트에게 리소스에 액세스할 권한을 부여하고 실제 데이터를 제공합니다

 

  OAuth2.0 용어

액세스 토큰(Access Token): 권한 서버가 내가 만든 애플리케이션에 리소스 서버에서 데이터를 받아오도록 허락한 증명서

: 클라이언트가 리소스 서버의 리소스에 접근하기 위한 권한을 부여받는 토큰입니다. 액세스 토큰은 권한 서버로부터 발급되며, 일반적으로 제한된 유효 기간을 가지고 있습니다.

리프레시 토큰(Refresh Token): 액세스 토큰의 유효기간이 만료되었을 때,  새로운 액세스 토큰을 간편하게 받아오도록 함

: 리프레시 토큰은 액세스 토큰의 유효 기간이 만료된 후 새로운 액세스 토큰을 받기 위한 토큰입니다. 이를 통해 사용자는 다시 로그인할 필요 없이 토큰 유효 시간 갱신만으로 계속 애플리케이션을 사용할 수 있습니다.

범위(Scope): 내가 제공받을 데이터의 범위. 권한 서버에 의해 엑세스 토큰에 명시

: 범위는 클라이언트가 리소스에 대한 어떤 작업을 수행할 수 있는지를 정의하는 문자열입니다. 범위는 권한 서버에 의해 정의되며, 클라이언트는 특정 범위의 액세스 권한을 요청할 수 있습니다.

인증 코드(Authorization Code): 사용자가 권한 서버에게 인증(주로 로그인을 통해 이루어짐)을 받을 때 사용하는 코드

: 인증 코드는 클라이언트가 액세스 토큰을 얻기 위한 중간 단계로 사용되는 코드입니다. 인증 코드 부여(Authorization Code Grant) 방식을 통해 권한 서버로부터 발급되며, 이를 사용하여 액세스 토큰과 리프레시 토큰을 얻을 수 있습니다.


    OAuth2.0 권한 부여 과정 & 프론트엔드가 해야할 것

 

  1. 인증 코드 요청 
    • 클라이언트가 사전에 권한 서버로부터 발급한 클라이언트 ID, redirect_uri 정보와 함께 response_type을 code로 지정하여 요청합니다.
    • 클라이언트 ID, redirect_uri 정보와 함께 response_type을 포함한 인증 요청 URL 생성
    • 유저가 인증을 시작하도록 이 URL로 리디렉션하는 버튼(이벤트) 구현
  2. 로그인
    • 권한 서버에서 로그인 페이지를 제공하고 리소스 소유자가 로그인 합니다.
  3. 인증 코드 전달
    • 로그인에 성공하면 권한 서버는 전달받은 redirect_uri로 인증 코드를 전달합니다.
    • redirect_uri로 리디렉션된 URL에서 인증 코드 파라미터를 추출하여 백엔드로 전달
  4. 엑세스 토큰 발급
    • 클라이언트가 권한 부여 승인 코드를 통해 엑세스 토큰 (및 리프레시 토큰)을 발급받습니다.
    • 이때 사전에 권한 서버로부터 발급한 클라이언트 ID와 클라이언트 시크릿 (Client Secret) 정보가 필요합니다
  5. 백엔드에서 발급받은 엑세스 토큰을 활용하여, 소셜 플랫폼의 API에서 사용자 정보를 가져오기

 

[예시]

//1
const authUrl = `https://authserver.com/oauth/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}`;
window.location.href = authUrl; // 인증 요청을 보냄

//3
const urlParams = new URLSearchParams(window.location.search);
const authCode = urlParams.get('code'); // 인증 코드 추출
if (authCode) {
  // 인증 코드가 있다면 백엔드로 전달
  sendAuthCodeToBackend(authCode);
}
async function sendAuthCodeToBackend(authCode) {
  const response = await fetch('/api/auth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code: authCode })
  });
  const data = await response.json();
  if (data.accessToken) {
    saveAccessToken(data.accessToken); // 토큰 저장
  }
}

//5
async function fetchUserProfile() {
  const response = await fetch('/api/user/profile');
  const profile = await response.json();
  displayUserProfile(profile);
}

function displayUserProfile(profile) {
  document.getElementById('user-name').innerText = profile.name;
  document.getElementById('user-email').innerText = profile.email;
}