2012-07-05 11 views
7

Ich versuche, Google Kalender in meine Anwendung zu integrieren und habe Probleme mit der OAuth-Autorisierung, die ein RefreshToken abgibt. Ich erhalte ein AccessToken ohne Problem, aber die RefreshToken-Eigenschaft ist null. Siehe die Linie markiert "ERROR HIER:" Denn wo ich das Problem aussieht habeWarum erhalte ich von einer Google OAuth-Anfrage kein RefreshToken?

Mein Asp.Net MVC-Controller (mit dem Namen OAuthController) wie folgt aus:

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); 
    } 

ich in den Google-API-Dokumente zu sehen, dass Ich muss sicherstellen, dass die access_type Anforderung für ein RefreshToken auf offline gesetzt ist, um gesendet zu werden. Wie setze ich diesen Wert in meiner Authenticator-Anfrage?

+0

Ich habe meinen Code abgespeckt, und jetzt bekomme ich einen 400 Bad Request Fehler beim Versuch, Google abzufragen. –

+0

Ich löste mein Problem, indem ich die HttpRequests manuell an Google kodierte. Ich werde sanieren und Code als Antwort eingeben –

Antwort

12

Nach Stunden des Fiedelns mit DotNetOpenAuth und Google APIs für .Net veröffentlicht, kam ich nirgends schnell. Ich beschloss, die Bibliotheken zu umgehen und ging direkt an die Google REST API mit nativen HttpRequest und HttpResponse Objekte. Mein hygienisiert Code für meine MVC-Controller folgt:

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); 

    } 

Big dank Kelp für ein Bit des Codes in diesem Code-Schnipsel.

+2

danke für den Code, es funktioniert wirklich :) –

9

Passen Sie GoogleAuthenticationServer.Description an, um einen Autorisierungsendpunkt-URI zu haben, der ?access_type=offline in der Abfragezeichenfolge enthält.

+0

Danke für diesen sehr nützlichen Hinweis 'AuthorizationServerDescription descServer = GoogleAuthenticationServer.Description; descServer.AuthorizationEndpoint = neuer Uri (descServer.AuthorizationEndpoint.ToString() + "? Access_type = offline"); ' hat es so benutzt und funktioniert für mich – RohitWagh

0

einfach

Accesslype = "offline", fügen

zu GoogleOAuth2AuthenticationOptions() Objekt.

Verwandte Themen