diff --git a/internal/auth/login.go b/internal/auth/login.go index d4817e8..4e50f5a 100644 --- a/internal/auth/login.go +++ b/internal/auth/login.go @@ -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) } diff --git a/internal/auth/transport.go b/internal/auth/transport.go index d4c81e7..e5f9dad 100644 --- a/internal/auth/transport.go +++ b/internal/auth/transport.go @@ -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")