Autentizace pomoci Facebook Server Flow

Jan Holan       09.05.2012       ASP.NET/IIS, HTTP/HTML, Bezpečnost       12059 zobrazení

Před časem jsme na tomto blogu zveřejnili příklad autentizace pomoci Windows Live ID ve webové aplikaci. Podobné přihlášení do ASP.NET aplikace je možné provést pomoci sociální sítě Facebook, zde je ukázáno jak nato.

Tento příklad bude používat přístup nazývaný “server flow authentication” (nebo také server-side flow), jedná se o přihlašování pomoci redirectů v prohlížeči klienta. Druhou možností co Facebook nabízí je totiž autentizace pomoci Facebook JavaScript SDK (tutoriál zde), a zdá se, že tato druhá možnost je (bohužel) dnes více podporovaná. Já jsem ale u ASP.NET aplikací zastánce psaní kódu v C# a ne postavení celého řešení na stránce v JavaScriptu, a tak jsem vypracoval tento příklad.

Nastavení Facebook Aplikace

Podobně jako u Live i zde je nejprve potřeba provést nastavení aplikace na straně vlastního Facebooku. Postup je následující:

  • Přihlásíme se na stránku https://developers.facebook.com/apps.
  • Zvolíme Vytvořit novou aplikaci a vyplníme jméno aplikace.
    Zde se může stát, že se po potvrzení objeví hláška “Váš účet musí být ověřen, předtím než budete moci učinit tento krok. Prosím potvrďte svůj účet přidáním Vašeho telefonního čísla mobilního telefonu nebo kreditní karty kreditní kartu.” (děkujeme Zuckerbergovi). Mě se při přidání čísla telefonu nic neposlalo, ale situaci jsem vyřešil tím, že jsem naopak smazal svoje mobilní číslo zadané v profilu, měl jsem ho tam totiž přidané ještě bez služby Facebook SMS. (Případně pokud máte O2 operátora, tak při správném ověření služby Facebook SMS by založení aplikace mělo také chodit.)
  • Zpět v nastavení aplikace, zde je nutné v základním nastavení kliknout na Webová stránka a zadat URL vaší aplikace. Facebook zde nyní již podporuje i localhost doménu, ale pro tu je nutné použít výchozí port. Pro vývoj a testování naší aplikace tedy, můžeme zadat např.: “http://localhost/FBLoginSampleWeb” (kde FBLoginSampleWeb je jméno virtuálního adresáře na lokálním IIS). Toto pro účely přihlašování stačí, nemusíme zadávat žádné jiné url.
  • Opíšeme si údaje App ID a App Secret, ty při volání API budeme potřebovat. Pro rozchození příkladu je spolu s RedirectUri zadejte v souboru Global.cs.
  • Volitelně můžete aplikaci změnit iconu, na záložce Auth Dialog upravit vzhled potvrzovacího dialogu aplikace a v sekci Rozšířené zadat Kontaktní informace autora aplikace.

Knihovny Facebook SDK

Pro .NET není žádná oficiální SDK knihovna přímo od tvůrců Facebooku (ti tvoří pouze samotné API), ale o .NET knihovnu se stará komunita. Její vývoj prošel řekl bych trochu více divokou historií, posuďte sami. Nejznámější projekt býval Facebook Developer Toolkit, na začátku sponzorovaný také Microsoftem. Projekt obsahuje knihovny (poslední verze je 3.01) pro Winforms, WPF, Web a Silverlight. Používají ale dnes již starší Facebook Rest API.

Tento projekt byl již zrušen a nahradil ho projekt Facebook C# SDK původně na codeplexu. Zde ale dneska nic nestáhnete, protože projekt byl přesunut na Github, stránky projektu jsou nyní http://csharpsdk.org a distribuce posledních verzí knihoven (od verze 5.4) je již pouze přes nuget balíček. Poslední verzi knihovny nainstalujete z Visual Studio Package manager Console příkazem “Install-Package Facebook”, v době tvorby tohoto přikladu to byla verze 6.0.16.

Tento projekt již používá nové OAuth 2.0 a Graph Facebook API (nahradilo starší Rest API), podporuje ASP.NET, Winforms, WPF (FW 3.5, 4.0, 4.5), Silverlight 5, Windows Phone 7.1 , Windows Azure a i Windows 8 Metro Style aplikace. Dokumentace projektu je ještě ale poměrně slabá, navíc jednotlivé verze knihoven nejsou kompatibilní. Například přechod z verze 5.4 a 6.0 je poměrně náročný, protože se změnili třídy a také zde již nejsou webové knihovny Facebook.Web.dll a Facebook.Web.Mvc.dll.

Použití knihovny Facebook C# SDK

Nejdůležitější třídu, kterou v assembly Facebook.dll v. 6 najdeme je FacebookClient. Přes ní můžeme volat requesty na Facebook Graph API. Můžeme rovnou zkusit načíst veřejné (public) informace uživatele facebooku pomoci tohoto kódu:

