You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
현재 백엔드에 인증이 없습니다. foundation reset에서 Spring Security와 SecurityConfig를 의도적으로 제거했고, 개인의 장소 기록·위치 데이터를 다루는 서비스라 본인 식별과 보호 경로가 필요합니다.
BFF와 리소스 서버를 한 서버로 운영하기로 확정했고, IETF draft-ietf-oauth-browser-based-apps가 개인 데이터를 다루는 애플리케이션에 BFF를 "strongly recommended"로 두면서 브라우저 쿠키에 Secure·HttpOnly를 MUST로 요구하는 데 따라 토큰을 JS에 노출하지 않는 쿠키 방식을 택했습니다.
인증 도입과 로그인 기능은 같은 SecurityConfig를 수정하고 JWT 서명키·클레임 규약을 공유하므로 분리할 수 없어 한 티켓으로 묶습니다. 다만 provider 3사를 한 번에 넣으면 과대해지므로 Google 하나로 골격을 완성하고 나머지는 후속 티켓으로 갑니다.
배경 / 목적
현재 백엔드에 인증이 없습니다. foundation reset에서 Spring Security와
SecurityConfig를 의도적으로 제거했고, 개인의 장소 기록·위치 데이터를 다루는 서비스라 본인 식별과 보호 경로가 필요합니다.BFF와 리소스 서버를 한 서버로 운영하기로 확정했고, IETF
draft-ietf-oauth-browser-based-apps가 개인 데이터를 다루는 애플리케이션에 BFF를 "strongly recommended"로 두면서 브라우저 쿠키에Secure·HttpOnly를 MUST로 요구하는 데 따라 토큰을 JS에 노출하지 않는 쿠키 방식을 택했습니다.인증 도입과 로그인 기능은 같은
SecurityConfig를 수정하고 JWT 서명키·클레임 규약을 공유하므로 분리할 수 없어 한 티켓으로 묶습니다. 다만 provider 3사를 한 번에 넣으면 과대해지므로 Google 하나로 골격을 완성하고 나머지는 후속 티켓으로 갑니다.Jira (필수)
[BE] 소셜 로그인 및 계정 관리상위/관련 GitHub Issue
out-of-order설정 (별도 PR, 아래 참고)상세 내용
설계 근거와 클라이언트 계약은 11_인증_설계, 엔드포인트 계약은 08_API_명세 §1·§3에 있습니다.
확정된 값
HttpOnly+Secure+SameSite=Lax쿠키Path/api/core/v1/auth(재발급·로그아웃 모두 포함, 일반 API 제외)XSRF-TOKEN쿠키 →X-XSRF-TOKEN헤더, 실패 403logged_in=1비-HttpOnly쿠키/auth/callback(실패 시?error=OAUTH_FAILED)범위
spring-boot-starter-security-oauth2-client의존성 (Boot 4에서 starter 이름이 바뀌었습니다)social_account마이그레이션 —core.member는 이미 있습니다(feat(S15P11A705-41): member 엔티티 + BaseEntity(created_at·soft delete) (도메인 스캐폴딩 3/3) #25). 번호는 member·auth 구간인V50SocialAccount엔티티 —BaseEntity상속,@Table(schema = "core"), 부분 유니크UNIQUE(provider, provider_user_id) WHERE deleted_at IS NULLGET /auth/google/login,GET /auth/google/callback(redirect URI에 context path/api/core포함)social_account있으면 로그인, 없으면member+social_account생성 후 로그인. 약관 동의는 프론트 전담Set-Cookie. 본문 없음POST /auth/refresh— 쿠키 기반, 204, 회전 발급POST /auth/logout— Refresh 쿠키로 해당 세션만 무효화, 204SecurityConfig— 공개 경로(actuator health·prometheus, 인증 엔드포인트, api-docs)와 보호 경로 구분@LoginMember MemberPrincipal. 리졸버 본문만 교체하면 되도록 (member·인증 파트와 core 도메인의 접점 인터페이스 선합의 3건 #28 합의)AuthenticationEntryPoint·AccessDeniedHandler가 직접ApiResponse를 작성 — 필터 체인은@RestControllerAdvice를 타지 않습니다TraceIdFilter가 Security 필터 체인보다 먼저 실행되어야traceId가null이 되지 않습니다이미 끝난 것 — 이 티켓 범위 아님
Jira 설명에는 남아 있지만 docs#12가 머지되어 완료된 항목입니다.
08_API_명세개정 (§1.1 쿠키 인증, §1.7 CSRF, §1.8 표시 쿠키, §2.1/me/agreements삭제, §3.2 콜백, §3.3 재발급, §3.4 로그아웃)SameSite=Lax확정Jira 설명의 "OAuth state 쿠키는 Lax, 인증 쿠키는 Strict로 분리"도 폐기됐습니다. same-origin이라 전부
Lax입니다.할 일
V50__social_account.sql+SocialAccount엔티티·리포지토리MemberPrincipal+@LoginMember+LoginMemberArgumentResolverlogged_in포함)/auth/refresh,/auth/logoutSecurityConfig+ CSRF + EntryPoint·AccessDeniedHandlerErrorCode.FORBIDDEN(403) 추가 —UNAUTHORIZED(401)가 아직 없으면 함께api-conventions.md에v1반영 (현재 예시가@RequestMapping("/v1/...")방식과 어긋남)X-Debug-Member-Id스텁이 머지되어 있으면 제거 — 조건부authentication.md갱신 + ADR 기록완료 조건
member와social_account가 생성되고 인증 쿠키가 발급된다member가 추가 생성되지 않고 쿠키만 발급된다HttpOnly·Secure·SameSite속성이 모두 있고,logged_in만HttpOnly가 아니다Path가/api/core/v1/auth로 제한되어 일반 API 요청에 실리지 않는다/actuator/health와/actuator/prometheus는 인증 없이 접근된다POST /auth/refresh가 204와 함께 새 쿠키를 발급하고, 회전 전 Refresh 재사용은 401POST /auth/logout이 204를 반환하고, 이후 같은 Refresh로 재발급하면 401{success:false, error:{...}})를 따르고traceId가null이 아니다V50__social_account.sql이 빈 PostgreSQL DB에서 성공한다(provider, provider_user_id)재INSERT가 성공한다 (부분 유니크)./gradlew clean check --no-daemon통과docs/development/authentication.md가 실제 구현과 일치한다