HTTP - logowanie do facebook

0

Witajcie,

Robię aplikację na desktopa, która ma pewne informacje z FB pokazać na pulpicie windowsa. Finalnie mi się to udało jednak sposób logowania jaki zrobiłem ... pozostawia wiele do życzenia. Może wy macie pomysł jak zrobić to inaczej/lepiej?

W mojej aplikacji wygląda to tak, że jest sobie na formatce schowany komponent TWebbrowser. Po wprowadzeniu loginu, hasła i naciśnięciu "loguj" odpalam taki skrypt:

(kodu jest więcej bo są dodatkowe flagi zabezpieczające ale z grubsza wygląda to tak jak poniżej)

 
procedure TfmMain.bLogujClick(Sender: TObject);
begin
  gLoaded:= true;
  wbPage.Navigate('https://www.facebook.com');
end;

procedure TfmMain.wbPageDocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
  if gLoaded then
  begin
    gLoaded := False;
    wbPage.OleObject.document.all.tags('input').item('email').value := edLogin.Text;
    wbPage.OleObject.document.all.tags('input').item('pass').value := edPass.Text;
    wbPage.OleObject.document.all.item('u_0_b',0).click;
  end
  else
    GetFromFB;
  end;
end;

Pozostałe informacje z FB pobieram za pomocą np takiego kodu:

function TfmMain.GetFBName: string;
const
  URL = 'http://graph.facebook.com';
var
  slPostData: TStringList;
  IdHTTP: TidHTTP;
begin
  IdHTTP := TIdHTTP.Create(Self);
  IdHTTP.HandleRedirects:= True;

  slPostData:= TStringList.Create;
  try
    IdHTTP.Request.ContentType:= 'multipart/form-data;';
    Result := IdHTTP.Get(URL+'/xxx?fields=id,name');   //xxx to numer konta FB, który wyciągam z wbPage
  finally
    idHTTP.Free;
  end;
end;

Ta operacja zwraca mi odpowiedni plik JSON z wszystkim interesującymi mnie informacjami

Co prawda na facebook.com/developers jest opisany sposób logowania za pomocą JavaScript, PHP na iOS, Androida itd ale nie udało mi się tego uruchomić w Delphi za pomocą HTTP i metody GET/POST

0

Może ktoś coś jeszcze doradzi, ale w swoim programie http://olesio.eu/fb - którego głównie katuje @Marooned też logowanie i pobieranie danych zrobiłem po HTTP. Nie trzeba wtedy aplikacji instalować jako dodatkowej usługi i inne takie kombinacje jak autoryzacja naszej aplikacji itp. Tylko oczywiście nie tykałem indyka, a jak wiecie bardziej preferowane przeze mnie Synapse. Tylko, że ja aby pobrać listę userów, właśnie wchodziłem na stronę graphu i pobierałem z treści kodu html tymczasowego "access token" (tego u Ciebie nie widzę, więc może są jakieś dane które go nie potrzebują), a później żądane dane. Parsowanie JSONa i wyciąganie danych o fotkach ze stron użytkowników dokonywałem za pomocą modułu do wyrażeń regularnych, bo tak było mi najłatwiej.

Ogólnie z FB praktycznie nie korzystam i nie ogarniam jego niuansów, czasami kogoś dodam do znajomych i sprawdze Jego zdjęcia itp. Jednak nie sidzę tam non stop i nie żyje tymi nieco jak dla mnie wirtualnymi bzdetami. Wolę zaglądać intensywniej na 4p :) Przynajmniej można podzielić się z Wami czasami wiedzą i czasem czegoś nauczyć lub dowiedzieć. Także to pytanie do uzależnionych od tego portalu.

A ja założyłem tam konto, tylko po to aby sklecić pierwsze wersje tej aplikacji dla mojego bratanka, który o to pytał. Później programem po moim wspomnieniu o nim kiedyś tutaj, zainteresował się @Marooned i swoimi testami zachęcał do rozwijania. Także poprawiłem co się dało, dodałem pełne wsparcie dla UNICODE i jakoś to działą. Ostatnio jednak zauważyłem, że czasami u niektórych naszych znajomych liczba opublikowanych zdjęć w widoku albumów jest znacznie większa od tego co później możemy stamtąd pobrać. Nie analizowałem jeszcze tego, bo @Marooned twierdzi że testy wykazują iż wszystko jest u Niego ok. Być może trzeba by dopasować alternatywne wyrażenia do wydobycia danych zdjęć w niektórych albumach. A być może jest to i jakaś suma w zależności od sufixu w nazwie oryginalnego pliku ze zdjęciem, co świadczy o jego jakości (czyli "_o", "_n" i ewentualnie inne jeśli są).

0