var fb = new FacebookClient();
var result = (IDictionary<string, object>)fb.Get("jan.holan");

var userInfo = new
{
    UserID = (string)result["id"],
    UserName = (string)result["username"],
    FullName = (string)result["name"],
    Link = (string)result["link"],
    Gender = (string)result["gender"],
    Locale = (string)result["locale"]
};

Pozn:. Já zde ani v celém příkladu nepoužívám datový typ dynamic, na který u tohoto volání v některých kódech narazíte.

Pokud potřebujeme určit, které pole vrátit, můžeme to provést předáním parametru fields takto:

var result = (IDictionary<string, object>)fb.Get("jan.holan", new { fields = "id,username,name,picture" });

Do volání lze předat parametry pomoci anonymní třídy. Zde se nám tedy zavoláním například vrátí i URL na obrázek uživatele, které se standardně nevrací.

Uvedeným kódem ale zatím načteme pouze veřejné informace nějakého uživatele, což je dostupné i bez Authentizace.

Authentizace

V naší aplikaci provedeme přihlášení uživatele k Facebooku pomoci OAuth 2.0 protokolu, tím obdržíme Access Token a pomoci něho budeme moci získat další informace o přihlášeném uživateli.

Nyní si v krocích popíšeme celý postup použité server-side authentizace.

  1. Url pro vyvolání OAuth Dialogu

Login link obsahuje URL na přihlašovací OAuth dialog. Link je získán tímto kódem:

var parameters = new Dictionary<string, object>();
parameters["client_id"] = appId;
parameters["response_type"] = "code";   //Must be code, because token is not returned in query string
parameters["display"] = "page";
parameters["redirect_uri"] = redirectUri;
parameters["scope"] = "email";

var fb = new FacebookClient();
return fb.GetLoginUrl(parameters).AbsoluteUri;

