Files
satusehat-worker/internal/worker/auth.go
T
2026-04-26 08:06:57 +00:00

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
}