A widzisz olesio to w podobnej sytuacji jesteśmy. Mnie moja znajoma poprosiła o aplikację na desktopa do FB. Niestety ja nawet konta nie miałem do teraz i zmuszony byłem do jego założenia. W obecnej wersji ja wyciągam tylko dane o użytkowniku (login, id, itp.) a finalnie będę potrzebował jedynie jakieś wiadomości (ilość i treść) ale jeszcze się do tego nie zabrałem. Nie do końca wiem o co chodzi z tym "access token" dlatego pytałem o inne sposoby logowania niż tak jak u mnie za pomocą TWebBrowera.

1

Nie precyzyjnie napisałem. Access Token to ciąg znaków, który można wydobyć nie z GraphApi tylko z treści strony dla Developerów: http://developers.facebook.com - znajduja się on między pewnymi znacznikami. Mozna go wydobyć taką funkcją najszybciej jak poniżej, jeżli kod strony siedzi w zmiennej FPage.

function SimpleParse(StrBegin, StrEnd, Str : string) : string;
var
  B, E : integer;
begin
  Result := '';
  if StrBegin = '' then
  begin
    B := 1;
  end
  else
  begin
    B := Pos(StrBegin, Str);
  end;
  if B > 0 then
  begin
    Str := Copy(Str, B + Length(StrBegin), MaxInt);
    if StrEnd = '' then
    begin
      E := Length(Str) + 1;
    end
    else
    begin
      E := Pos(StrEnd, Str);
    end;
    if E > 0 then
    begin
      Result := Copy(Str, 1, E - 1);
    end;
  end;
end;

Przykład użycia dla wydobycia Access Tokena na FB:

    FAccesstoken := SimpleParse('"init",', '",["apps.', FPage);
    FAccesstoken := SimpleParse(',"', '', FAccesstoken);

Jeżeli zmienna będzie pusta mimo braku w kodzie strony komunikatów o błędnym logowaniu i innych to najprawdopodobniej FaceBook pokazał stronę żądającą potwierdzenia lokalizacji. Zdarza się to na FB u mnie na przykład kiedy wiele razy wchoziłem z mojego łącza w UPC, a nagle zmienili mi IP na innym po długim czasie. Wtedy trzeba albo się babrać z obsługę przycisku do potiwerdzenia (za pewne wysyła coś POSTem). Ja ponieważ rzadko się to zdarza przewidziałem na tę okoliczność komunikat dla usera proszący aby sam się zalogował przez przeglądarkę www i kliknął co trzeba. Wiem, że to lame trochę, ale cóż począć jak błąd występuje bardzo rzadko.

Jeszcze wrócę do access tokena. Jest on potrzebny do pobrania listy własnych znajomych ze strony Graph Api. Bo normalnie pisząc jakoś - nie ogarniam tego jak i nie potrzebuje wiedzieć - aplikacje jako usługę FaceBookową sami otrzymamy taki access token na podstawie jakichś tam bzdetów po rejestracji naszej aplikacji na FB. Wadą jest jednak to że potrzeba potwierdzenia przez użytkownika i wielu kombinacji. Może miało to ukrócić pisanie szkodników, ale co z tego jak przez socjotechniczne sposoby i tak ludzie instalują jakieś bzdety co późnej spamują ich tablice i ich znajomych bzdetami.

Więćej nie jestem w stanie nic doradzić. Jak może widziałeś moja aplikacja jako tako działa. Na stronie devloperki możesz sprawdzić jakie dane są zwracane przez ten ich moduł do postowania zapytań i otrzymywania odpowiedzi w postaci JSON'a.

0

Miałeś rację Olesio przy wywołaniu ?fields=id,name,outbox.fields(from,message,unread,unseen) dostałem BadRequest. Przypuszczam, że to wina tego tokena bo w Graphic Tool Explorerze to działa bez problemu. Jednakże po pobraniu źródła strony developers.facebook.com twoja funkcja zwróciła mi '' zamiast ciągu znaków ...

0

To nie wiem, jeżeli logowałeś się prawidłowo to powinieneś uzyskac Token, o ile przy logowaniu nie masz wspomnianej informacji o potrzebie potwierdzenia zmiany IP. Pokaże na szybko kod do logowania jaki u siebie używam w wątku, oczywiście z wykorzystaniem Synapse i dllek do HTTPS:

//...
const
  Redir_C = 'Location: ';
  Base_Url = 'https:'#47#47'www.facebook.com/';
  Graph_Url_SSL = 'https:'#47#47'graph.facebook.com/';
  Default_MimeType = 'application/x-www-form-urlencoded';
  Opera_UserAgent = 'Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.12.388 Version/12.11';