Je zde volána metoda GetLoginUrl objektu FacebookClient. Hodnoty parametrů, které se metodě předávají jsou:

  • client_id - appId z nastavení Facebook aplikace
  • response_type - musí být ve webové aplikaci nastaveno na hodnotu code, protože pouze ten je vrácen v query stringu. Při nastavení na token, by byl vrácen Access Token (který potřebujeme), ale byl by vrácen v URL části fragment (část za znakem #), která se neposílá na server a tudíž není v objektu Request.Url dostupná.
    Pozn.: Nastavení token je výhodné v desktop aplikacích, kde je volání přihlášení v hostovaném WebBrowser kontrolu a tam je pak část fragment načtena (příklad zde).
  • display - protože login dialog voláme redirectem, je výhodné použít nastavení page, pokud přihlášení bylo zobrazováno v jiném okně, je možné uvést hodnotu popup nebo touch (pro mobilní zařízení).
  • redirect_uri - URL kam se  po přihlášení provede redirect zpět do naší aplikace, zde předáváme nějaký handler pro vyzvednutí odpovědi. (Doména URL je kontrolována proti té zadané v nastavení aplikace).
  • scope - práva, které požadujeme od aplikace. My zde předáváme právo email, které po přihlášení umožní načíst email uživatele. Celý seznam práv je uveden zde.
  • state - ještě je možné do volání předat parametr state, jedná se o libovolný string, který je pak vrácen zase zpět v odpovědí. V příkladu není použit.

Výsledné sestavené URL může vypadat následovně: https://www.facebook.com/dialog/oauth?client_id=0000000000000000&response_type=code&display=page&redirect_uri=http%3A%2F%2Flocalhost%2FFBLoginSampleWeb%2FFacebookLoginCallback&scope=email

  1. Autorizace aplikace

Po zavolání vytvořeného linku je uživatel vyzván k přihlášení na Facebook (pokud již není přihlášen), a poté je vyzván k souhlasu s právy (pomoci OAuth dialogu), které aplikace požaduje (v našem případě načtení email uživatele). Tento proces se nazývá authorizace aplikace, pokud již uživatel aplikaci schválil, je tento krok přeskočen.

  1. Odpověď authorizace

Po přihlášení uživatelem je vyvolán redirect zpět do naší aplikace na URL uvedenou parametrem redirect_uri. Zde v případě úspěšného přihlášení obdržíme požadovanou hodnotu code (viz. response_type = code). Volané URL vypadá např. takto: http://localhost/FBLoginSampleWeb/FacebookLoginCallback?code=AQDGXsVkwI7b7mjcgSudTDBR22…

Pro zpracování tohoto URL můžeme použít metodu TryParseOAuthCallbackUrl takto:

var fb = new FacebookClient();
FacebookOAuthResult oauthResult;
if (fb.TryParseOAuthCallbackUrl(HttpContext.Current.Request.Url, out oauthResult))
{
    if (oauthResult.IsSuccess)
    {
        string code = oauthResult.Code;
        ...
    }
}
  1. Načtení Access Token

Nyní získaný code “vyměníme” za Access Token. K tomu použijeme následující volání Graph API:
https://graph.facebook.com/oauth/access_token?client_id=0000000000000000&client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&redirect_uri=http%3A%2F%2Flocalhost%2FFBLoginSampleWeb%2FFacebookLoginCallback&code=AQDGXsVkwI7b7mjcgSudTDBR22…

Volání vrací výsledek v této podobě:
access_token=USER_ACESS_TOKEN&expires=NUMBER_OF_SECONDS_UNTIL_TOKEN_EXPIRES

V příkladu to řeším tímto kódem:

const string RequestAccessTokenUrl = "https://graph.facebook.com/oauth/access_token";

var fb = new FacebookClient();

var parameters = new Dictionary<string, object>();
parameters["client_id"] = appId;
parameters["client_secret"] = appSecret;
parameters["code"] = code;
parameters["redirect_uri"] = redirectUri;

var data = (IDictionary<string, object>)fb.Post(RequestAccessTokenUrl, parameters);

//Parse results to FacebookOAuthResult object
string responseData = string.Format("access_token={0}&expires_in={1}", data["access_token"], data["expires"]);
FacebookOAuthResult oauthResult = fb.ParseOAuthCallbackUrl(new Uri(redirectUri + "#" + responseData, UriKind.Absolute));

return oauthResult;

Pro volání používám metodou POST na objektu FacebookClient, výsledky jsou vráceny v Dictionary, ve které jsou dostupné jako data["access_token"], data["expires"]. Zbývající část uvedeného kódu řeší pouze to, abych data vrátil v objektu FacebookOAuthResult (a ten lze zkonstruovat pouze metodou ParseOAuthCallbackUrl ze sestaveného Uri).

Pozn.: Dříve toto řešila metoda ExchangeCodeForAccessToken třídy FacebookOAuthClient (ve verzi assembly 5.4). Tato metoda nyní ve verzi 6 mě z neznámého důvodu již chybí.

  1. Načtení dat uživatele pomoci Graph API

Pomoci načteného Access token nyní konečně můžeme provést načtení požadovaných údajů přihlášeného uživatele. K tomu slouží speciální volání “me”. V kódu to vypadá následovně:

var fb = new FacebookClient(accessToken);
var result = (IDictionary<string, object>)fb.Get("me",
    new { fields = "id,username,name,first_name,last_name,picture,link,gender,locale,email" });

Všimněte si ještě, že díky autentizaci a předaní práva scope aplikaci můžeme zde navíc načíst i email uživatele.

Celý proces server flow authentikace je také více popsán na stránkách facebook developers zde. O dalších možnostech volání pomoci FacebookClient třídy v. 6 se můžete dočíst zde.

Příklad FBLoginSampleWeb

Hlavní kód aplikace přihlášení na Facebook je implementován ve třídě FacebookAuthClient, která obsahuje výše uvedený kód pro autentizaci. Celou třídu si můžete prohlédnout zde. Access Token přihlášeného uživatele je pak udržován v cookie, podobně jako tomu bylo u příkladu s Windows Live ID. Po přihlášení uživatele aplikace nabízí možnost odhlášení Sign Out.

FBLoginSampleWeb1

FBLoginSampleWeb2

Zdrojové kódy kompletního webového adresáře příkladu si můžete stáhnout zde.

Příště přihlášení pomoci Google.


Pozn.: Příklad neřeší zjištění, zda je uživatel již na Facebook přihlášen (v tomto scénáři to ani není potřeba). Pomoci server flow authentikace to není možné provádět, je k tomu zapotřebí použít Facebook JavaScript SDK funkci FB.getLoginStatus. Kód je k dispozici například zde a zde.

Pozn.: Kromě knihovny Facebook C# SDK ještě existuje jiný komunitní projekt Facebook Graph Toolkit 3.0, jedná se knihovnu pro volání Facebook Graph API a FQL (Facebook Query Language) dotazů.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

RE: Autentizace pomoci Facebook Server Flow

Zdá se že na Facebook něco změnili, Picture se nyní nevrací jako string, ale jako Json.

V konstruktoru třídy FacebookAuthUser je potřeba načtení PictureUrl následovně upravit:

[quote]var pictureJson = data[AuthUserConstants.Picture] as Facebook.JsonObject;

if (pictureJson != null)

{

this.PictureUrl = (string)((Facebook.JsonObject)pictureJson["data"])["url"];

}

else

{

this.PictureUrl = data[AuthUserConstants.Picture] as string;

}[/quote]

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Autentizace pomoci Facebook Server Flow

Doplněno, díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Autentizace pomoci Facebook Server Flow

Ahoj, díky za super článek. Konečně dobře a srozumitelně popsaný problém, na který se naráží velmi často.

A pak ještě jeden drobný návrh, možná bych v ukázkovém kódu rozepsal konstantu "RequestAccessTokenUrl", která je definovaná pouze v kódu na stažení, nikoliv ve článku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback