forked from Gitlink/gitlink-cli
fix: correct GitLink auth to use autologin_trustie cookie
GitLink authenticates via Set-Cookie autologin_trustie, not Bearer token or query param. Login response body contains user info directly (no status wrapper on success). Fixed login to extract cookie from response headers and transport to inject it on subsequent requests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eaa348b0e2
commit
52809e110f
|
|
@ -6,20 +6,22 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gitlink-org/gitlink-cli/internal/config"
|
||||
)
|
||||
|
||||
type LoginResponse struct {
|
||||
UserID int `json:"user_id"`
|
||||
type LoginResult struct {
|
||||
Username string `json:"username"`
|
||||
Login string `json:"login"`
|
||||
Token string `json:"token"`
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
UserID int `json:"user_id"`
|
||||
// Error fields (only present on failure)
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Login authenticates with username/password and stores the token.
|
||||
func Login(username, password string) (*LoginResponse, error) {
|
||||
// Login authenticates with username/password and stores the autologin cookie.
|
||||
func Login(username, password string) (*LoginResult, error) {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load config: %w", err)
|
||||
|
|
@ -31,8 +33,15 @@ func Login(username, password string) (*LoginResponse, error) {
|
|||
}
|
||||
bodyJSON, _ := json.Marshal(body)
|
||||
|
||||
url := cfg.BaseURL + "/accounts/login"
|
||||
resp, err := http.Post(url, "application/json", bytes.NewReader(bodyJSON))
|
||||
loginURL := cfg.BaseURL + "/accounts/login"
|
||||
req, err := http.NewRequest("POST", loginURL, bytes.NewReader(bodyJSON))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
|
|
@ -43,20 +52,30 @@ func Login(username, password string) (*LoginResponse, error) {
|
|||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
var result LoginResponse
|
||||
var result LoginResult
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
if result.Token == "" {
|
||||
msg := result.Message
|
||||
if msg == "" {
|
||||
msg = "login failed, no token returned"
|
||||
}
|
||||
return nil, fmt.Errorf("%s", msg)
|
||||
// Check for error response (has negative status)
|
||||
if result.Status < 0 {
|
||||
return nil, fmt.Errorf("%s", result.Message)
|
||||
}
|
||||
|
||||
if err := StoreToken(result.Token); err != nil {
|
||||
// Extract autologin_trustie cookie
|
||||
autologinToken := ""
|
||||
for _, cookie := range resp.Cookies() {
|
||||
if strings.Contains(strings.ToLower(cookie.Name), "autologin") {
|
||||
autologinToken = cookie.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if autologinToken == "" {
|
||||
return nil, fmt.Errorf("login succeeded but no autologin cookie received")
|
||||
}
|
||||
|
||||
if err := StoreToken(autologinToken); err != nil {
|
||||
return nil, fmt.Errorf("failed to store token: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
// Transport wraps an http.RoundTripper and injects the Bearer token header.
|
||||
// Transport wraps an http.RoundTripper and injects the autologin token.
|
||||
type Transport struct {
|
||||
Base http.RoundTripper
|
||||
}
|
||||
|
|
@ -12,7 +12,11 @@ type Transport struct {
|
|||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
token, err := LoadToken()
|
||||
if err == nil && token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
// GitLink authenticates via cookie or query param
|
||||
req.AddCookie(&http.Cookie{Name: "autologin_trustie", Value: token})
|
||||
q := req.URL.Query()
|
||||
q.Set("token", token)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
if req.Body != nil && req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
|
|
|||
Loading…
Reference in New Issue