7

我正在尝试将 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 请求中设置此值?

4

3 回答 3

12

经过数小时摆弄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提供了这段代码中的一些代码。

于 2012-07-06T11:57:46.317 回答
9

调整GoogleAuthenticationServer.Description以具有包含?access_type=offline在查询字符串中的授权端点 URI。

于 2012-07-05T15:59:29.003 回答
0

只需添加

AccessType = "offline",

反对GoogleOAuth2AuthenticationOptions()

于 2015-06-26T05:39:25.717 回答