Google účet lze také podobně jako například u Windows Live nebo Facebook využít pro přihlašování do vlastního webu nebo aplikace. Google toto umožnuje buď pomoci OpenID (Google OpenID provider) nebo pomoci OAuth 2.0 protokolu (nástupce OAuth 1.0, které bylo pro přihlašování na Google již od 20.4.2012 označeno za deprecated). Zde je příklad na přihlášení do ASP.NET aplikace pomoci Google OAuth 2.0.
Registrace aplikace
Princip této Goole OAuth 2.0 autentizace je velice podobný jak jsme si uvedli u příkladu na přihlašování pomoci Facebooku nebo v příkladu na Window Live ID. Opět nejprve musíme provést vytvoření a nastavení vlastní aplikace, tentokrát pro vybraný Google účet. To se provádí ve službě Google apis tímto odkazem:
https://accounts.google.com/ServiceLogin?service=devconsole&passive=1209600&continue=https://code.google.com/apis/console/&followup=https://code.google.com/apis/console/
Po přihlášení se přepneme na záložku API Access a vytvoříme novou aplikaci (Create Client ID). Zvolíme Web application a nastavíme Your site or hostname a Redirect URI. Nelze zde ovšem zadat libovolná adresa, ale pouze buď doména nebo lze použít localhost pro testování, například tedy:
Redirect URIs: http://localhost/GoogleLoginSampleWeb/oauth2callback
JavaScript origins: http://localhost
nebo
Redirect URIs: http://mojedomena.com/GoogleLoginSampleWeb/oauth2callback
JavaScript origins: http://mojedomena.com
Po založení je vám vygenerováno Client ID a Client secret.
Rozchození příkladu
Kompletní ASP.NET web site příkladu je k dispozici ke stažení zde. Pro jeho rozběhnutí musíme doplnit naše Client ID a Client secret do konstant v souboru Global.cs.
Ještě upozorním, že v příkladu není použita žádná externí knihovna, ačkoliv některé open source projekty existují (Google .NET Client API nebo přímo DotNetOpenAuth), přišlo mi, že konstrukci OAuth 2.0 requestů pro přihlášení je jednodušší provést přímo.
Volání OAuth 2.0 pro přihlášení webové aplikace
Projdeme si některý kód volání OAuth 2.0 pro přihlášení webové aplikace (Web Server flow) obsažený ve třídě GoogleAuthClient v příkladu. Jak již jsem psal, postup je velice podobný jako OAuth 2.0 přihlašení v případě Facebooku, takže hlavně upozorním na rozdíly.
1. Přihlašovací Url
Google OAuth 2.0 Login URL vypadá takto:
https://accounts.google.com/o/oauth2/auth?state=profile&response_type=code&redirect_uri=http%3a%2f%2flocalhost%2fGoogleLoginSampleWeb%2foauth2callback&client_id=000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&approval_prompt=auto&scope=https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/userinfo.email
Podobně jako u Facebooku i zde pro webovou aplikaci volíme response_type typu code (volbu token by jsme opět využili spíše pro desktop aplikace). Dále ve volání již typicky uvádíme parametr redirect_uri a client_id naší aplikace.
Nový parametr je zde approval_prompt, ten může mít buď hodnotu auto nebo force. Zatímco při nastavení force je stránka pro potvrzení práv aplikace zobrazena vždy, při hodnotě auto se naopak stránka nezobrazuje v případě, že požadovanou aplikaci již uživatel jednou potvrdil. (Přístup k aplikaci může být uživatelem na této adrese ručně odstraněn).
Posledním parametrem je scope, ten určuje seznam požadovaných práv, ale na rozdíl od Facebook aplikací, zde máme pouze dvě hodnoty: profile – přístup k základním údajům profilu uživatele (ID, jméno, fotografie, URL, atd.) a email – přístup k email adrese uživatele (hodnoty se zadávají ve formě URL adresy).
Google Developers oficiální stránky popisující formát přihlašovacího URL zde. Kód na sestavení URL v příkladu najdete v metodě BuildLoginUrl.
2. Zpracování odpovědi
Po úspěšném přihlášení a autorizování aplikace (user consent) je volání vráceno na předané redirect_uri. V příkladu je URL GoogleLoginSampleWeb/oauth2callback, který je ve web.config nastaven na handler třídu OAuth2CallbackHandler. Odsud je zavolána metoda OnAuthenticateCompleted, ve které se vyzvedne code z odpovědi, která vypadá například takto:
http://localhost/GoogleLoginSampleWeb/oauth2callback?state=profile&code=0/xxx-xxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Dále je metodou GetAccessTokenForCode provedeno volání pro získání user access token. Kód vypadá takto:
private static GoogleOAuthResult GetAccessTokenForCode(string code, string clientID, string clientSecret, string redirectUri)
{
string body = string.Format(cRequestAccessTokenPostBodyTemplate, code, HttpUtility.UrlEncode(clientID), HttpUtility.UrlEncode(clientSecret), HttpUtility.UrlEncode(redirectUri));
var request = WebRequest.Create(cRequestAccessTokenUrl);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
try
{
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(body);
}
var response = request.GetResponse();
if (response != null)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
try
{
var serializer = new DataContractJsonSerializer(typeof(RequestAccessTokenResponse));
var responseData = (RequestAccessTokenResponse)serializer.ReadObject(responseStream);
if (responseData != null)
{
return new GoogleOAuthResult(responseData.AccessToken, DateTimeOffset.UtcNow.AddSeconds((double)Int64.Parse(responseData.ExpiresIn)));
}
}
catch (FormatException)
{
return null;
}
}
}
}
catch (WebException)
{
//Ignore exception
}
catch (IOException)
{
//Ignore exception
}
return null;
}
Zde je operací POST zavolán request na adresu https://accounts.google.com/o/oauth2/token. V body volání jsou předány parametry code, client_id, client_secret, redirect_uri a grant_type s hodnotou authorization_code. Odpověď je vrácena ve formátu JSON a je zpracována pomoci třídy DataContractJsonSerializer, tím jsou načteny vrácené hodnoty access_token a expires_in (více zde).
Pozn.: Pokud při tomto volání obdržíte chybu 500 - Internal server error jako já, tak jí nejprve zkuste odstranit tím, že si v Google profilu vygenerujete nové ClientID a ClientSecret.
3. Volání Google API
Příklad ASP.NET aplikace si udržuje stejně jako u v případě Windows Live ID nebo Facebooku získaný AccessToken v cookie. Odsud je vyzvednut a použit pro volání Google API na získání informací o přihlášeném uživateli. Kód je ve třídě GoogleAuthClient v metodě RequestUserInfo a vypadá takto:
private static GoogleAuthUser RequestUserInfo(string accessToken)
{
string url = string.Format(cUserInfoUrlTemplate, accessToken);
var request = WebRequest.Create(url);
try
{
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
if (response != null)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
try
{
var serializer = new DataContractJsonSerializer(typeof(AuthUser));
var user = (AuthUser)serializer.ReadObject(responseStream);
if (user != null)
{
return new GoogleAuthUser(user.ID, user.Name, user.GivenName, user.FamilyName, user.Picture, user.Gender, user.Locale,
!string.IsNullOrEmpty(user.Email) && user.VerifiedEmail ? user.Email : null, user.link);
}
}
catch (FormatException)
{
return null;
}
}
}
}
catch (WebException)
{
//Ignore exception
}
catch (IOException)
{
//Ignore exception
}
return null;
}
Je provedeno GET volání na adresu:
https://www.googleapis.com/oauth2/v1/userinfo?access_token=xxxxxxxxxxxxxxxxxxxxxxxx
Tím je vrácena JSON odpověď, která obsahuje tyto pole (více zde):
- id – ID přihlášeného uživatele .
- email – Email uživatele, je vrácen pouze pokud bylo aplikací požadováno a potvrzeno právo scope https://www.googleapis.com/auth/userinfo.email.
- verified_email – bool příznak, zda byl vrácený email ověřen.
- name – Celé jméno přihlášeného uživatele.
- given_name – Křestní jméno přihlášeného uživatele (může být null v případě nezadání uživatelem do profilu).
- family_name – Příjmení přihlášeného uživatele (může být null v případě nezadání uživatelem do profilu).
- picture – URL na profilový obrázek uživatele (může být null v případě nezadání uživatelem do profilu).
- locale – Registrované locale uživatele, např: ”cs" (může být null v případě nezadání uživatelem do profilu).
- timezone – Časová zóna přihlášeného uživatele (může být null).
- gender – Pohlaví (male, female) uživatele (může být null v případě nezadání uživatelem do profilu).
- link – URL na Google+ profil uživatele, pokud ho má uživatel vytvořen.
Přihlášení jako jiný uživatel
Třída GoogleAuthClient dále obsahuje metodu SignOut pro odhlášení, ta ovšem pouze odstraní držený cookie s access tokenem v naší aplikaci. Nepovedlo se mi provést volání na Google pro odhlášení (jako tomu bylo například v příkladu na Facebook).
Pokud se potřebujeme přihlásit jako jiný uživatel, můžeme provést volání přihlášení pomoci approval_prompt force (viz výše), kde je možné se odhlásit. Druhou možností je přímo zavolat přihlášení jako jiný uživatel (vynutí nejprve odhlášení případně přihlášeného uživatele). To lze provést voláním URL
https://accounts.google.com/o/logout?continue=https://accounts.google.com/o/oauth2/auth?
kde předáme stejný query string jako v případě normálního přihlášení (ale je zde nutné ho předat encodovaně).
Celé URL tedy může vypadat takto:
https://accounts.google.com/o/logout?continue=https://accounts.google.com/o/oauth2/auth?state%3Dprofile%26response_type%3Dcode%26redirect_uri%3Dhttp%253a%252f%252flocalhost%252fGoogleLoginSampleWeb%252foauth2callback%26client_id%3D000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com%26approval_prompt%3Dauto%26scope%3Dhttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%2520https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email
Získání tohoto URL je v příkladu implementováno vlastností LoginDifferentUser.