초기 커밋
This commit is contained in:
251
ZELLYY/zellyy core/sso-implementation.md
Normal file
251
ZELLYY/zellyy core/sso-implementation.md
Normal 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 흐름 테스트
|
||||
```
|
||||
Reference in New Issue
Block a user