Authorization 완벽 마스터
Authorization의 핵심 개념과 실전 활용법
학습 항목
이미지 로딩 중...
OAuth 2.0 완벽 가이드
OAuth 2.0의 핵심 개념과 인증 플로우를 실제 코드로 배워봅니다. Authorization Code Grant부터 Token 관리까지 중급 개발자를 위한 완벽 가이드입니다.
들어가며
이 글에서는 OAuth 2.0 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Authorization_Code_Grant_Flow
- Authorization_Code_Exchange
- Access_Token_사용
- Refresh_Token_갱신
- PKCE_Extension
- State_Parameter_검증
- Scope_권한_관리
- Token_저장_보안
- Token_만료_처리
- Client_Credentials_Grant
- JWT_Token_검증
- OAuth_에러_처리
1. Authorization_Code_Grant_Flow
개요
OAuth 2.0의 가장 안전한 인증 방식으로, 인증 코드를 받아 액세스 토큰으로 교환하는 방식입니다.
코드 예제
// 인증 URL 생성
const authUrl = `https://oauth.provider.com/authorize?` +
`client_id=${CLIENT_ID}` +
`&redirect_uri=${REDIRECT_URI}` +
`&response_type=code` +
`&scope=read write` +
`&state=${randomState()}`;
window.location.href = authUrl;
설명
클라이언트가 사용자를 인증 서버로 리다이렉트하며, state 파라미터로 CSRF 공격을 방지합니다.
2. Authorization_Code_Exchange
개요
인증 서버로부터 받은 코드를 액세스 토큰으로 교환하는 과정입니다.
코드 예제
const response = await fetch('https://oauth.provider.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI
})
});
const { access_token, refresh_token } = await response.json();
설명
서버 측에서 안전하게 클라이언트 시크릿과 함께 코드를 토큰으로 교환합니다.
3. Access_Token_사용
개요
발급받은 액세스 토큰을 사용하여 보호된 API 리소스에 접근합니다.
코드 예제
const userData = await fetch('https://api.provider.com/user', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
const user = await userData.json();
console.log('User info:', user);
설명
Bearer 토큰 방식으로 Authorization 헤더에 액세스 토큰을 포함하여 API를 호출합니다.
4. Refresh_Token_갱신
개요
액세스 토큰이 만료되었을 때 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받습니다.
코드 예제
const refreshAccessToken = async (refreshToken) => {
const response = await fetch('https://oauth.provider.com/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
})
});
return await response.json();
};
설명
사용자가 다시 로그인하지 않고도 리프레시 토큰으로 새로운 액세스 토큰을 받을 수 있습니다.
5. PKCE_Extension
개요
모바일 앱이나 SPA에서 클라이언트 시크릿 없이 안전하게 OAuth를 사용하는 방법입니다.
코드 예제
const codeVerifier = base64URLEncode(crypto.randomBytes(32));
const codeChallenge = base64URLEncode(
crypto.createHash('sha256').update(codeVerifier).digest()
);
const authUrl = `https://oauth.provider.com/authorize?` +
`client_id=${CLIENT_ID}` +
`&code_challenge=${codeChallenge}` +
`&code_challenge_method=S256`;
설명
Code Verifier와 Challenge를 사용하여 인증 코드 탈취 공격을 방지합니다.
6. State_Parameter_검증
개요
CSRF 공격을 방지하기 위해 state 파라미터를 생성하고 검증합니다.
코드 예제
const state = crypto.randomBytes(16).toString('hex');
sessionStorage.setItem('oauth_state', state);
// 콜백에서 검증
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('state') !== sessionStorage.getItem('oauth_state')) {
throw new Error('Invalid state parameter');
}
설명
랜덤 state 값을 저장했다가 콜백에서 비교하여 요청의 유효성을 검증합니다.
7. Scope_권한_관리
개요
애플리케이션이 필요한 권한만 요청하여 사용자 데이터를 보호합니다.
코드 예제
const requestedScopes = ['user:read', 'repo:write', 'notifications'];
const authUrl = `https://oauth.provider.com/authorize?` +
`client_id=${CLIENT_ID}` +
`&scope=${requestedScopes.join(' ')}` +
`&response_type=code`;
// 받은 토큰의 scope 확인
const { scope } = await response.json();
console.log('Granted scopes:', scope.split(' '));
설명
필요한 최소 권한만 요청하고, 실제 부여된 scope를 확인하여 사용합니다.
8. Token_저장_보안
개요
액세스 토큰과 리프레시 토큰을 안전하게 저장하는 방법입니다.
코드 예제
// httpOnly 쿠키에 저장 (서버 측)
res.cookie('refresh_token', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000
});
// 메모리에만 저장
let accessToken = null;
설명
리프레시 토큰은 httpOnly 쿠키에, 액세스 토큰은 메모리에 저장하여 XSS 공격을 방지합니다.
9. Token_만료_처리
개요
API 호출 시 토큰 만료를 감지하고 자동으로 갱신하는 로직입니다.
코드 예제
const apiCall = async (url, options = {}) => {
const response = await fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${accessToken}` }
});
if (response.status === 401) {
accessToken = await refreshAccessToken(refreshToken);
return fetch(url, { ...options, headers: { Authorization: `Bearer ${accessToken}` }});
}
return response;
};
설명
401 에러를 감지하면 자동으로 토큰을 갱신하고 요청을 재시도합니다.
10. Client_Credentials_Grant
개요
서버 간 통신에서 사용자 개입 없이 인증하는 방식입니다.
코드 예제
const getServiceToken = async () => {
const response = await fetch('https://oauth.provider.com/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
scope: 'api:read'
})
});
return await response.json();
};
설명
백엔드 서비스가 API에 접근할 때 사용하며, 사용자 컨텍스트가 필요 없는 경우에 적합합니다.
11. JWT_Token_검증
개요
받은 액세스 토큰이 JWT인 경우 서명을 검증하고 클레임을 확인합니다.
코드 예제
const jwt = require('jsonwebtoken');
const verifyToken = (token, publicKey) => {
try {
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
if (decoded.exp < Date.now() / 1000) {
throw new Error('Token expired');
}
return decoded;
} catch (error) {
throw new Error('Invalid token');
}
};
설명
공개키로 서명을 검증하고 만료시간(exp)을 확인하여 토큰의 유효성을 판단합니다.
12. OAuth_에러_처리
개요
OAuth 플로우에서 발생할 수 있는 다양한 에러를 처리합니다.
코드 예제
const urlParams = new URLSearchParams(window.location.search);
const error = urlParams.get('error');
if (error) {
const errorMessages = {
'access_denied': '사용자가 권한을 거부했습니다',
'invalid_scope': '요청한 scope가 유효하지 않습니다',
'server_error': '인증 서버 오류가 발생했습니다'
};
alert(errorMessages[error] || '알 수 없는 오류');
}
설명
인증 서버가 반환하는 표준 에러 코드를 파싱하여 사용자에게 적절한 메시지를 표시합니다.
마치며
이번 글에서는 OAuth 2.0 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#OAuth #Authentication #Authorization #JWT #Security