테마
Auth0 인증 통합
App Shell에서 Auth0 SaaS를 활용한 인증 시스템을 구축하고, 마이크로앱 전체에 인증 컨텍스트를 전파하는 구조를 학습한다.
학습 목표
- Auth0의 역할과 SPA 인증 흐름(Authorization Code + PKCE)을 이해한다.
- App Shell 레벨에서
@auth0/auth0-react프로바이더를 구성한다. - 마이크로앱에서 인증 정보(사용자, 토큰)에 접근하는 방법을 구현한다.
- 로그인/로그아웃 플로우와 비인증 사용자 차단 로직을 완성한다.
- 액세스 토큰 관리와 API 호출 시 인증 헤더 주입 전략을 익힌다.
본문
1. 마이크로프론트엔드에서 인증의 위치
마이크로프론트엔드 환경에서 인증은 App Shell이 중앙에서 관장하는 것이 일반적이다. 각 마이크로앱이 독립적으로 인증을 처리하면 로그인 상태 불일치, 토큰 중복 관리, UX 파편화가 발생한다.
2. Auth0 설정
2-1. Auth0 대시보드 설정
Auth0에서 SPA(Single Page Application) 타입의 애플리케이션을 생성하고 다음 값을 설정한다.
| 설정 항목 | 값 | 설명 |
|---|---|---|
| Application Type | Single Page Application | SPA용 PKCE 흐름 활성화 |
| Allowed Callback URLs | http://localhost:3000 | 로그인 후 리다이렉트 대상 |
| Allowed Logout URLs | http://localhost:3000 | 로그아웃 후 리다이렉트 대상 |
| Allowed Web Origins | http://localhost:3000 | CORS 허용 및 Silent Auth 지원 |
대시보드에서 확인하는 Domain과 Client ID가 프론트엔드 설정에 필요하다.
2-2. 환경 변수 구성
민감한 설정값은 .env 파일로 관리한다. Webpack 환경에서는 dotenv-webpack 플러그인으로 런타임에 주입한다.
bash
# apps/shell/.env
REACT_APP_AUTH0_DOMAIN=your-tenant.auth0.com
REACT_APP_AUTH0_CLIENT_ID=your_client_id_here
REACT_APP_AUTH0_CALLBACK_URL=http://localhost:3000js
// apps/shell/webpack.config.js
const Dotenv = require("dotenv-webpack");
module.exports = {
plugins: [
new Dotenv(),
// ...기존 플러그인
],
};3. Auth0Provider 구성
@auth0/auth0-react 패키지의 Auth0Provider를 React Router의 내부에 배치해야 useNavigate 훅을 사용할 수 있다. 이를 위해 별도의 래퍼 컴포넌트를 만든다.
tsx
// apps/shell/src/components/Auth0ProviderWithNavigator.tsx
import React from "react";
import { useNavigate } from "react-router-dom";
import { Auth0Provider } from "@auth0/auth0-react";
import type { AppState } from "@auth0/auth0-react";
const Auth0ProviderWithNavigator: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const navigate = useNavigate();
const domain = process.env.REACT_APP_AUTH0_DOMAIN;
const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
const redirectUri = process.env.REACT_APP_AUTH0_CALLBACK_URL;
if (!domain || !clientId || !redirectUri) {
throw new Error("Auth0 환경 변수가 설정되지 않았습니다.");
}
const onRedirectCallback = (appState?: AppState) => {
navigate(appState?.returnTo || window.location.pathname);
};
return (
<Auth0Provider
domain={domain}
clientId={clientId}
authorizationParams={{ redirect_uri: redirectUri }}
onRedirectCallback={onRedirectCallback}
>
{children}
</Auth0Provider>
);
};
export default Auth0ProviderWithNavigator;이 컴포넌트를 Layout 컴포넌트에서 감싸 적용한다.
tsx
// apps/shell/src/components/Layout.tsx
import Auth0ProviderWithNavigator from "./Auth0ProviderWithNavigator";
export default function Layout() {
return (
<Auth0ProviderWithNavigator>
{/* Header, Navigation, Outlet 등 */}
</Auth0ProviderWithNavigator>
);
}4. 로그인/로그아웃 플로우
로그인/로그아웃 핸들러 구현
useAuth0() 훅에서 loginWithRedirect, logout, isAuthenticated, user를 꺼내 사용한다.
- 로그인:
await loginWithRedirect({ appState: { returnTo: "/" } })-- Auth0 로그인 페이지로 리다이렉트 - 로그아웃:
await logout({ logoutParams: { returnTo: window.location.origin } })-- 세션 종료 후 메인 페이지로 복귀
UIKit의 Button 컴포넌트와 결합하여 isAuthenticated 상태에 따라 로그인/로그아웃 버튼을 조건부 렌더한다.
인증 가드
Layout에서 Outlet을 isAuthenticated로 감싸 비인증 사용자의 마이크로앱 접근을 차단한다. isLoading 상태를 반드시 먼저 처리해야 한다. Auth0 SDK 초기화 시 토큰 유효성을 확인하는데, 이 과정 완료 전에는 isAuthenticated가 false로 나타나 깜빡임이 발생할 수 있다.
tsx
{isLoading ? <div>인증 확인 중...</div>
: isAuthenticated ? <Outlet />
: <div>로그인이 필요합니다.</div>}5. 마이크로앱에서 인증 정보 접근
App Shell의 Auth0Provider 내부에 렌더되는 마이크로앱은 useAuth0() 훅으로 인증 정보에 접근할 수 있다. 단, Module Federation 환경에서는 @auth0/auth0-react를 shared 싱글톤으로 설정해야 동일한 컨텍스트를 공유한다.
js
// webpack.config.js (마이크로앱)
shared: {
"@auth0/auth0-react": { singleton: true, requiredVersion: "^2.0.0" },
react: { singleton: true },
"react-dom": { singleton: true },
}마이크로앱 내부에서는 getAccessTokenSilently()로 토큰을 획득하고, fetch 호출 시 Authorization: Bearer {token} 헤더를 주입하여 API 서버와 통신한다.
6. 토큰 관리 전략
Auth0 React SDK는 기본적으로 토큰을 메모리에 저장한다. getAccessTokenSilently()를 호출하면 다음 순서로 동작한다.
- 캐시 확인: 메모리에 유효한 토큰이 있으면 즉시 반환
- Silent Auth: 토큰이 만료됐으면 hidden iframe으로 Auth0에 새 토큰 요청
- 실패 시: 사용자에게 재로그인 요청
토큰 저장 방식별 보안 수준은 다음과 같다.
| 저장 방식 | XSS 방어 | CSRF 방어 | 탭 간 공유 | 권장 여부 |
|---|---|---|---|---|
| 메모리 (기본) | 강함 | 해당 없음 | 불가 | 권장 |
| localStorage | 취약 | 해당 없음 | 가능 | 비권장 |
| HttpOnly Cookie | 강함 | 토큰 필요 | 가능 | 가장 안전 (BFF 필요) |
Axios 인터셉터로 토큰 자동 주입
매번 getAccessTokenSilently()를 호출하는 대신 Axios 인터셉터에 토큰 획득 함수를 등록하여 자동화할 수 있다. setTokenGetter(getAccessTokenSilently)를 앱 초기화 시 한 번 호출하면, 이후 모든 API 요청에 Authorization: Bearer {token} 헤더가 자동으로 주입된다.
7. 마이크로앱 간 인증 공유 방식 비교
| 방식 | 장점 | 단점 | 적합한 경우 |
|---|---|---|---|
| Auth0 SDK 싱글톤 공유 | 설정 간단. 컨텍스트 직접 공유 | Module Federation shared 필수 | 동일 프레임워크 (React) |
| CustomEvent 전파 | 프레임워크 무관 | 토큰 직접 전달 시 보안 위험 | 다중 프레임워크 |
| 마이크로앱별 Auth0 클라이언트 | 완전 독립. Shell 장애 영향 없음 | SSO Session 의존. 초기 지연 | 고도의 독립성 요구 |
커리어 플랫폼에서는 모든 앱이 React를 사용하므로 Auth0 SDK 싱글톤 공유 방식을 기본으로 채택하되, 높은 보안이 필요한 경우 마이크로앱별 Auth0 클라이언트를 사용하여 Shell에 의존하지 않는 토큰 획득 경로를 확보한다.
핵심 정리
| 항목 | 내용 |
|---|---|
| 인증 위치 | App Shell에서 중앙 관리. Auth0Provider로 컨텍스트 전파 |
| 인증 흐름 | Authorization Code + PKCE. loginWithRedirect 기반 |
| 프로바이더 배치 | React Router 내부에 위치 (useNavigate 사용을 위해) |
| 인증 가드 | isAuthenticated로 Outlet 조건부 렌더링 |
| 토큰 저장 | 메모리 (기본). XSS 방어에 가장 안전 |
| 토큰 갱신 | getAccessTokenSilently() -> Silent Auth (hidden iframe) |
| API 호출 | Authorization: Bearer {token} 헤더 주입 |
| 마이크로앱 공유 | Module Federation shared 싱글톤으로 Auth0 SDK 공유 |
다음 단계
다음 문서 ../12-마이크로앱-구현/01-API-서버-구축.md에서는 마이크로앱이 실제로 통신할 API 서버를 구축한다. Auth0에서 발급한 토큰을 서버 측에서 검증하고, 각 마이크로앱의 API 엔드포인트를 설계하는 과정을 다룬다.