forked from baron/baron-sso
105 lines
8.4 KiB
Markdown
105 lines
8.4 KiB
Markdown
## Baron SSO 백엔드 인증 로직 상세 분석
|
|
|
|
Baron SSO 백엔드는 Descope IDP(Identity Provider)를 활용하여 사용자 인증 및 비밀번호 관리를 처리합니다. 주요 로직은 `backend/internal/handler/auth_handler.go`에 구현되어 있으며, Descope Go SDK를 통해 Descope API와 상호작용합니다.
|
|
|
|
### 4.1. 주요 컴포넌트
|
|
|
|
* **`AuthHandler`:** Fiber 웹 프레임워크의 요청을 처리하는 핸들러. `DescopeClient`와 `IdpProvider` (DescopeProvider 구현체)를 포함합니다.
|
|
* **`DescopeClient` (`github.com/descope/go-sdk/descope/client`):** Descope API와의 통신을 담당하는 SDK 클라이언트.
|
|
* **`IdpProvider` (`backend/internal/idp/factory.go` -> `backend/internal/service/descope_service.go`):** DescopeClient를 래핑하여 Descope 관련 인증 작업을 추상화한 인터페이스.
|
|
|
|
### 4.2. 비밀번호 재설정 흐름 (Mermaid Diagram)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Frontend
|
|
participant Backend as AuthHandler
|
|
participant Descope as Descope API
|
|
participant InternalServices as 이메일/SMS
|
|
|
|
Frontend->>AuthHandler: POST /api/v1/auth/password/reset/initiate (loginId)
|
|
AuthHandler->>AuthHandler: 비밀번호 재설정 시작 요청 유효성 검증
|
|
AuthHandler->>Descope: IdpProvider.InitiatePasswordReset(loginId, redirectURL)
|
|
note over Descope: Descope가 loginId로 재설정 링크 이메일 발송<br/>(링크는 백엔드의 /password/reset/verify를 가리킴)
|
|
Descope-->>AuthHandler: 성공 또는 오류 반환
|
|
alt 재설정 시작 성공 시
|
|
AuthHandler->>AuthHandler: 성공 로그 기록
|
|
AuthHandler-->>Frontend: 200 OK (비밀번호 재설정 링크가 성공적으로 전송되었습니다.)
|
|
else 재설정 시작 실패 시
|
|
AuthHandler->>AuthHandler: Descope 오류 로그 기록
|
|
AuthHandler-->>Frontend: 500 Internal Server Error (비밀번호 재설정 시작 실패)
|
|
end
|
|
|
|
User->>이메일/SMS: 재설정 링크 클릭
|
|
이메일/SMS->>Backend: GET /api/v1/auth/password/reset/verify?t={token} (브라우저 리다이렉트)
|
|
Backend->>Backend: 중간 HTML 페이지 제공 (POST 폼 포함)
|
|
Backend-->>Frontend: 200 OK (HTML 폼)
|
|
|
|
Frontend->>Backend: POST /api/v1/auth/password/reset/verify (폼에서 전달된 token)
|
|
Backend->>Backend: 폼 데이터에서 토큰 추출
|
|
Backend->>Descope: IdpProvider.VerifyPasswordResetToken(token)
|
|
Descope-->>Backend: 인증 정보 (세션/리프레시 토큰) 또는 오류 반환
|
|
alt 토큰 검증 성공 시
|
|
Backend->>Backend: 리프레시 토큰으로 DSRF 쿠키 설정
|
|
Backend->>Backend: JWT 페이로드에서 loginId 추출
|
|
Backend->>Frontend: /reset-password?loginId={loginId}로 리다이렉트
|
|
else 토큰 검증 실패 시 (예: 토큰 만료)
|
|
Backend->>Backend: Descope 오류 로그 기록
|
|
Backend->>Frontend: /login?error=invalid_token으로 리다이렉트
|
|
end
|
|
|
|
Frontend->>Backend: POST /api/v1/auth/password/reset/complete (loginId, newPassword)
|
|
Backend->>Backend: 요청 본문 및 비밀번호 정책 유효성 검증
|
|
Backend->>Descope: DescopeClient.Management.User().SetPassword(loginId, newPassword)
|
|
note over Descope: DESCOPE_MANAGEMENT_KEY 권한 필요
|
|
Descope-->>Backend: 성공 또는 오류 반환
|
|
alt 비밀번호 업데이트 성공 시
|
|
Backend->>Backend: 성공 로그 기록
|
|
Backend-->>Frontend: 200 OK (비밀번호가 성공적으로 재설정되었습니다.)
|
|
else 비밀번호 업데이트 실패 시
|
|
Backend->>Backend: Descope 오류 로그 기록 (예: 만료된 비밀번호, 정책 위반)
|
|
Backend-->>Frontend: 500 Internal Server Error (비밀번호 업데이트 실패)
|
|
end
|
|
```
|
|
|
|
### 4.3. 각 단계 동작 설명
|
|
|
|
이 섹션에서는 위 다이어그램에 나타난 주요 비밀번호 재설정 단계별 동작을 상세히 설명합니다.
|
|
|
|
**1. `POST /api/v1/auth/password/reset/initiate` (비밀번호 재설정 시작)**
|
|
* **요청:** 프론트엔드에서 사용자 `loginId`를 포함하여 백엔드로 비밀번호 재설정 시작 요청을 보냅니다.
|
|
* **백엔드 (`AuthHandler.InitiatePasswordReset`):**
|
|
* 요청 본문(`loginId`)의 유효성을 검사합니다.
|
|
* `FRONTEND_URL` 환경 변수를 사용하여 Descope가 재설정 링크에 포함할 `redirectURL` (백엔드의 `/api/v1/auth/password/reset/verify` 엔드포인트)을 구성합니다.
|
|
* `h.IdpProvider.InitiatePasswordReset(loginId, redirectURL)` 메서드를 호출하여 Descope에 비밀번호 재설정 링크 발송을 위임합니다. (`IdpProvider`는 내부적으로 `DescopeClient.Auth.Password().SendResetPasswordLink()`를 호출합니다.)
|
|
* **Descope API:** Descope는 제공된 `loginId`에 해당하는 이메일 주소로 재설정 링크를 포함한 이메일을 발송합니다. 이 링크에는 사용자를 `redirectURL`로 안내하고, 비밀번호 재설정 토큰이 포함됩니다.
|
|
* **응답:**
|
|
* **성공 시:** `Password reset link sent successfully.` 메시지와 함께 200 OK 응답을 프론트엔드에 반환합니다.
|
|
* **실패 시:** Descope 오류를 로그로 기록하고 500 Internal Server Error 응답을 프론트엔드에 반환합니다.
|
|
|
|
**2. `GET /api/v1/auth/password/reset/verify` (재설정 링크 클릭 후 중간 페이지)**
|
|
* **요청:** 사용자가 Descope가 보낸 재설정 이메일의 링크를 클릭하면, 브라우저는 이 백엔드 엔드포인트로 `token` 쿼리 파라미터와 함께 GET 요청을 보냅니다.
|
|
* **백엔드 (`AuthHandler.VerifyPasswordResetPage`):**
|
|
* 링크 스캐너가 토큰을 소비하는 것을 방지하기 위한 중간 페이지를 HTML 형태로 반환합니다. 이 페이지에는 `token`을 숨긴 POST 폼이 포함되어 있습니다.
|
|
* **응답:** POST 폼이 포함된 HTML 페이지를 프론트엔드에 반환합니다.
|
|
|
|
**3. `POST /api/v1/auth/password/reset/verify` (비밀번호 재설정 토큰 검증)**
|
|
* **요청:** 중간 페이지의 "계속하기" 버튼을 클릭하면 브라우저는 숨겨진 `token`을 포함하여 이 백엔드 엔드포인트로 POST 요청을 보냅니다.
|
|
* **백엔드 (`AuthHandler.ProcessPasswordResetToken`):**
|
|
* 폼 데이터에서 `token`을 추출합니다.
|
|
* `h.IdpProvider.VerifyPasswordResetToken(token)` 메서드를 호출하여 Descope에 토큰 검증을 위임합니다. (내부적으로 `DescopeClient.Auth.MagicLink().Verify()` 또는 유사한 토큰 검증 API를 사용)
|
|
* **Descope API:** 토큰의 유효성을 검증하고, 성공 시 `AuthenticationInfo` (세션/리프레시 토큰, 사용자 정보)를 반환하거나 실패 시 오류를 반환합니다.
|
|
* **응답:**
|
|
* **성공 시:** Descope로부터 받은 리프레시 토큰을 `DSRF` 쿠키로 설정하고, 세션 JWT에서 `loginId`를 추출하여 `https://sso-test.hmac.kr/reset-password?loginId={loginId}` URL로 프론트엔드에 리다이렉트합니다.
|
|
* **실패 시:** Descope 오류를 로그로 기록하고 `https://sso-test.hmac.kr/login?error=invalid_token` URL로 프론트엔드에 리다이렉트합니다.
|
|
|
|
**4. `POST /api/v1/auth/password/reset/complete` (비밀번호 재설정 완료)**
|
|
* **요청:** 프론트엔드 (리다이렉트된 `/reset-password` 페이지)에서 새로운 `newPassword`와 `loginId`를 포함하여 백엔드로 비밀번호 재설정 완료 요청을 보냅니다.
|
|
* **백엔드 (`AuthHandler.CompletePasswordReset`):**
|
|
* 요청 본문(`newPassword`)과 쿼리 파라미터(`loginId`)의 유효성을 검사합니다.
|
|
* 비밀번호 정책(길이, 문자 포함)을 직접 검증합니다.
|
|
* `h.DescopeClient.Management.User().SetPassword(context.Background(), loginID, req.NewPassword)` 메서드를 호출하여 Descope에 비밀번호 업데이트를 위임합니다. **이때 `DESCOPE_MANAGEMENT_KEY`의 권한이 매우 중요합니다.**
|
|
* **Descope API:** Descope는 `loginId`에 해당하는 사용자의 비밀번호를 `newPassword`로 업데이트합니다. 이 과정에서 Descope의 비밀번호 정책 및 계정 상태(예: 만료)가 적용됩니다.
|
|
* **응답:**
|
|
* **성공 시:** `Password has been reset successfully.` 메시지와 함께 200 OK 응답을 프론트엔드에 반환합니다.
|
|
* **실패 시:** Descope 오류를 로그로 기록하고 500 Internal Server Error 응답을 프론트엔드에 반환합니다. 현재 `Expired password` 오류가 발생한 지점입니다. |