91 lines
2.4 KiB
Go
91 lines
2.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ExchangeResponse struct {
|
|
ErrorCode string `json:"errorCode"`
|
|
ErrorMsg string `json:"errorMsg"`
|
|
Message string `json:"message"`
|
|
Body map[string]any `json:"body"`
|
|
}
|
|
|
|
func ExchangeCode(client *http.Client, endpoint string, code string, decryptKey string) (Credentials, error) {
|
|
if strings.TrimSpace(endpoint) == "" {
|
|
return Credentials{}, errors.New("exchange endpoint is required")
|
|
}
|
|
if strings.TrimSpace(code) == "" {
|
|
return Credentials{}, errors.New("code is required")
|
|
}
|
|
if strings.TrimSpace(decryptKey) == "" {
|
|
return Credentials{}, errors.New("decrypt key is required")
|
|
}
|
|
if client == nil {
|
|
client = &http.Client{Timeout: 60 * time.Second}
|
|
}
|
|
|
|
body, _ := json.Marshal(map[string]string{"code": code})
|
|
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(body))
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var exchange ExchangeResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&exchange); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
if exchange.ErrorCode != "Success" {
|
|
msg := firstNonEmpty(exchange.ErrorMsg, exchange.Message, exchange.ErrorCode)
|
|
return Credentials{}, fmt.Errorf("exchange failed: %s", msg)
|
|
}
|
|
|
|
ak, err := decryptBodyField(exchange.Body, "ak", decryptKey)
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
sk, err := decryptBodyField(exchange.Body, "sk", decryptKey)
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
token, err := decryptBodyField(exchange.Body, "token", decryptKey)
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
return Credentials{AccessKey: ak, SecretKey: sk, Token: token, SavedAt: time.Now()}, nil
|
|
}
|
|
|
|
func decryptBodyField(body map[string]any, key string, decryptKey string) (string, error) {
|
|
v, ok := body[key]
|
|
if !ok {
|
|
return "", fmt.Errorf("response body missing %s", key)
|
|
}
|
|
s, ok := v.(string)
|
|
if !ok || strings.TrimSpace(s) == "" {
|
|
return "", fmt.Errorf("response body %s is not a string", key)
|
|
}
|
|
return DecryptCredential(strings.TrimSpace(s), decryptKey)
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, v := range values {
|
|
if strings.TrimSpace(v) != "" {
|
|
return v
|
|
}
|
|
}
|
|
return "unknown error"
|
|
}
|