@startuml
!theme mono

title 사용자 인증 내부 시퀀스 (AFR-USER-010)

participant "API Gateway<<E>>" as Gateway
participant "UserController" as Controller
participant "AuthService" as Service
participant "LdapAuthenticator" as LdapAuth
participant "LDAP<<E>>" as LDAP
participant "JwtTokenProvider" as TokenProvider
participant "UserRepository" as Repository
database "PostgreSQL<<E>>" as DB
database "Redis Cache<<E>>" as Cache

Gateway -> Controller: POST /api/v1/auth/login\n{username, password}
activate Controller

Controller -> Service: authenticate(username, password)
activate Service

Service -> LdapAuth: validateCredentials(username, password)
activate LdapAuth

LdapAuth -> LDAP: bind(dn, password)
note right
  LDAP 인증:
  - DN: cn={username},ou=users,dc=company,dc=com
  - Protocol: LDAPS (636)
  - Timeout: 5s
end note

alt 인증 성공
    LDAP --> LdapAuth: authentication success

    LdapAuth -> LDAP: searchUser(username)
    note right
      사용자 정보 조회:
      - cn (이름)
      - mail (이메일)
      - department (부서)
      - title (직급)
    end note

    LDAP --> LdapAuth: user attributes

    LdapAuth --> Service: UserDetails
    deactivate LdapAuth

    Service -> Repository: findByUsername(username)
    activate Repository
    Repository -> DB: 사용자 정보 조회\n(사용자명 기준)

    alt 사용자 존재
        DB --> Repository: user data
    else 신규 사용자
        Repository -> DB: 신규 사용자 등록\n(사용자명, 이메일, 부서)
        DB --> Repository: user created
        note right
          LDAP 정보 동기화:
          - 자동 사용자 등록
          - 프로필 정보 저장
        end note
    end

    Repository --> Service: User entity
    deactivate Repository

    Service -> TokenProvider: generateAccessToken(user)
    activate TokenProvider

    TokenProvider -> TokenProvider: createClaims(user)
    note right
      JWT Claims:
      - sub: userId
      - username
      - roles
      - exp: 1h
    end note

    TokenProvider -> TokenProvider: signToken(claims, secretKey)
    TokenProvider --> Service: access token
    deactivate TokenProvider

    Service -> TokenProvider: generateRefreshToken(user)
    activate TokenProvider

    TokenProvider -> TokenProvider: createRefreshClaims(user)
    note right
      Refresh Token:
      - sub: userId
      - type: refresh
      - exp: 7d
    end note

    TokenProvider --> Service: refresh token
    deactivate TokenProvider

    Service -> Cache: storeRefreshToken(userId, refreshToken)
    note right
      Redis 저장:
      - Key: refresh:{userId}
      - Value: refreshToken
      - TTL: 7d
    end note
    Cache --> Service: stored

    Service -> Repository: updateLastLogin(userId)
    activate Repository
    Repository -> DB: 최종 로그인 일시 업데이트\n(현재시각)
    DB --> Repository: updated
    Repository --> Service: updated
    deactivate Repository

    Service --> Controller: AuthResponse\n{accessToken, refreshToken, user}
    deactivate Service

    Controller --> Gateway: 200 OK\n{tokens, userInfo}
    deactivate Controller

else 인증 실패
    LDAP --> LdapAuth: authentication failed
    LdapAuth --> Service: AuthenticationException
    deactivate LdapAuth

    Service -> Repository: incrementFailedAttempts(username)
    activate Repository
    Repository -> DB: 실패 횟수 증가\n(failed_attempts + 1)

    alt 실패 횟수 초과 (5회)
        Repository -> DB: 계정 잠금 설정\n(잠금시간=현재+30분)
        note right
          계정 잠금:
          - 5회 실패 시
          - 30분 잠금
        end note
    end

    DB --> Repository: updated
    Repository --> Service: updated
    deactivate Repository

    Service --> Controller: AuthenticationException
    deactivate Service

    Controller --> Gateway: 401 Unauthorized
    note right
      에러 응답 형식:
      {
        "error": {
          "code": "AUTHENTICATION_FAILED",
          "message": "인증에 실패했습니다",
          "details": "사용자명 또는 비밀번호가 올바르지 않습니다",
          "timestamp": "2025-10-23T12:00:00Z",
          "path": "/api/v1/auth/login"
        }
      }
    end note
    deactivate Controller
end

@enduml
