151 lines
4.8 KiB
Go
151 lines
4.8 KiB
Go
package worker
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"service/internal/auth"
|
|
"service/pkg/logger"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// apiLoginResponse is a helper struct to unmarshal the nested API response.
|
|
type apiLoginResponse struct {
|
|
Data auth.LoginResponse `json:"data"`
|
|
}
|
|
|
|
// login performs the initial login to get the first set of tokens.
|
|
func (m *Manager) login(ctx context.Context) error {
|
|
loginURL := fmt.Sprintf("%s/auth/login", strings.TrimRight(m.cfg.SatuSehat.InternalFHIRServerURL, "/"))
|
|
|
|
reqBody := map[string]string{
|
|
"email": "[email protected]",
|
|
"password": "password123",
|
|
}
|
|
payloadBytes, _ := json.Marshal(reqBody)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", loginURL, bytes.NewBuffer(payloadBytes))
|
|
if err != nil {
|
|
return fmt.Errorf("gagal membuat login request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
logger.Default().Info("[WORKER AUTH] Melakukan login untuk mendapatkan token awal...", logger.String("url", loginURL))
|
|
|
|
client := &http.Client{Timeout: 15 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("gagal melakukan login request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Baca body untuk logging jika terjadi error
|
|
bodyBytes, readErr := io.ReadAll(resp.Body)
|
|
if readErr != nil {
|
|
return fmt.Errorf("gagal membaca response body login: %w", readErr)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("login gagal dengan status: %d, response: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var apiResp apiLoginResponse
|
|
if err := json.Unmarshal(bodyBytes, &apiResp); err != nil {
|
|
return fmt.Errorf("gagal decode response login: %w, body: %s", err, string(bodyBytes))
|
|
}
|
|
loginResp := apiResp.Data
|
|
|
|
if loginResp.AccessToken == "" || loginResp.RefreshToken == "" {
|
|
logger.Default().Error("[WORKER AUTH] Token kosong diterima dari server", logger.Any("response", loginResp))
|
|
return fmt.Errorf("access token atau refresh token kosong diterima dari server")
|
|
}
|
|
|
|
m.accessToken = loginResp.AccessToken
|
|
m.refreshToken = loginResp.RefreshToken
|
|
logger.Default().Info("[WORKER AUTH] Berhasil mendapatkan token awal.")
|
|
return nil
|
|
}
|
|
|
|
// refresh uses the refresh_token to get a new access_token.
|
|
func (m *Manager) refresh(ctx context.Context) error {
|
|
refreshURL := fmt.Sprintf("%s/auth/refresh", strings.TrimRight(m.cfg.SatuSehat.InternalFHIRServerURL, "/"))
|
|
|
|
if m.refreshToken == "" {
|
|
return fmt.Errorf("tidak ada refresh token untuk digunakan, akan mencoba login ulang")
|
|
}
|
|
|
|
reqBody := auth.RefreshTokenRequest{
|
|
RefreshToken: m.refreshToken,
|
|
Provider: "jwt",
|
|
}
|
|
payloadBytes, _ := json.Marshal(reqBody)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", refreshURL, bytes.NewBuffer(payloadBytes))
|
|
if err != nil {
|
|
return fmt.Errorf("gagal membuat refresh request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
logger.Default().Info("[WORKER AUTH] Melakukan refresh token...", logger.String("url", refreshURL))
|
|
|
|
client := &http.Client{Timeout: 15 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("gagal melakukan refresh request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
bodyBytes, readErr := io.ReadAll(resp.Body)
|
|
if readErr != nil {
|
|
return fmt.Errorf("gagal membaca response body refresh: %w", readErr)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("refresh token gagal dengan status: %d, response: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var apiResp apiLoginResponse
|
|
if err := json.Unmarshal(bodyBytes, &apiResp); err != nil {
|
|
return fmt.Errorf("gagal decode response refresh: %w, body: %s", err, string(bodyBytes))
|
|
}
|
|
refreshResp := apiResp.Data
|
|
|
|
if refreshResp.AccessToken == "" {
|
|
logger.Default().Error("[WORKER AUTH] Access token kosong diterima saat refresh", logger.Any("response", refreshResp))
|
|
return fmt.Errorf("access token kosong diterima dari server saat refresh")
|
|
}
|
|
|
|
m.accessToken = refreshResp.AccessToken
|
|
// Update refresh token if a new one is provided
|
|
if refreshResp.RefreshToken != "" {
|
|
m.refreshToken = refreshResp.RefreshToken
|
|
}
|
|
logger.Default().Info("[WORKER AUTH] Berhasil me-refresh token.")
|
|
return nil
|
|
}
|
|
|
|
// GetAccessToken safely retrieves the current access token.
|
|
func (m *Manager) GetAccessToken() string {
|
|
m.tokenMutex.RLock()
|
|
defer m.tokenMutex.RUnlock()
|
|
return m.accessToken
|
|
}
|
|
|
|
// ForceRefreshAndGetToken tries to refresh the token, falls back to login, and returns the new token.
|
|
func (m *Manager) ForceRefreshAndGetToken(ctx context.Context) (string, error) {
|
|
m.tokenMutex.Lock()
|
|
defer m.tokenMutex.Unlock()
|
|
|
|
if err := m.refresh(ctx); err != nil {
|
|
logger.Default().Warn("[WORKER AUTH] Refresh token gagal, mencoba login ulang...", logger.ErrorField(err))
|
|
if loginErr := m.login(ctx); loginErr != nil {
|
|
return "", fmt.Errorf("fallback login juga gagal: %w", loginErr)
|
|
}
|
|
}
|
|
return m.accessToken, nil
|
|
}
|