OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다.
사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수있게 해주는 HTTP 기반의 보안 프로토콜
OAuth에서 Auth는 Authentication(인증) & Authorization(허가) 2가지 의미를 포함하고 있다. 위 제목에서 제 3자는 이용할 서비스를 말한다. 이 물음에 동의한다는 것은 먼저 서비스 제공자에서 자신을 인증하고 제 3자에게 자신의 정볼르 사용하도록 접근 권한을 부여하는 것이다.
사용자 입장에서는 여러 서비스들을 하나의 계정으로 관리할 수 있게ㅔ되어 편해지고 개발자 입장에서는 민감한 사용자 정보를 다루지 않아 위험부담이 줄고 서비스 제공자로부터 사용자 정보를 활용할 수 있다.
OAuth2 토큰
카카오 로그인 연동을 하기 위해서는 서비스에 가입된 상태여야 한다.
서비스 제공자로부터 사용자 인증 및 허가를 받는다.
인증자의 정보를 받아 가입한 계정에 저장을 하면, 연동이 이루어진다.
연동된 계정으로 로그인 시도 시 서비스 제공자로부터 재확인이 완료되면,
소셜로그인이 이루어진다.
출처: 카카오 디벨로퍼스 문서 카카오 로그인 이해하기
카카오 로그인의 로그아웃은 사용자가 카카오 로그인을 통해 발급받은 토큰을 만료시켜, 로그아웃을 요청한 서비스에서 해당 사용자 정보로 카카오 API를 호출시킬 수 없게한다.
카카오 공식 문서를 참조하기
카카오 디벨로퍼스 카카오 로그인 설정하기
// views/partial/header.ejs
<div>
<a href="/auth/kakao">카카오 간편 로그인</a>
</div>
auth/kakao
route에 의해 생성된 주소임을 확인할 수 있다.// 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);
});
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('/');
});
// 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('/');
});