Add authentication
Breaking changes: removed WithCookie and WithXCsrfToken
This commit is contained in:
parent
3d364abaac
commit
eb18988829
7 changed files with 236 additions and 33 deletions
172
auth.go
Normal file
172
auth.go
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package twitterscraper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
)
|
||||
|
||||
const (
|
||||
loginURL = "https://api.twitter.com/1.1/onboarding/task.json"
|
||||
bearerToken2 = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
|
||||
)
|
||||
|
||||
type flow struct {
|
||||
Errors []struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"errors"`
|
||||
FlowToken string `json:"flow_token"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (s *Scraper) getFlowToken(data map[string]interface{}) (string, error) {
|
||||
headers := http.Header{
|
||||
"Authorization": []string{"Bearer " + s.bearerToken},
|
||||
"Content-Type": []string{"application/json"},
|
||||
"User-Agent": []string{"Mozilla/5.0 (Linux; Android 11; Nokia G20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.88 Mobile Safari/537.36"},
|
||||
"X-Guest-Token": []string{s.guestToken},
|
||||
"X-Twitter-Auth-Type": []string{"OAuth2Client"},
|
||||
"X-Twitter-Active-User": []string{"yes"},
|
||||
"X-Twitter-Client-Language": []string{"en"},
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequest("POST", loginURL, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header = headers
|
||||
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var info flow
|
||||
err = json.NewDecoder(resp.Body).Decode(&info)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(info.Errors) > 0 {
|
||||
return "", fmt.Errorf("auth error (%d): %v", info.Errors[0].Code, info.Errors[0].Message)
|
||||
}
|
||||
|
||||
return info.FlowToken, nil
|
||||
}
|
||||
|
||||
// IsLoggedIn check if scraper logged in
|
||||
func (s *Scraper) IsLoggedIn() bool {
|
||||
return s.isLogged
|
||||
}
|
||||
|
||||
// Login to Twitter
|
||||
func (s *Scraper) Login(username string, password string) error {
|
||||
s.setBearerToken(bearerToken2)
|
||||
|
||||
err := s.GetGuestToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flow start
|
||||
data := map[string]interface{}{
|
||||
"flow_name": "login",
|
||||
"input_flow_data": map[string]interface{}{
|
||||
"flow_context": map[string]interface{}{
|
||||
"debug_overrides": map[string]interface{}{},
|
||||
"start_location": map[string]interface{}{"location": "splash_screen"},
|
||||
},
|
||||
},
|
||||
}
|
||||
flowToken, err := s.getFlowToken(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flow instrumentation step
|
||||
data = map[string]interface{}{
|
||||
"flow_token": flowToken,
|
||||
"subtask_inputs": []map[string]interface{}{
|
||||
{
|
||||
"subtask_id": "LoginJsInstrumentationSubtask",
|
||||
"js_instrumentation": map[string]interface{}{"response": "{}", "link": "next_link"},
|
||||
},
|
||||
},
|
||||
}
|
||||
flowToken, err = s.getFlowToken(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flow username step
|
||||
data = map[string]interface{}{
|
||||
"flow_token": flowToken,
|
||||
"subtask_inputs": []map[string]interface{}{
|
||||
{
|
||||
"subtask_id": "LoginEnterUserIdentifierSSO",
|
||||
"settings_list": map[string]interface{}{
|
||||
"setting_responses": []map[string]interface{}{
|
||||
{
|
||||
"key": "user_identifier",
|
||||
"response_data": map[string]interface{}{"text_data": map[string]interface{}{"result": username}},
|
||||
},
|
||||
},
|
||||
"link": "next_link",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
flowToken, err = s.getFlowToken(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flow password step
|
||||
data = map[string]interface{}{
|
||||
"flow_token": flowToken,
|
||||
"subtask_inputs": []map[string]interface{}{
|
||||
{
|
||||
"subtask_id": "LoginEnterPassword",
|
||||
"enter_password": map[string]interface{}{"password": password, "link": "next_link"},
|
||||
},
|
||||
},
|
||||
}
|
||||
flowToken, err = s.getFlowToken(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flow duplication check
|
||||
data = map[string]interface{}{
|
||||
"flow_token": flowToken,
|
||||
"subtask_inputs": []map[string]interface{}{
|
||||
{
|
||||
"subtask_id": "AccountDuplicationCheck",
|
||||
"check_logged_in_account": map[string]interface{}{"link": "AccountDuplicationCheck_false"},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = s.getFlowToken(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.isLogged = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout is reset session
|
||||
func (s *Scraper) Logout() {
|
||||
s.isLogged = false
|
||||
s.guestToken = ""
|
||||
s.client.Jar, _ = cookiejar.New(nil)
|
||||
s.setBearerToken(bearerToken)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue