package keycloak import ( "antrian-operasi/internal/config" "context" "encoding/json" "fmt" "log" "net/http" "net/url" "strings" ) type IKeycloakService interface { FetchTokenUsingRefreshToken(c context.Context, refreshToken string) (*TokenResponse, error) } type KeycloakService struct { config config.KeycloakConfig } func NewKeycloakService(cfg config.KeycloakConfig) IKeycloakService { return KeycloakService{cfg} } func (s KeycloakService) FetchTokenUsingRefreshToken(c context.Context, refreshToken string) (*TokenResponse, error) { refreshTokenUrl := s.config.BaseUrl + "/realms/" + s.config.Realm + "/protocol/openid-connect/token" bodyRequest := url.Values{} bodyRequest.Set("grant_type", "refresh_token") bodyRequest.Set("client_id", s.config.Audience) bodyRequest.Set("client_secret", s.config.SecretKey) bodyRequest.Set("refresh_token", refreshToken) client := &http.Client{} req, err := http.NewRequestWithContext(c, http.MethodPost, refreshTokenUrl, strings.NewReader(bodyRequest.Encode())) if err != nil { log.Printf("error request token %s", err) } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") resp, err := client.Do(req) if err != nil { log.Printf("error response %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { var errResp TokenErrorResponse json.NewDecoder(resp.Body).Decode(&errResp) return nil, fmt.Errorf("Keycloak error : %s - %s", errResp.Error, errResp.ErrorDescription) } var tokenResp TokenResponse err = json.NewDecoder(resp.Body).Decode(&tokenResp) if err != nil { return nil, err } return &tokenResp, nil }