초기 커밋

This commit is contained in:
hansoo
2025-03-26 18:16:46 +09:00
commit 266674cc0e
67 changed files with 14235 additions and 0 deletions

View File

@@ -0,0 +1,251 @@
# ZELLYY Core SSO 구현 계획
## 개요
이 문서는 ZELLYY Core의 Single Sign-On (SSO) 구현 방안과 소셜 로그인(구글, 애플) 통합에 대해 설명합니다.
## SSO 구현 전략
### 지원하는 인증 방식
1. **이메일/비밀번호** - 기본 인증 방식
2. **구글 OAuth** - 구글 계정을 통한 로그인
3. **애플 로그인** - 애플 계정을 통한 로그인 (iOS 앱 필수)
### Supabase Auth 활용
ZELLYY Core는 Supabase Auth를 활용하여 SSO를 구현합니다. Supabase는 다음 기능을 제공합니다:
- 소셜 로그인 통합 (Google, Apple, Github 등)
- JWT 토큰 관리
- 사용자 세션 관리
- 보안 설정 (2FA, 비밀번호 정책 등)
## 소셜 로그인 구현 단계
### 1. 제공자 등록 및 설정
#### 구글 OAuth 설정
1. Google Cloud Console에서 프로젝트 생성
2. OAuth 2.0 클라이언트 ID 생성
3. 승인된 리디렉션 URI 설정:
```
https://[PROJECT_ID].supabase.co/auth/v1/callback
```
4. 클라이언트 ID와 비밀번호를 Supabase 설정에 등록
#### 애플 로그인 설정
1. Apple Developer 계정에서 App ID 등록
2. Sign in with Apple 서비스 활성화
3. 서비스 ID 생성 및 도메인 검증
4. 승인된 리디렉션 URI 설정
5. 키 생성 및 Supabase 설정에 등록
### 2. 프론트엔드 구현
#### 로그인 버튼
```jsx
import { supabase } from './supabaseClient';
const SocialLoginButtons = () => {
const handleGoogleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'https://app.zellyy.com/auth/callback'
}
});
};
const handleAppleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'apple',
options: {
redirectTo: 'https://app.zellyy.com/auth/callback'
}
});
};
return (
<div className="social-login">
<button onClick={handleGoogleLogin} className="google-btn">
Google로 계속하기
</button>
<button onClick={handleAppleLogin} className="apple-btn">
Apple로 계속하기
</button>
</div>
);
};
```
#### OAuth 콜백 처리
```jsx
import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { supabase } from './supabaseClient';
const AuthCallback = () => {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
// URL에서 인증 정보 추출
const handleAuthCallback = async () => {
const { error } = await supabase.auth.getSession();
if (error) {
console.error('인증 오류:', error);
navigate('/login?error=auth_callback_error');
return;
}
// 성공적으로 인증됨
navigate('/dashboard');
};
handleAuthCallback();
}, [navigate]);
return <div>로그인 처리 중...</div>;
};
```
### 3. 백엔드 구현
#### 사용자 프로필 동기화
새 사용자가 소셜 로그인으로 가입할 때 추가 정보를 저장하는 Supabase 함수:
```sql
-- Supabase 함수로 auth.users 테이블에서 core.users로 사용자 정보 동기화
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO core.users (id, email, name, auth_provider, auth_provider_id, avatar_url)
VALUES (
NEW.id,
NEW.email,
COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.email),
NEW.provider,
NEW.provider_id,
NEW.raw_user_meta_data->>'avatar_url'
);
-- 기본 서비스 접근 권한 부여 (예: 처음 가입한 서비스)
INSERT INTO core.service_access (user_id, service_id, access_level)
SELECT
NEW.id,
id,
'user'
FROM core.services
WHERE name = 'budget'; -- 예시: 적자 탈출 가계부를 기본 서비스로 설정
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 트리거 설정
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();
```
#### 서비스 간 인증 상태 공유
쿠키 또는 localStorage를 사용한 SSO 세션 관리:
```javascript
// ZELLYY Core API 서비스
export const checkAndRefreshSession = async () => {
const { data, error } = await supabase.auth.getSession();
if (error || !data.session) {
// 세션이 없거나 만료됨
return { isAuthenticated: false };
}
// 세션 활성 연장
if (data.session.expires_at - Date.now() / 1000 < 3600) {
// 만료 1시간 전이면 갱신
await supabase.auth.refreshSession();
}
return {
isAuthenticated: true,
user: data.session.user,
accessibleServices: await getUserServices(data.session.user.id)
};
};
// 사용자의 접근 가능한 서비스 목록 조회
const getUserServices = async (userId) => {
const { data, error } = await supabase
.from('service_access')
.select(`
service:service_id (
id,
name,
description,
api_endpoint
),
access_level
`)
.eq('user_id', userId);
if (error) throw new Error('서비스 접근 권한 조회 실패');
return data.map(item => ({
...item.service,
accessLevel: item.access_level
}));
};
```
## 통합 사용자 경험
### 1. 최초 로그인
1. 사용자가 어느 ZELLYY 서비스에서든 "구글로 로그인" 버튼 클릭
2. 구글 OAuth 인증 화면으로 리다이렉트
3. 인증 후 ZELLYY Core로 리다이렉트
4. 최초 로그인인 경우:
- 사용자 프로필 정보 저장
- 접근한 서비스에 대한 권한 부여
- 환영 화면 표시
5. 로그인 완료 후 원래 서비스로 리다이렉트
### 2. 서비스 간 전환
사용자가 이미 로그인한 상태에서 다른 ZELLYY 서비스 접근 시:
1. 서비스는 로컬 스토리지 또는 쿠키에서 토큰 확인
2. ZELLYY Core API에 토큰 검증 요청
3. 유효한 토큰이면 자동 로그인 처리
4. 접근 권한이 없는 서비스의 경우 권한 요청 화면 표시
### 3. 로그아웃
1. 어느 서비스에서든 로그아웃 시 ZELLYY Core 로그아웃 API 호출
2. 모든 서비스에서 인증 토큰 삭제
3. SSO 세션 종료
## 보안 고려사항
1. **PKCE 인증 흐름**: OAuth에 PKCE(Proof Key for Code Exchange) 사용
2. **토큰 관리**: 짧은 수명의 액세스 토큰과 리프레시 토큰 사용
3. **HTTPS**: 모든 통신은 HTTPS로 암호화
4. **CORS 설정**: API에 적절한 CORS 정책 적용
5. **CSP(Content Security Policy)**: 적절한 CSP 헤더 설정
## 다음 단계
1. 구글 및 애플 개발자 계정 설정 및 OAuth 클라이언트 등록
2. Supabase Auth 설정 및 테스트
3. 로그인 UI 컴포넌트 개발
4. 소셜 로그인 통합 테스트
5. 서비스 간 SSO 흐름 테스트
```