fetchToken exchanges username+password for a Taiga auth token via POST /auth
()
| 63 | |
| 64 | // fetchToken exchanges username+password for a Taiga auth token via POST /auth |
| 65 | func (tc *TaigaConn) fetchToken() (string, errors.Error) { |
| 66 | endpoint := strings.TrimSuffix(tc.Endpoint, "/") |
| 67 | // strip /api/v1 suffix to get base, then re-add /api/v1/auth |
| 68 | authURL := endpoint + "/auth" |
| 69 | |
| 70 | body, e := json.Marshal(map[string]string{ |
| 71 | "type": "normal", |
| 72 | "username": tc.Username, |
| 73 | "password": tc.Password, |
| 74 | }) |
| 75 | if e != nil { |
| 76 | return "", errors.Default.WrapRaw(e) |
| 77 | } |
| 78 | |
| 79 | resp, e := http.Post(authURL, "application/json", bytes.NewReader(body)) //nolint:noctx |
| 80 | if e != nil { |
| 81 | return "", errors.Default.WrapRaw(e) |
| 82 | } |
| 83 | defer resp.Body.Close() |
| 84 | |
| 85 | if resp.StatusCode != http.StatusOK { |
| 86 | return "", errors.Default.New(fmt.Sprintf("taiga auth failed with status %d", resp.StatusCode)) |
| 87 | } |
| 88 | |
| 89 | var result map[string]interface{} |
| 90 | if e := json.NewDecoder(resp.Body).Decode(&result); e != nil { |
| 91 | return "", errors.Default.WrapRaw(e) |
| 92 | } |
| 93 | // Taiga returns auth_token (v5) or token (v6) |
| 94 | for _, key := range []string{"auth_token", "token"} { |
| 95 | if t, ok := result[key]; ok { |
| 96 | if token, ok := t.(string); ok && token != "" { |
| 97 | return token, nil |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | // fallback: read raw body hint |
| 102 | raw, _ := io.ReadAll(bytes.NewReader(body)) |
| 103 | return "", errors.Default.New(fmt.Sprintf("taiga auth response missing token field, body: %s", string(raw))) |
| 104 | } |
| 105 | |
| 106 | // TaigaConnection holds TaigaConn plus ID/Name for database storage |
| 107 | type TaigaConnection struct { |
no test coverage detected