이전에 참여했던 가말다 프로젝트에서 소셜 로그인 기능을 구현한 경험이 있다.
별도의 회원가입을 하지않고 구글과 네이버, 카카오와 같은 외부 소셜 계정을 기반으로 간편하게 회원가입 및 로그인할 수 있어 많은 웹과 앱 어플리캐이션에서 사용하고 있다.
이때 사용했던 것이 OAuth 2.0 프로토콜을 사용하는 Naver Login API인데 오늘은 OAuth에 대해 알아보자.
OAuth란?
OAuth는 유저가 비밀번호를 제공하지 않고 다른 웹사이트 상의 유저의 정보에 대해 웹사이트 혹은 애플리케이션의 접근 권한을 부여할 수 있는 수단으로서 사용되는 접근 위임을 위한 개방형 표준이다.
OAuth의 구성요소
구분 | 설명 |
Resource Owner | 웹 서비스를 이용하려는 유저, 자원(개인 정보)의 소유주, 사용자 Resource는 개인정보라 생각 |
Client | 회사 혹은 개인이 만든 애플리케이션 서버 Client는 Resource Server에게 필요한 자원을 요청하고 응답하는 관계라 붙은 이름이다. |
Authorization Server | 권한을 부여(인증에 사용할 아이템을 제공)해주는 서버 사용자는 이 서버로 ID와 PW를 넘겨 Authorization Code를 발급 받을 수 있다. Client는 이 서버로 Authorization Code를 넘겨 Token을 발급 받는다. |
Resource Server | 사용자의 정보를 갖고있는 회사 서버(구글, 카카오, 네이버 등) Client는 Token을 이 서버로 넘겨 개인정보를 응답 받는다. |
Access Token | 자원에 대한 접근 권한을 Resource Owner가 인가했음을 나타내는 자격증명 |
Refresh Token | Client는 Authorization Server로 부터 Access Token(비교적 짧은 만료기간)과 Refresh Token (비교적 긴 만료기간) 을 함께 부여 받는다. Access Token은 보안상 짧은 만료기간을 갖어야 하기 때문에 만료되면 사용자는 다시 로그인 해야한다. 하지만, Refresh Token이 있다면 Access Token이 만료될 때 Refresh Token을 통해 Access Token을 재발급 받아 로그인 상태를 유지할 수 있다. |
사용자는 소셜 미디어로 로그인할 경우 Client는 Resource Owner 대신 로그인을 시도하는데, 필요한 정보를 Resource Server에서 얻어 서로 비교해 유효성을 판단한다.
( 유효성 판단은 6번에서 얻는 Authorization Code부터 시작해 9번 Access Token을 발급받아 API 호출, 13번 서비스(리소스) 제공까지 Client와 Resource Server의 유효성 판단을 진행한다. )
Client가 유저의 (로그인) 정보/자원(리소스)을 Resource Server에 요청해 대신 로그인 하는 것이다.
Client는 Resource Owner의 동의를 구하고 Resource Server는 Resource Owner의 브라우저를 통해 clitent를 구분값(code)를 전달한다.
네이버 로그인 API 이용하기
로그인 시도에 적용할 callbackURL을 설정 및 받아올 데이터 설정
네이버 로그인 API에서 제공되는 Client ID와 Client Secret을 받는다.
Client ID : 서비스를 식별하는 ID, Resource Server에서 어떤 서비스를 제공할지 구분하는 ID
Client Secret : Client ID에 대한 비밀번호로 노출되면 안된다.
Authorized redirect URIs : client에 권한을 부여하는 과정에 Authorized Code를 제공하는 주소, client와 Resource Server 유효성 검사에서 redirect URIs도 체크하며 해당 주소가 아닐경우 Resource Server는 client가 아니라 판단.
위의 화면에서 네이버로 시작하기를 누르면 아래의 로그인 시도 화면으로 이동하게 된다.
이때 Client ID와 Client Secret, 인코딩된 Redirect URI를 쿼리스트링으로 정보를 보낸다.
/**
* @returns 네이버에 로그인 요청을 보내는 URL
*/
export const generateNaverApiUrl = (): string => {
return `https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${process.env.REACT_APP_NAVER_CLIENT_ID}&redirect_uri=${encodeURI(process.env.REACT_APP_NAVER_LOGIN_CALLBACK_URL!)}&state=${process.env.REACT_APP_NAVER_STATE}`;
}
이 과정이 아래의 3번과 4번에 해당한다.
위와 같은 화면에서 사용자가 "동의하기"를 누르고 로그인을 시도하면 6번 프로세스로 넘어간다.
Authorization code를 발급 받으면 이전에 설정한 Client로 Authorization code를 전달하고
Client는 코드를 이용해 Resource Server(네이버)에 Access Token을 요청한다.
내가 구현한 프로젝트에서는 naver-passport를 이용해 해당 과정을 수행했다.
// 정보가 get되는지 확인되면 BE작업을 하자
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver';
@Injectable()
export class NaverStrategy extends PassportStrategy(Strategy, 'naver') {
constructor(
) {
super({
clientID: process.env.NAVER_CLIENT_ID,
clientSecret: process.env.NAVER_CLIENT_SECRET,
callbackURL: process.env.NAVER_LOGIN_CALLBACK_URL
});
}
async validate(
accessToken: string, // 지워주면 데이터 못얻어옴
refreshToken: string, // 지워주면 데이터 못얻어옴
profile: any,
) {
const email = profile._json.email;
const nickname = profile._json.nickname;
const profileImage = profile._json.profile_image;
return {
email: email,
nickname: nickname,
profileImage: profileImage,
naverRefresh_token: refreshToken,
};
}
}
이렇게 해서 받아온 데이터를 이용해 사용자 로그인 로직을 구현했다.
이 다음에는 사용자의 이미지, 닉네임, 이메일, naverRefresh_token을 사용자의 고유 DB에 저장하여 서비스에서 이용 및 관리했다.
어려웠던 점
어려웠던 점은 크게 2가지가 있었다.
첫번째로는 Nestjs라는 처음 접하는 백엔드 프래임워크로 작업을 하는 것이다.
Naver Login API의 예시를 보여주는 코드들은 대부분 Express로 구성되어 있어 참고 레퍼런스가 부족했던 것이 고충이였다.
두번째로는 naver-passport란 개념이다.
passport라는 것이 Redirect URI로 오는 Authorization code를 받고 자동으로 AccessToken을 발급 받아 서비스 요청까지 해주는 좋은 도구이다.
하지만 내가 처음 사용할 때는 Redirect URI 설정과 받아온 Authorization code를 제대로 전달하지 않아 긴시간 어려움을 겪었다....
내가 생각해도 이건 너무 오래 걸린거 같다....
처음 접하는 Nestjs 프래임워크와 OAuth라는 개념을 학습하는 시간과 실제 서비스에 적용하는 시간이 합쳐저 이런 경과가 나온거 같다.
그렇지만 이렇게 배웠기에 더욱더 기억에 남는다.
역시 맞아가며 배우는 것이 가장 좋다......
'프로젝트 > 가말다 - 마일스톤' 카테고리의 다른 글
오류) 도메인 변경에 따른 이미지 서버 관련 오류 발견 (0) | 2024.03.23 |
---|---|
로그인 페이지 리펙토링 (0) | 2023.12.29 |
이미지 파일 서버에 전송 후 저장, 그리고 이미지 주소 이용 (0) | 2023.08.26 |
Store과 DB를 혼동해 사용하지 말자. (0) | 2023.05.16 |
로그아웃 기능 중 쿠키 전달을 위한 fetch의 credentials(내용증명) 옵션 사용 (0) | 2023.04.19 |