카카오로 로그인하기 (JWT 토큰 발급, OAuth)
진행하고 있는 프로젝트에 접근성이 중요해서 카카오톡으로 로그인할 수 있도록 구현했었는데,
내가 짠 구조가 맞는지 의문이 들어 찾아보다가
[OAuth + Spring Boot + JWT] 1. OAuth란? 프론트엔드와 백엔드의 역할
OAuth(Open Authorization)란? OAuth는 인증을 위한 프로토콜이다. 다른 인터넷 서비스의 기능을 다른 어플리케이션에서도 사용할 수 있게 해준다. OAuth는 인증(Authentication)과 인가(Authorization)를 모두 포함
velog.io
위 글을 보게 되어 구조를 수정하였다.
수정 전의 카카오 로그인 흐름은 아래와 같았다.
근데
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
가이드를 다시 읽어보다가
위 다이어그램을 발견했고,
우선 위와 같이 흐름을 수정하였다.
Func loginWithKakao
async loginWithKakao({ code }: LoginWithKakaoInput): Promise<LoginOutput> {
try {
// get access token
const formData = {
grant_type: 'authorization_code',
client_id: process.env.KAKAO_REST_API_KEY,
redirect_uri: process.env.REDIRECT_URI_LOGIN,
code,
client_secret: process.env.KAKAO_CLIENT_SECRET,
};
const {
data: { access_token },
} = await axios
.post(`https://kauth.kakao.com/oauth/token?${qs.stringify(formData)}`)
.then((res) => {
return res;
});
// get user info
const { data: userInfo } = await axios
.get('https://kapi.kakao.com/v2/user/me', {
headers: {
Authorization: 'Bearer ' + access_token,
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
})
.then((res) => {
return res;
});
const name = userInfo.properties.nickname;
const profileImg = userInfo.properties.profile_image;
const email = userInfo.kakao_account.email;
const gender = userInfo.kakao_account.gender;
const birth = userInfo.kakao_account.birthday;
const password = // 비밀번호 생성
let intGender = gender === 'male' ? 0 : 1;
// check user exist with email
const { ok: user } = await this.findByEmail({ email });
// control user
let createAccountResult;
if (!user) {
const { ok } = await this.createAccount({
name,
email,
gender: +intGender,
password,
profileImg,
birth,
});
createAccountResult = ok;
}
if (user || createAccountResult) {
return await this.login({ email, password });
} else {
return { ok: false, error: "Couldn't create account in try" };
}
} catch (e) {
console.log(e);
return { ok: false, error: 'Please Refresh and Try One more time' };
}
}
backend에서 동작하는 login with kakao 함수이다.
// get access token
const formData = {
grant_type: 'authorization_code',
client_id: process.env.KAKAO_REST_API_KEY,
redirect_uri: process.env.REDIRECT_URI_LOGIN,
code,
client_secret: process.env.KAKAO_CLIENT_SECRET,
};
const {
data: { access_token },
} = await axios
.post(`https://kauth.kakao.com/oauth/token?${qs.stringify(formData)}`)
.then((res) => {
return res;
});
frontend에서 넘겨준 코드를 통해 access token을 발급받은 후,
// get user info
const { data: userInfo } = await axios
.get('https://kapi.kakao.com/v2/user/me', {
headers: {
Authorization: 'Bearer ' + access_token,
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
})
.then((res) => {
return res;
});
access token을 통해 유저 정보를 가져온다.
// check user exist with email
const { ok: user } = await this.findByEmail({ email });
// control user
let createAccountResult;
if (!user) {
const { ok } = await this.createAccount({
name,
email,
gender: +intGender,
password,
profileImg,
birth,
});
createAccountResult = ok;
}
if (user || createAccountResult) {
return await this.login({ email, password });
} else {
return { ok: false, error: "Couldn't create account in try" };
}
그 후 이메일로 유저를 확인하여
이미 계정이 있다면 로그인을, 없다면 유저 정보로 계정을 생성한 후 로그인한다.
이렇게 흐름은 변경하였지만 코드가 섹시하지 못한 것 같아 더 수정해보도록 할 것이다.
또한 현재 token 하나만 반환하고 있는데, 만료 기한을 설정하고 refresh token을 적용하도록 할 것이다.
참고자료
https://velog.io/@max9106/OAuth