이슈 #411
진행중Supabase GoTrue 인증 전환 — wallet-based auth 제거 및 GoTrue JWT 통합
설명
개요¶
기존 wallet address 기반 secp256k1 challenge/verify 인증을 Supabase GoTrue(자체 호스팅)로 전환한다.
배경¶
- 기존 구조에서는 wallet private key = 사용자 identity였음
- 모바일 다중 디바이스 환경에서 private key 동기화 불가 → 근본적 문제
- wallet address는 보상 수신 주소(reward destination)로만 사용되어야 하며, 특정 사용자에 종속되지 않아야 함
변경 범위¶
인증 레이어
- GoTrue(supabase/gotrue) 서비스를 docker-compose에 추가 (port 9011)
- JWT 검증을 표준 HMAC-SHA256으로 교체 (
jsonwebtokencrate, GoTrue 호환) -
AuthContext:user_id(GoTrue UUID) +email+token_type("user" | "node") - Node 토큰은 별도 유지 (
typ: "node", sub = node_id)
프로토 / AuthService
-
AuthServiceRPC를GenerateKeypair하나로 축소 (Challenge/Verify/Register 제거) - secp256k1 챌린지는 노드 등록(NodeService)에만 남김
DB 스키마 (migrations/0006_supabase_auth_schema.sql)
-
accounts테이블 재설계:user_id UUID PK(GoTrue sub),email,display_name,profile_icon -
wallets테이블 신설:user_id → wallet_address(1:N, 보상 지갑 목록) -
nodes.owner_user_id UUID컬럼 추가 (노드 소유 사용자 추적) - 기존 wallet address 기반 계정 데이터 DROP
핸들러 변경
-
register_node: GoTrue JWT 필수,owner_user_id= JWT sub -
heartbeat/delete_node:owner_user_id기준 소유권 검증 -
get_user: wallet →wallets테이블 join으로 조회 - Admin HTTP 콘솔: challenge/verify 엔드포인트 제거, GoTrue JWT paste UI 추가
tools/client.py
- Python gRPC 테스트 클라이언트에서 Auth Challenge/Verify 메뉴 제거
GoTrue 설정 (개발)¶
GOTRUE_JWT_SECRET: JWT_SECRET 환경변수와 동일값으로 설정
GOTRUE_MAILER_AUTOCONFIRM: true
GOTRUE_SITE_URL: http://localhost:3000
Sign-up: POST :9011/signup {"email":"…","password":"…"}
Login: POST :9011/token?grant_type=password {"email":"…","password":"…"}
JL James Lee이(가) 6달 전에 변경
구현 완료 (commit:server|8f52241)¶
변경 파일 요약¶
인증 레이어 (src/domain/auth/, src/server/auth_middleware.rs)
-
jsonwebtokencrate 기반 표준 HMAC-SHA256 JWT 검증으로 교체 -
AuthContext:user_id(GoTrue UUID or node_id),email,token_type("user" | "node") - GoTrue 발행 토큰:
typclaim 없음 →token_type = "user" - Node 토큰:
typ: "node",sub = node_id유지 - Public path set에서 Challenge/Verify 제거; RegisterNode는 보호 RPC로 전환
프로토 (proto/alphea/v1/auth.proto)
-
AuthService를GenerateKeypair단일 RPC로 축소
DB 마이그레이션 (migrations/0006_supabase_auth_schema.sql)
-
accounts재설계:user_id UUID PK,email,display_name,profile_icon -
wallets신설:user_id → wallet_address(1:N) -
nodes.owner_user_id UUID추가 - 기존 wallet identity 데이터 DROP
핸들러
-
handler/node.rs:register_node— GoTrue JWT 필수,owner_user_id저장;heartbeat/delete_node—owner_user_id기준 소유권 검증 -
handler/user.rs:get_user— wallets 테이블 join으로 조회 -
handler/auth.rs:GenerateKeypair만 남김
Admin 콘솔 (src/admin/mod.rs)
-
/api/auth/challenge,/api/auth/verify라우트 제거 - 노드 등록에 JWT 필수 적용
-
owner_wallet_address→owner_user_id소유권 검증으로 전환 - Wallet 엔드포인트: wallet 소유권 assertion 제거 (wallet은 보상 주소일 뿐)
- UI: challenge/verify 카드 제거 → GoTrue JWT paste 카드 추가
인프라 (docker-compose.yml)
-
gotrue서비스 추가 (port 9011,supabase/gotrue:v2.158.1)
의존성 (Cargo.toml)
-
sqlxuuid feature 추가 -
jsonwebtoken = "9"추가
남은 작업¶
- GoTrue 연동 통합 테스트 (실 서버 환경)
- 모바일 앱 auth flow 업데이트 (GoTrue SDK 적용)
JL James Lee이(가) 6달 전에 변경
- 상태을(를) 신규에서 해결(으)로 변경되었습니다.
Dev Console & 테스트 클라이언트 GoTrue 인증 적용 (commit:server|442ebd1)¶
tools/client.py¶
Auth 메뉴 교체
- 기존: Challenge → Sign → Verify, EmailRegister, Login (wallet 기반)
- 변경: GoTrue Sign up / Log in (HTTP → GoTrue REST API) + JWT paste
GoTrue 통신은 표준 urllib 사용 (추가 의존성 없음):
POST <gotrue_url>/signup {"email":"…","password":"…"}
POST <gotrue_url>/token?grant_type=password {"email":"…","password":"…"}
로그인 성공 시 access_token, user.id, user.email 상태에 저장.
Node 등록 변경
- GoTrue JWT 없으면 등록 차단 (안내 메시지 출력)
-
NodeChallengeRequest에서owner_wallet_address제거 (node_id만 전송) -
RegisterNode호출 시 user JWT metadata 포함
--gotrue CLI 플래그 추가
- 기본값: localhost →
http://localhost:9011, 원격 TLS →https://<host>:9011 - 기존 state에
_gotrue_url로 저장 (재시작 시 유지)
상태 표시 개선
-
show_state: wallet →email,user_id,node_token표시로 개편 - 메인 메뉴 상단:
wallet=…→email표시
src/admin/mod.rs (Dev Console)¶
- JWT bar placeholder 문구 업데이트
- JWT bar: click-to-copy 동작 추가
- clearAll 시
gotrue-jwtinput도 함께 초기화
JL James Lee이(가) 6달 전에 변경
후속 — 가입 / 로그인 audit 로그 + 클라이언트 IP 추적¶
commit:server|c519434
GoTrue proxy 의 signup/login endpoint 에 audit 로그 추가. 성공/실패 모두 기록 + 요청 IP 정밀 추적.
추가된 로그¶
| Action | 결과 | Fields |
|---|---|---|
| signup | success | email, ip, user_id (GoTrue id) |
| signup | failure | email, ip, error (GoTrue 에러 메시지) |
| login | success | email, ip, user_id |
| login | failure | email, ip, error |
- 모두 WARN 레벨
- Target:
audit::auth(Dev Console 의 target filter 에audit또는auth입력 시 필터링)
Client IP 추출 (client_ip 헬퍼)¶
우선순위:
-
CF-Connecting-IP(Cloudflare) -
X-Real-IP(nginx) -
X-Forwarded-For의 첫 항목 (가장 원래의 client) - axum ConnectInfo 의 connection peer
→ reverse proxy 환경에서도 실제 client 의 IP 를 정확히 캡처. proxy 가 위 헤더들을 덮어쓰는 게 보안상 중요 (클라이언트가 임의로 헤더 보내면 IP spoofing 가능).
Axum Routing 변경¶
admin::run 을 into_make_service_with_connect_info::<SocketAddr>() 로 마운트하여 핸들러가 ConnectInfo<SocketAddr> 를 추출 가능하게 함.
보안 고려¶
- 비밀번호는 절대 로그에 남지 않음 (body 자체를 GoTrue 로 forward 만 하고 로깅엔 email/ip 만)
- 실패 case 도 기록 — brute-force 시도 탐지 가능
- 동일 IP 에서 다수 실패는 audit log 에서 grep 만으로 발견
Dev Console 에서 보는 법¶
- Logs 탭
- Level:
WARN또는ALL - Target filter:
audit(모든 audit) 또는audit::auth(인증만)
검증¶
-
cargo build성공 -
cargo test --lib54 passed -
cargo clippy --all-targetsclean
운영 적용¶
git pull
docker compose up -d --build app
이후 Dev Console Auth 탭에서 Sign Up / Log In → Logs 탭에서 audit 로그 확인 가능.
JL James Lee이(가) 6달 전에 변경
Go 원본 프로젝트(alphea-rpc-next) 로의 백포트 — Phase 1 반영¶
후속 이슈 #434 에서 발생. 본 이슈의 핵심 설계(GoTrue 인증 / accounts.user_id / nodes.owner_user_id / AuthService 축소) 를 그대로 Go 측으로 옮겨왔다.
commit:server|03c6c64
Go 측에 적용된 있어서의 차이¶
- JWT 라이브러리: Rust
jsonwebtoken→ Gogithub.com/golang-jwt/jwt/v4. 동일한 HS256 표준 구현 - Tower Layer → ConnectRPC
connect.UnaryInterceptorFunc. AuthContext 는 동일 구조체로 설계 (UserID/NodeID/Email/TokenType/Wallet) - DB 마이그레이션: Rust 의 0006 그대로 옷아오지 않고, 기존 Go 프로젝트의 멀티-서비스 스키마(accounts/wallets/friends/rewards_log/referral_claims)를 고려해 v12 단일 마이그레이션으로 적용. 구조적으로는 additive ALTER 와 식별권한 TRUNCATE 조합
- Rust 춝 8개 관련 이슈(#411 / #421 / #422 / #424 / #419 / #420) 중 본 이슈 범위만 반영. AGX 회계 재설계(원본 #421)는 Go 백포트의 Phase 2 이슈에서 처리
- Admin Dev Console 의 GoTrue UI(원본 #424)는 Phase 3 이슈에서 처리. 본 커밋은
docker-compose.yml+ Postgres init 스크립트 설정만 동반
제외된 RPC¶
AuthService 의 Challenge / Verify / Login / SetPassword / EmailRegister / EmailRecover 는 proto 에서 완전 제거. 노드 온보딩용 challenge-response 는 NodeService.NodeChallenge + RegisterNode 경로로 유지 (단, RegisterNode 는 이제 user JWT 명시적으로 요구).
HP Henry Park이(가) 6달 전에 변경
후속 docs 정리¶
commit:server|a7b3c4f 에서 GoTrue 도입 이후 documentation 을 정렬:
-
docs/01-auth.md전면 재작성 — GoTrue REST + NodeService 흐름 명시 -
docs/00-overview.md/docs/07-api-reference.md— AuthService 가 GenerateKeypair 만 남은 상태 반영 (60 RPCs) -
docs/06-mobile-sdk.md— 옷 wallet-challenge 흐름이 stale 임 경고 -
CLAUDE.md— 서비스 카운트 동기화 (auth 1, total 60)
#440 의 작업 일부.