function TMainForm.Logon(UserEmail, UserPassword : string) : boolean;
const
  Developers_Url = 'http:'#47#47'developers.facebook.com';
  Tools_Path_Friends = '/tools/explorer?method=GET&path=me/friends';
  ACharset_Test = '%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84';
  Error_Text0 = 'Za często próbujesz. Spróbuj ponownie później.';
  Error_Text1 = 'Nieprawidłowy adres e-mail';
  Error_Text2 = 'Nieprawidłowa nazwa użytkownika';
  Error_Text3 = 'Wprowadzone hasło jest nieprawidłowe';
  Error_Text4 = 'Wykryto wielokrotne próby zalogowania się do Twojego konta';
  Error_Text5 = 'Zaktualizuj informacje dotyczące bezpieczeństwa';
  Error_Text6 = 'Aby wyświetlić tę stronę, musisz się zalogować';
var
  I, RedirPos : integer;
  LgnRndStr, LsdStr, RedirUrl : string;
begin
  Result := False;
  with SynHttp do
  begin
    Cookies.Clear;
    Headers.Clear;
    Document.Clear;
    UpdateCurrentStepGBCaption(0);
    FUrl := Base_Url;
    HTTPMethod('GET', FUrl);
    FPage := StreamToStr(Document);
    LsdStr := SimpleParse('name="lsd" value="', '"', FPage);
    LgnRndStr := SimpleParse('name="lgnrnd" value="', '"', FPage);
    UpdateCurrentStepGBCaption(1);
    Headers.Clear;
    FUrl := Base_Url + '/login.php?next=' + Developers_Url + Tools_Path_Friends;
    FStrData := 'lsd=' + LsdStr + '&email=' + UserEMail + '&pass=' + UserPassword +
      '&persistent=1&default_persistent=1' + '&charset_test=' + ACharset_Test +
      '&timezone=&lgnrnd=' + LgnRndStr + '&lgnjs=n&locale=pl_PL';
    Document.Write(PChar(FStrData)^, Length(FStrData));
    HTTPMethod('POST', FUrl);
    FPage := StreamToStr(Document);
    if Pos(Error_Text0, FPage) > 0 then
    begin
      AlreadyLogged := Result;
      SetFocusToEditFields(PasswordEdit);
      MessageBox(Application.Handle, PChar(Error_Text0),
        PChar(Application.Title), MB_ICONWARNING + MB_OK);
      Exit;
    end;
    if Pos(Error_Text1, FPage) > 0 then
    begin
      AlreadyLogged := Result;
      SetFocusToEditFields(UserEmailEdit);
      MessageBox(Application.Handle, PChar(Error_Text1 + '!'),
        PChar(Application.Title), MB_ICONERROR + MB_OK);
      Exit;
    end;
    if Pos(Error_Text2, FPage) > 0 then
    begin
      AlreadyLogged := Result;
      SetFocusToEditFields(UserEmailEdit);
      MessageBox(Application.Handle, PChar(Error_Text2 + '!'),
        PChar(Application.Title), MB_ICONERROR + MB_OK);
      Exit;
    end;
    if Pos(Error_Text3, FPage) > 0 then
    begin
      AlreadyLogged := Result;
      SetFocusToEditFields(PasswordEdit);
      MessageBox(Application.Handle, PChar(Error_Text3 + '!'),
        PChar(Application.Title), MB_ICONERROR + MB_OK);
      Exit;
    end;
    if Pos(Error_Text4, FPage) > 0 then
    begin
      MessageBox(Application.Handle, PChar(Error_Text4 + '! ' +
        'Zaloguj się do konta tymczasowo przez przeglądarkę WWW. ' +
        'Jeżeli to nie pomoże, odczekaj kilka minut i spróbuj ponownie. ' +
        'Błąd ten związany jest z zabezpieczeniami stosowanymi przez ' +
        'portal FaceBook. A metoda logowania stosowana przez program, ' +
        'nie wymaga za to konieczności zmiany ustawień swojego konta i ' +
        'dodania ' + Application.Title + ' do akceptowanych aplikacji.'),
        PChar(Application.Title), MB_ICONERROR + MB_OK);
      Exit;
    end;
    while (ResultCode = 301) or (ResultCode = 302) do
    begin
      for I := 0 to Headers.Count - 1 do
      begin
        RedirPos := Pos(Redir_C, Headers[I]);
        if RedirPos > 0 then
        begin
          RedirUrl := Copy(Headers[I], RedirPos + Length(Redir_C), MaxInt);
          if Pos(':443/', RedirUrl) > 0 then
          begin
            RedirUrl := StringReplace(RedirUrl, ':443/', '/', []);
            RedirUrl := StringReplace(RedirUrl, 'http:'#47#47, 'https:'#47#47, []);
          end;
          Headers.Clear;
          HTTPMethod('GET', RedirUrl);
          Break;
        end;
      end;
    end;
    UpdateCurrentStepGBCaption(2);
    FPage := StreamToStr(Document);
    FAccesstoken := SimpleParse('"init",', '",["apps.', FPage);
    FAccesstoken := SimpleParse(',"', '', FAccesstoken);
    if FAccesstoken = '' then
    begin
      if Pos(Error_Text5, FPage) > 0 then
      begin
        CopyTextToClipBoard(FUrl);
        MessageBox(Application.Handle, PChar(Error_Text5 + '. ' +
          'Zaloguj się do konta tymczasowo przez przeglądarkę WWW pod adres: ' +
          FUrl + #32 + 'Adres zostal skopiowany do Schowka.'),
          PChar(Application.Title), MB_ICONERROR + MB_OK);
      end
      else
      begin
        if Pos(Error_Text6, FPage) > 0 then
        begin
          MessageBox(Application.Handle,
            PChar('Prawdopodobnie Twoje hasło zostało zmienione przez FaceBook. ' +
            'Sprawdź swoje konto e-mail używane z FaceBookiem aby uzyskać ' +
            'nowe hasło i zaloguj się używając go'),
            PChar(Application.Title), MB_ICONERROR + MB_OK);
        end
        else
        begin
          MessageBox(Application.Handle,
            PChar('Nieoczekiwany błąd. Nie mozna pobrać wymaganego tokena. ' +
            'Upewnij się, czy posiadasz aktywne połączenie z Internetem. ' +
            'Sprawdź również, czy jest ono prawidłowo skonfigurowane oraz ' +
            'czy jakiś program nie wpływa na możliwości połączeń protokołem ' +
            'HTTP. Ewentualnie zaloguj się na FaceBook przez przeglądarkę ' +
            'WWW i sprawdź, czy Twoje konto nie jest tymczasowo blokowane. ' +
            'Taka sytuacja może mieć czasami miejsce w przypadku gdy zmieni ' +
            'się Twój adres IP, z którego wcześniej często się logowałeś.'),
            PChar(Application.Title), MB_ICONERROR + MB_OK);
        end;
      end;
      Exit;
    end;
    Result := True;
  end;
end;
//...
0

A widzisz Olesio trochę się nie zrozumieliśmy :)

Napisałęś:
"Access Token to ciąg znaków, który można wydobyć nie z GraphApi tylko z treści strony dla Developerów: http://developers.facebook.com", a z kodu widzę, że jednak z GraphApi:

const
  Developers_Url = 'http:'#47#47'developers.facebook.com';
  Tools_Path_Friends = '/tools/explorer?method=GET&path=me/friends';
...
FUrl := Base_Url + '/login.php?next=' + Developers_Url + Tools_Path_Friends; 

Czyli najnormalniej w świecie po logowaniu przechodzisz do strony (Tools_Path_Friends ) i parsujesz kod strony (tak jak ja) dzięki Tobie wiem czego szukać. Pytanie mam zatem nieco inne. W Twoim kodzie jest pobierana wartość me/friends i przykładowo na moim koncie (nowym) nie mam żadnych grantów nadanych. Czy oznacza to, że bez ręcznego granta na koncie usera nie da się tych danych pobrać?

0

Olesio pytanie co dalej? Udało mi się pobrać ten token. Pytanko jak to teraz zaimplementować?

0

Ten access token pozwala wyświetlic takie same dane jak ten - nie wiem jak to nazwać - demo podgląd zapytan w Graph Api. Fragment przykładowego kodu do pobierania listy znajomych. Pewne inne dane też się da pobrać. Ale nie pamiętam już coś na pewno wymaga zezwolenia usera. Trzeba zezwolić takiej aplikacji na dostep. Jak wspominałem. Moj program pobiera takie dane, które można sparsować z podstron i do ktorych wystarczy access token.

//...
procedure TMainForm.GetOwnFriends;
var
  I : integer;
  LI : TTntListItem;
begin
  UpdateCurrentStepGBCaption(3);
  with SynHttp do
  begin
    FUrl := Graph_Url_SSL + Friends_Path + FAccesstoken;
    Headers.Clear;
    Document.Clear;
    HTTPMethod('GET', FUrl);
    FPage := StreamToStr(Document);
  end;
  UpdateCurrentStepGBCaption(4);
  FriendsList := ExtractFriendsData(FPage);
  ViewerLV.Items.BeginUpdate;
  ViewerLV.Items.Clear;
  for I := 0 to FriendsList.Count - 1 do
  begin
    FBFriend := TFBFriend(FriendsList[I]);
    LI := ViewerLV.Items.Add;
    LI.Caption := FBFriend.FullName;
    LI.SubItems.Add(FBFriend.ID);
    LI.ImageIndex := 1;
  end;
  ViewerLV.Items.EndUpdate;
  FriendsList.Free;
//...

1 użytkowników online, w tym zalogowanych: 0, gości: 1