2012-07-05 6 views
7

Sto tentando di integrare Google Calendar nella mia applicazione e sto riscontrando alcuni problemi con l'autorizzazione OAuth che trasmette un RefreshToken. Ricevo un AccessToken senza problemi, ma la proprietà RefreshToken è null. Vedere la linea marcata "ERROR qui:" per cui sto avendo il problemaPerché non ricevo un RefreshToken da una richiesta Google OAuth?

regolatore

mio Asp.Net MVC (chiamato OAuthController) è simile al seguente:

public ActionResult Index() 
    { 
     var client = CreateClient(); 
     client.RequestUserAuthorization(new[] { "https://www.googleapis.com/auth/calendar" }, new Uri("http://localhost/FL.Evaluation.Web/OAuth/CallBack")); 

     return View(); 
    } 

    public ActionResult CallBack() 
    { 

     if (string.IsNullOrEmpty(Request.QueryString["code"])) return null; 

     var client = CreateClient(); 

     // Now getting a 400 Bad Request here 
     var state = client.ProcessUserAuthorization(); 

     // ERROR HERE: The RefreshToken is NULL 
     HttpContext.Session["REFRESH_TOKEN"] = Convert.ToBase64String(Encoding.Unicode.GetBytes(state.RefreshToken)); 

     return JavaScript("Completed!"); 
    } 

    private static WebServerClient CreateClient() 
    { 
     return 
      new WebServerClient(
       new AuthorizationServerDescription() 
        { 
         TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"), 
         AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"), 
         ProtocolVersion = ProtocolVersion.V20 
        } 
       , _GoogleClientId, _GoogleSecret); 
    } 

vedo nei documenti API di Google, che Devo assicurarmi che lo access_type richiesto sia impostato su offline per l'invio di un RefreshToken. Come posso impostare questo valore nella mia richiesta Authenticator?

+0

Ho ridotto il mio codice e ora ricevo un errore di 400 richieste errate durante il tentativo di interrogare Google. –

+0

Ho risolto il mio problema codificando manualmente HttpRequests avanti e indietro su Google. Io disinfetterò e inserirò il codice come risposta –

risposta

12

Dopo ore di giocherellare con DotNetOpenAuth e Google APIs pubblicato per .Net, non sono riuscito a ottenere nulla. Ho deciso di aggirare le librerie e sono andato direttamente allo Google REST API con gli oggetti HttpRequest e HttpResponse nativi. Il mio codice sterilizzata per il mio controller MVC segue:

private static string _GoogleClientId = "CLIENT_ID"; 
    private static string _GoogleSecret = "SECRET"; 
    private static string _ReturnUrl = "http://localhost/OAuth/CallBack"; 

    public ActionResult Index() 
    { 
     return Redirect(GenerateGoogleOAuthUrl()); 
    } 

    private string GenerateGoogleOAuthUrl() 
    { 

     //NOTE: Key piece here, from Andrew's reply -> access_type=offline forces a refresh token to be issued 
     string Url = "https://accounts.google.com/o/oauth2/auth?scope={0}&redirect_uri={1}&response_type={2}&client_id={3}&state={4}&access_type=offline&approval_prompt=force"; 
     string scope = UrlEncodeForGoogle("https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.readonly").Replace("%20", "+"); 
     string redirect_uri_encode = UrlEncodeForGoogle(_ReturnUrl); 
     string response_type = "code"; 
     string state = ""; 

     return string.Format(Url, scope, redirect_uri_encode, response_type, _GoogleClientId, state); 

    } 

    private static string UrlEncodeForGoogle(string url) 
    { 
     string UnReservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~"; 
     var result = new StringBuilder(); 

     foreach (char symbol in url) 
     { 
      if (UnReservedChars.IndexOf(symbol) != -1) 
      { 
       result.Append(symbol); 
      } 
      else 
      { 
       result.Append('%' + String.Format("{0:X2}", (int)symbol)); 
      } 
     } 

     return result.ToString(); 
    } 

    class GoogleTokenData 
    { 
     public string Access_Token { get; set; } 
     public string Refresh_Token { get; set; } 
     public string Expires_In { get; set; } 
     public string Token_Type { get; set; } 
    } 

    public ActionResult CallBack(string code, bool? remove) 
    { 

     if (remove.HasValue && remove.Value) 
     { 
      Session["GoogleAPIToken"] = null; 
      return HttpNotFound(); 
     } 

     if (string.IsNullOrEmpty(code)) return Content("Missing code"); 

     string Url = "https://accounts.google.com/o/oauth2/token"; 
     string grant_type = "authorization_code"; 
     string redirect_uri_encode = UrlEncodeForGoogle(_ReturnUrl); 
     string data = "code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type={4}"; 

     HttpWebRequest request = HttpWebRequest.Create(Url) as HttpWebRequest; 
     string result = null; 
     request.Method = "POST"; 
     request.KeepAlive = true; 
     request.ContentType = "application/x-www-form-urlencoded"; 
     string param = string.Format(data, code, _GoogleClientId, _GoogleSecret, redirect_uri_encode, grant_type); 
     var bs = Encoding.UTF8.GetBytes(param); 
     using (Stream reqStream = request.GetRequestStream()) 
     { 
      reqStream.Write(bs, 0, bs.Length); 
     } 

     using (WebResponse response = request.GetResponse()) 
     { 
      var sr = new StreamReader(response.GetResponseStream()); 
      result = sr.ReadToEnd(); 
      sr.Close(); 
     } 

     var jsonSerializer = new JavaScriptSerializer(); 
     var tokenData = jsonSerializer.Deserialize<GoogleTokenData>(result); 
     Session["GoogleAPIToken"] = tokenData.Access_Token; 

     return JavaScript("Refresh Token: " + tokenData.Refresh_Token); 

    } 

Un grande grazie a Kelp per un po 'di codice in questo snippet.

+2

grazie per il codice, funziona davvero :) –

9

Regolare GoogleAuthenticationServer.Description di avere un URI autorizzazione endpoint che include ?access_type=offline nella stringa di query.

+0

Grazie per questo utilissimo suggerimento 'AuthorizationServerDescription descServer = GoogleAuthenticationServer.Description; descServer.AuthorizationEndpoint = new Uri (descServer.AuthorizationEndpoint.ToString() + "? Access_type = offline"); ' utilizzato in questo modo e ha funzionato per me – RohitWagh

0

Basta aggiungere

AccessType = "offline",

a GoogleOAuth2AuthenticationOptions() dell'oggetto.