프런트엔드에서 OAuth 흐름 구현
SPA에서 PKCE를 사용하는 Authorization Code 흐름을 구현하고, 코드를 토큰으로 교환하며, 액세스 토큰을 안전하게 저장하고, 암시적 흐름은 피합니다.
프런트엔드에서 OAuth 흐름 구현은(는) CoddyKit의 무료 Frontend Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Frontend Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
OAuth란 무엇입니까?
OAuth 2.0은 위임 프로토콜입니다. 사용자의 비밀번호를 전혀 확인하지 않고도 앱이 사용자를 대신하여 타사 서비스(Google, GitHub 등)를 이용할 수 있도록 권한을 부여받습니다. 'Google로 로그인', GitHub OAuth 앱 및 대부분의 최신 인증 방식에 사용됩니다.
주요 역할
리소스 소유자: 사용자입니다. 클라이언트: 사용자의 앱입니다. 권한 부여 서버: OAuth 제공자(Google, Auth0)입니다. 리소스 서버: 액세스 토큰으로 보호되는 API입니다.
권한 부여 코드 흐름
서버 측 앱을 위한 표준적이고 안전한 흐름입니다. 1) 사용자를 제공자의 로그인 페이지로 리디렉션합니다. 2) 사용자가 로그인하고 동의합니다. 3) 제공자가 코드와 함께 사용자를 다시 리디렉션합니다. 4) 서버가 코드를 액세스 토큰으로 교환합니다(서버만 보유하는 비밀).
SPA에 PKCE가 필요한 이유
SPA는 클라이언트 비밀을 안전하게 보관할 수 없습니다(브라우저 번들에 포함되기 때문입니다). PKCE(Proof Key for Code Exchange)는 요청마다 생성하는 코드 검증자와 챌린지로 비밀을 대체합니다. 이제 모든 OAuth 공개 클라이언트의 표준입니다.
PKCE 흐름 단계별 살펴보기
1) 무작위 code_verifier를 생성합니다. 2) 이를 SHA-256으로 해시하여 code_challenge를 만듭니다. 3) 챌린지와 함께 /authorize로 리디렉션합니다. 4) 동의가 끝나면 코드를 돌려받습니다. 5) 코드와 검증자를 액세스 토큰으로 교환합니다. 제공자는 챌린지가 검증자의 해시와 같은지 확인합니다.
코드 검증자와 챌린지 생성
검증자는 무작위 문자열이고, 챌린지는 검증자를 SHA-256으로 해시한 뒤 base64-URL로 인코딩한 값입니다.
function base64url(arr) {
return btoa(String.fromCharCode(...arr))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
async function generatePKCE() {
const arr = new Uint8Array(32);
crypto.getRandomValues(arr);
const verifier = base64url(arr);
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
const challenge = base64url(new Uint8Array(hash));
return { verifier, challenge };
}제공자로 리디렉션하기
챌린지, state(CSRF 보호), 범위를 포함하여 권한 부여 URL을 구성하십시오.
const { verifier, challenge } = await generatePKCE();
const state = crypto.randomUUID();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);
const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
url.searchParams.set('client_id', CLIENT_ID);
url.searchParams.set('redirect_uri', `${origin}/auth/callback`);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
url.searchParams.set('state', state);
window.location.href = url.toString();콜백 처리
제공자는 ?code=...&state=...와 함께 콜백 URL로 리디렉션합니다. CSRF를 방지하기 위해 state를 확인한 다음 코드를 교환하십시오.
// /auth/callback page:
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const expected = sessionStorage.getItem('oauth_state');
if (returnedState !== expected) throw new Error('Bad state');
const verifier = sessionStorage.getItem('pkce_verifier');
const tokens = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: CLIENT_ID,
redirect_uri: `${origin}/auth/callback`,
code_verifier: verifier
})
}).then(r => r.json());
// tokens: { access_token, id_token, refresh_token, expires_in }안전한 토큰 저장
모범 사례는 액세스 토큰을 localStorage에 저장하지 않는 것입니다(XSS로 탈취될 수 있음). 방법은 다음과 같습니다. 1) 메모리에만 저장합니다(새로 고침하면 삭제되므로 다시 인증해야 함). 2) HttpOnly 쿠키를 사용합니다(PKCE 흐름 후 백엔드가 설정하며 JS로 탈취할 수 없음).
암시적 흐름 피하기
기존의 암시적 흐름은 URL 프래그먼트에 액세스 토큰을 직접 반환합니다. OAuth 2.0 Security BCP에서는 이 흐름을 폐기했는데, 브라우저 기록과 리퍼러를 통해 토큰이 유출되기 때문입니다. 항상 권한 부여 코드 + PKCE를 사용하십시오.
갱신 토큰
액세스 토큰은 일반적으로 1시간 후 만료됩니다. 수명이 더 긴 갱신 토큰을 사용하면 새 액세스 토큰을 조용히 발급받을 수 있습니다. SPA에서는 갱신 토큰을 점점 더 HttpOnly 쿠키로 전달하는 추세이며, localStorage에는 절대 저장하지 않습니다.
ID 토큰과 액세스 토큰 비교
ID 토큰(OpenID Connect): 사용자가 누구인지 증명하는 JWT입니다. 액세스 토큰: 불투명한 값이거나 JWT이며, API 호출에 사용됩니다. 신뢰하기 전에 ID 토큰의 서명과 클레임(iss, aud, exp, nonce)을 확인하십시오.
인증 라이브러리
직접 구현하지 마십시오. 원시 OIDC에는 oidc-client-ts, Auth0에는 auth0/spa-js, Clerk에는 @clerk/clerk-react, Next에는 NextAuth.js/Auth.js, Nuxt에는 Nuxt-auth를 사용하십시오. 이러한 라이브러리가 PKCE, 갱신 및 저장을 처리합니다.
빠른 확인
Single-Page Application이 기본적인 권한 부여 코드 흐름만 사용하는 대신 권한 부여 코드 흐름과 함께 PKCE(Proof Key for Code Exchange)를 사용해야 하는 이유는 무엇입니까?
복습: 프론트엔드의 OAuth
권한 부여 코드 + PKCE는 SPA의 현대적인 표준입니다. 요청마다 검증자와 S256 챌린지를 생성하십시오. 챌린지 + state와 함께 /authorize로 리디렉션하십시오. 콜백에서 state를 확인하십시오. 코드 + 검증자를 토큰으로 교환하십시오. 암시적 흐름은 절대 사용하지 마십시오. 토큰 저장에는 HttpOnly 쿠키를 우선 사용하십시오. 라이브러리(auth0-spa-js, oidc-client-ts, NextAuth)를 사용하고 직접 구현하지 마십시오.
자주 묻는 질문
“프런트엔드에서 OAuth 흐름 구현” 강의는 무료인가요?
네 — “프런트엔드에서 OAuth 흐름 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Frontend Academy 강의 전체를 잠금 해제할 수 있습니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“프런트엔드에서 OAuth 흐름 구현”에서 뭘 배우나요?
SPA에서 PKCE를 사용하는 Authorization Code 흐름을 구현하고, 코드를 토큰으로 교환하며, 액세스 토큰을 안전하게 저장하고, 암시적 흐름은 피합니다. 브라우저에서 직접 실행하는 실습 코드로 Frontend Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Frontend Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Frontend Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“프런트엔드에서 OAuth 흐름 구현” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Frontend Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Frontend Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- XSS 방지: 출력 인코딩과 CSP
- CSRF: SameSite 쿠키와 토큰
- Content Security Policy: nonce와 해시
- 프런트엔드에서 OAuth 흐름 구현