Asp.net-Mvc

為什麼我沒有收到來自 Google OAuth 請求的 RefreshToken?

  • June 26, 2020

我正在嘗試將 Google 日曆集成到我的應用程序中,並且在傳遞 RefreshToken 的 OAuth 授權方面遇到了一些問題。我收到一個沒有問題的 AccessToken,但 RefreshToken 屬性為空。請參閱標有“此處錯誤:”的行以了解我遇到問題的位置

我的 Asp.Net MVC 控制器(名為OAuthController)如下所示:

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

我在 Google 的 API 文件中看到,我需要確保將access_type請求設置offline為發送 RefreshToken。如何在 Authenticator 請求中設置此值?

經過數小時擺弄DotNetOpenAuth和為 .Net 發布的Google API之後,我一無所獲。我決定繞過這些庫,直接使用原生 HttpRequest 和 HttpResponse 對象訪問Google REST API 。我的 MVC 控制器的淨化程式碼如下:

   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 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
       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);

   }

非常感謝Kelp提供了這段程式碼中的一些程式碼。

引用自:https://stackoverflow.com/questions/11346355