카카오 로그인 연동을 통한 OAuth 2.0 이해하기

호두파파·2021년 10월 30일
2

프로젝트

목록 보기
2/3

OAuth란?

OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다.

사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수있게 해주는 HTTP 기반의 보안 프로토콜

'서비스 제공자를 대신해 제 3자가 어떤 정보를 사용하도록 접근을 허용하시겠습니까?'

OAuth에서 Auth는 Authentication(인증) & Authorization(허가) 2가지 의미를 포함하고 있다. 위 제목에서 제 3자는 이용할 서비스를 말한다. 이 물음에 동의한다는 것은 먼저 서비스 제공자에서 자신을 인증하고 제 3자에게 자신의 정볼르 사용하도록 접근 권한을 부여하는 것이다.

사용자 입장에서는 여러 서비스들을 하나의 계정으로 관리할 수 있게ㅔ되어 편해지고 개발자 입장에서는 민감한 사용자 정보를 다루지 않아 위험부담이 줄고 서비스 제공자로부터 사용자 정보를 활용할 수 있다.

OAuth2 토큰

Access Token

  • Client가 Resource Server에게 사용자 정보를 요청하기 위한 입장권.
  • 이 입장권에는 유효기간이 있다.
  • 유효기간이 지나면 더 이상 이 코튼을 사용할 수 없다.

Refresh Token

  • 위 Access Token이 유효기간이 만료되면, 새로운 Access Token을 발급받기 위해 필요한 토큰이다.
  • 이 토큰에도 유효기간이 있다. 단, Access Token 보다는 유효기간이 훨씬 길다

카카오 SNS 로그인 원리

카카오 로그인 연동을 하기 위해서는 서비스에 가입된 상태여야 한다.
서비스 제공자로부터 사용자 인증 및 허가를 받는다.
인증자의 정보를 받아 가입한 계정에 저장을 하면, 연동이 이루어진다.
연동된 계정으로 로그인 시도 시 서비스 제공자로부터 재확인이 완료되면,
소셜로그인이 이루어진다.

출처: 카카오 디벨로퍼스 문서 카카오 로그인 이해하기

카카오 SNS 로그아웃

카카오 로그인의 로그아웃은 사용자가 카카오 로그인을 통해 발급받은 토큰을 만료시켜, 로그아웃을 요청한 서비스에서 해당 사용자 정보로 카카오 API를 호출시킬 수 없게한다.


카카오 디벨로퍼스에서 카카오 로그인 설정하기

카카오 공식 문서를 참조하기
카카오 디벨로퍼스 카카오 로그인 설정하기


카카오 로그인 구현하기

  1. 클라이언트 서비스에 접속하기
// views/partial/header.ejs
<div>
  <a href="/auth/kakao">카카오 간편 로그인</a>
</div>
  1. 사용자 인증 및 접근 권한 받기
  • 미리 카카오에 로그인되어 있는 경우, 서비스 동의 페이지로 리다이렉트된다.
  • 이때 리다이렉트되는 주소(로그인 로직이 처리되고, 돌아갈 페이지라고 이해하면 쉽다)를 확인하면 auth/kakaoroute에 의해 생성된 주소임을 확인할 수 있다.
// routes/auth/kakao.js
// '간편 로그인' 링크로 접근했을때, '사용자 서비스 동의' 페이지로 리다이렉트한다.
// redirecUri는 인증 성공시, 카카오 개발자 콘솔에 기입한 이동할 주소를 말한다.
router.get('/', (req, res) => {
  const kakaoAuthUrl = `http://kauth.kaka.com/oauth/authorize?client_id=${kaka.clientId}$redirect_uri=${kakao.redirectUri}$response_type=code`;
  
  return res.redirect(kakaoAuthUrl);
});
  1. Authrorization Server로부터 Access Token 발급받기 & 사용자 정보 요청하기
  • 2번 과정이 완료된 후, 받은 code를 http://kauth.kakao.com/oauth/token에 요청해 Access token을 받는다.
  • 사용자의 기본 정보를 얻기 위해 발급 받은 Access token으로 https://kapi.kakao.com/v2/user/me 에 요청한다.
  • linkUser() 함수를 통해 사용자 session 정보에 Access token과 사용자 기본 정보를 저장한다.
function linkUser(session, provider, authData) {
  let result = false;
  if (session.authData) {
    if (session.authData[provider]) {
      // 이미 계정에 provider가 연결되어 있는 경우 
      return result;
    }
    session.authData[provider] = authData;
  } else {
    session.authData = {
      [provider]: authData
    };
  }
  result = true;
  rerun result;
}

// 사용자로부터 동의를 구한 후, 서비스 내에서 처리할 로직을 구현한다. 
// http://localhost:3000/auth/kakao/callback
router.get('/callback', async(req, res) => {
  const {session, query} = req;
  const { code } = query;
  
  console.info('===session===');
  console.log(session);
  
  let tokenResponse;
  try {
    // Authorization Server로부터 Access Token 발급받기
    tokenResponse = await axios({
      method: 'POST',
      url: 'https://kauth.kakao.com/oauth/token',
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      },
      data: qs.stringify({
        grant-type: 'autorization_code',
        client_id: kakao.clentId,
        client_secret: kakao.clientSecret,
        redirect_uri: kakao.redirectUri,
        code
      })
    });
  } catch (error) {
    return res.json(error.data);
  }
  console.info(tokenResponse.data);
  
  const { access_token} = tokenResponse.data;
  
  let userResponse;
  try {
    // access_token 으로 사용자 정보 요청하기 
    userResponse = await axios({
      method='GET',
      url: 'https://kapi.kakao.com/v2/user/me',
      headers: {
      	Authorization: `Bearer ${access_token}`
	  }
   	});
  } catch (error) {
	return res.json(error.data);
  }

  console.log(userResponse.data);

  const authData = {
	...tokenResponse.data,
	...userResponse.data
  };

  const result = linkUser(session, 'kakao', authData);

  if (result) {
	console.info('계정에 연결되었습니다.');
  } else {
	console.warn('이미 연결된 계정입니다.');
  }

  res.redirect('/');
});
  1. 사용자 Client 앱 연동 해제
  • Client 앱에 대한 인증 및 허가를 철회한다.
// views/partial/header.ejs
<div>
  <a href="/auth/kakao/unlink">카카오 앱 연결 해제</a>
</div>
// 카카오로 연동한 계정을 연결 해제한다. 사용자 authData 정보를 삭제한다.

function unlinkUser(session, provider, userId) {
  let result = false;
  
  if (
    session.authData &&
    session.authData[provider] &&
    session.authData[provider].id === useId
  ) {
    delete session.authData[provider];
    result = true;
  }
  return result;
}

// http://localhost:3000/auth/kakao/unlink
router.get('/unlink', async(req, res) => {
  const { session } = req;
  
  const { access_token } = session.authData.kakao;
  
  let unlinkResponse;
  try {
    unlinkResponse = await axios({
      method: 'POST',
      url: 'http://kapi.kakao.com/v1/user/unlink',
      headers: {
        Authorization: `Bearer ${access_token}`
      }
    });
  } catch (error) {
    return res.json(error.data);
  }
  console.log(unlinkResponse.data);
  
  const { id } = unlinkResponse.data;
  
  const result = unlinkUser(session, 'kakao', id);
  
  if (result) {
    console.log('연결 해제되었습니다.');
  } else {
    console.log('연동된 계정이 아닙니다.');
  }
  res.redirect('/');
});

참고

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글