-
Notifications
You must be signed in to change notification settings - Fork 0
OAuth 개념 정리와 로그인 방법
-
OAuth
는 Open Authorization 의 약자로, 주로 사용자 정보를 보유한 서비스 (Google, kakao) 에서 인증과 권한을 위임하는 프로토콜입니다. -
OAuth
는 애플리케이션이 사용자 비밀번호나 민감한 정보를 요청하지 않고, 사용자로부터 특정 권한을 위임받아 작업을 수행할 수 있도록 합니다.
- 자원에 대한 접근 권한을 가진 사용자입니다.
- 예) Google Drive에 파일을 가진 실제 사용자
- 자원 소유자를 대신해서 자원 서버에 접근하고자 하는 애플리케이션입니다.
- 예) 클라이언트 앱은 Google calender에 접근하여 일정을 등록할 수 있는 웹 애플리케이션일 수 있습니다.
- 우리가 제어하고자하는 실제 자원, 데이터를 가지고 제공하는 서버입니다.
- 예) Google Drive의 파일 저장 서버나, Google calender 서버가 이에 해당합니다.
- 사용자의 신원을 확인하고 클라이언트가 자원에 접근할 수 있는 권한을 부여하는 서버입니다.
- Access Token을 주는 곳이라고 생각하면 좋을 것 같습니다.
- 보통 자원을 제공하는 서비스 제공자가 직접 운영합니다.
- 클라이언트가 자원 서버에 접근할 수 있도록 권한을 부여 받으면 최종적으로 Authorization Server가 발급해주는 토큰입니다.
- 해당 토큰을 통해 클라이언트는 제한적으로 자원에 접근할 수 있습니다.
Client 서비스에서 OAuth를 제공하기 위해서는 우선 Resource Server 에 그에 대한 사전 승인을 받아야합니다. 이를 “register” 한다고 합니다.
Resoure server 마다 OAuth에 대한 승인 절차는 다르겠지만, 공통적으로 부여해주는 항목이 꼭 있습니다.
- 말그대로 Client를 구별하기 위해 있는 아이디입니다. 노출되어도 크게 상관없습니다.
- Client 식별에 대한 비밀번호입니다. 절대 노출이 되어서는 안됩니다.
- Resource Owner로 부터 OAuth 인증을 허가하는데 중요한 역할을 합니다.
- 권한을 부여하는 과정에서 Authorized code를 전달받을 주소입니다.
- 만약 OAuth 요청을 해당 주소를 통해 하지않고 다른 경로에 한다면, 요청을 무시당하게됩니다.
- Client Secret 와 함께 Resource Owner로 부터 OAuth 인증을 허가하는데 중요한 역할을 합니다.
앞서 Client가 Resource server에 등록을 했기에,
Client와 Resource Server에는 각각 그에 관련된 정보가 저장되어있습니다.
승인 과정을 알아보기전,
만약 Resource Server에 존재하는[ A, B, C, D ]의 기능 중 [B, C] 의 기능만을 사용하고 싶다면 그 인증 범위 또한 지정을 해줄 수 있습니다.
이를 scope라고 합니다. 클라이언트가 접근할 수 있는 데이터와 권한의 범위를 라고 생각해주시면 될 것 같습니다.
이제 Resource Owner의 승인 과정을 알아보겠습니다.
만약, Resource Owner가 Client 에 있는 외부 서비스를 이용해야된다면,
위와 같이 Client 서비스는 OAuth 로그인 화면을 보여주게 됩니다.
해당 버튼들은 이러한 url로 이동하도록 설정되어있습니다.
여기에 client_id, scope, redirect_uri 가 명시되어있는 것을 볼 수 있습니다.
register 과정에서 보았던 것들인 것 같군요.
Resource Owner가 해당 버튼을 누른 후 일어나는 일은 Resource Server에 로그인 여부를 확인하는 것 입니다. 만일 로그인이 되어있지않으면 먼저 로그인을 하도록 합니다.
로그인에 성공을 한다면 이제 url에 있는 client_id, redirect_uri 와 기존 client의 register 과정에서 생성되었던 client_id, redirect_uri 을 비교합니다.
만일 둘 중 하나라도 다르다면 Resource Server는 해당 요청을 거절하게 됩니다.
client_id와 redirect_uri를 확인하고 일치한다면, 이제 Resource Owner에게 Client의 요청에 동의하는지에 대한 문구를 보여주게 됩니다.
Resource Owner가 자신의 정보를 특정 서비스에 제공하는 데 동의하면, Resource Server는 이 동의에 따라 Resource Owner의 허용 정보(사용자 ID 및 허용 범위인 scope)를 저장합니다. 여기서 scope는 클라이언트가 접근할 수 있는 데이터와 권한의 범위를 말합니다.
앞서 Resource Owner의 승인을 받았으니, 이제는 Resource Server의 승인이 필요한 상황입니다.
Resource Server의 승인 절차는 다음과 같습니다. (상단의 그림과 함께 참고해 주세요!)
-
Resource Owner가 서비스 제공에 동의하면, Resource Server는
Authorization Code
라는 임시 인증 코드를 생성합니다. - 생성된
Authorization Code
가 포함된 URL을 HTTP 리다이렉트 방식으로 Resource Owner를 자동으로 Client에 연결시킵니다. - 이를 통해 Client는
Authorization Code
를 전달받을 수 있습니다.
이후 Client는 받은 Authorization Code
와 함께 기존 인증 정보를 사용하여 Resource Server에 Resource Owner의 최종 인증을 요청합니다.
모든 인증 정보가 확인되면, Resource Server는 최종적으로 Client에게 Access Token을 발급합니다.
발급된 Access Token은 Client에 저장되며, Client는 필요할 때마다 이 Access Token을 사용하여 Resource Server에 요청을 보내고 리소스에 접근할 수 있습니다.
Resource Server는 이 Access Token을 통해 user id가 1인 사용자가 b와 c 권한에 대한 접근을 요청하고 있음을 확인할 수 있습니다.
- 클라이언트가 서비스 서버로 카카오 로그인 요청을 하고 서비스 서버는 클라이언트가 카카오 로그인을 수행할 수 있게 카카오 서버로 요청을 보낸다.
@Get('kakao')
kakaoLogin(@Res({passthrough: true}) res: Response) {
const clientId = this.configService.get<string>('CLIENT_ID');
const redirectUrl = this.configService.get<string>('REDIRECT_URL');
const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUrl}&response_type=code`;
res.redirect(kakaoAuthUrl);
return;
}
// 클라이언트
const KakaoLogin = () => {
const handleKakaoLogin = async () => {
try {
window.location.href = 'http://localhost:3000/api/login/kakao';
} catch (error) {
console.error('카카오 로그인 에러:', error);
}
};
return {
handleKakaoLogin
};
};
- 쿼리 파라미터에서 필수로 필요한 요소들을 전부 추가했습니다.
https://kauth.kakao.com/oauth/authorize
에 쿼리 파라미터를 추가하여 인가코드를 받습니다.
HTTP/1.1 302 Found
Content-Length: 0
Location: ${REDIRECT_URI}?code=${AUTHORIZE_CODE}
- 받은 인가코드를 가지고 다시 카카오 인증/인가 처리를 진행합니다.
// 클라이언트
export const KakaoCallback = ()=>{
const [searchParams] = useSearchParams();
const navigate = useNavigate();
useEffect(() => {
const code = searchParams.get('code');
if (code) {
axios.get(`http://localhost:3000/api/login/kakao/callback?code=${code}`, {
withCredentials: true
})
.then(response => {
console.log('로그인 성공:', response.data);
navigate('/');
})
.catch(error => {
console.error('카카오 로그인 콜백 에러:', error);
navigate('/login');
});
}
}, [searchParams, navigate]);
return (
<div>카카오 로그인 처리중...</div>
);
}
//서버
@Get('kakao/callback')
async kakaoCallback(@Query('code') code: string) {
await this.loginUserUsecase.login(code);
return;
}
- 해당 서버로 코드를 가지고 다시 요청을 보내 진행합니다.
curl -v -X POST "https://kauth.kakao.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded;charset=utf-8" \
-d "grant_type=authorization_code" \
-d "client_id=${REST_API_KEY}" \
--data-urlencode "redirect_uri=${REDIRECT_URI}" \
-d "code=${AUTHORIZE_CODE}"
- 서버에 요청을 보내고 서버에서는 해당 카카오 인증서버로 요청을 보낸다.
const clientId = this.configService.get<string>('CLIENT_ID');
const redirectUrl = this.configService.get<string>('REDIRECT_URL');
const response = await axios.post('https://kauth.kakao.com/oauth/token', new URLSearchParams({
grant_type: 'authorization_code',
client_id: clientId,
redirect_uri: redirectUrl,
code: code,
}).toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
data: {
access_token: '97Uy6f52V3wWEAyiGPib56b6mMfS7dcJAAAAAQoqJY8AAAGTQo3TRJgXPJRhmZ-F',
token_type: 'bearer',
refresh_token: 'SnoMWJdzi2arGm0Cqq6W6rdjmdvYYM2IAAAAAgoqJY8AAAGTQo3TQpgXPJRhmZ-F',
expires_in: 21599,
scope: 'account_email',
refresh_token_expires_in: 5183999
}
- 이 응답이 나온다.
- 해당 발급받은 토큰을 통해 이제 카카오 정보를 얻어와야 한다.
curl -v -G GET "https://kapi.kakao.com/v2/user/me" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
이 형식으로 카카오에 요청을 보내면 유저의 정보를 얻을 수 있다.
const userResponse = await axios.get('https://kapi.kakao.com/v2/user/me', {
headers: {
Authorization: `Bearer ${response.data.access_token}`,
},
});
{
id: 3798636860,
connected_at: '2024-11-19T03:30:15Z',
kakao_account: {
has_email: true,
email_needs_agreement: false,
is_email_valid: true,
is_email_verified: true,
email: '[email protected]'
}
}
- 해당 형식으로 OAuth 로그인을 통한 카카오 유저 로그인과 유저 정보를 얻어올 수 있다.
OAuth는 다양한 서비스 간 안전한 인증과 권한 부여를 가능하게 하며, 특히 사용자의 민감한 정보를 보호하는 데 중요한 역할을 합니다.
이번 글을 통해 OAuth의 기본적인 흐름을 이해하고, 실제 인증 과정에서 어떤 일이 일어나는지 감을 잡으셨기를 바랍니다. 이후에는 Refresh Token, 토큰 만료와 갱신 등 더 깊은 OAuth 개념을 공부해 보시면 더욱 좋을 것 같습니다. :)
web12-MafiaCamp