使用mvc从google api获取访问令牌的困惑

时间:2017-08-16 17:40:29

标签: asp.net-mvc google-api google-oauth

我一直在尝试按照一些教程,我可以找到一个mvc应用程序允许用户验证应用程序并获取访问权限并刷新令牌。不幸的是,我无法找到任何足够清楚的地方,以便我可以跟踪所发生的事情。我从谷歌的sample code开始,然后找到其他一些像this onethis one

当我运行我的应用程序时,我试图转到http://localhost:61581/Integration/Google/IndexAsync它会遇到最终命中AppFlowMetadata.GetUserId方法的方法然后点击我的自定义TenixDataStore类' GetAsync方法。

令人困惑的事情是

  1. 首先,我要使用正确的网址/方法吗?我想我是基于谷歌的代码示例,但不确定。
  2. 我认为我得到的密钥是电子邮件地址,而是GUID。是谷歌如何识别用户?
  3. 如果我要访问正确的网址,为什么页面会挂起而永远不会返回。我希望它能打开一个没有发生的谷歌授权页面。
  4. 这是我的代码。

    AppFlowMetadata类

    using System.Web.Mvc;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Auth.OAuth2.Flows;
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Gmail.v1;
    using Tenix.Domain.Constants;
    
    namespace MyApp.Areas.Integration.Controllers
    {
        public class AppFlowMetadata : FlowMetadata
        {
            private static readonly IAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = new ClientSecrets
                    {
                        ClientId = APIConstants.GMailApiKey,
                        ClientSecret = APIConstants.GmailApiSecret
                    },
                    Scopes = new[] {GmailService.Scope.GmailReadonly},
                    DataStore = new TenixDataStore()
                });
    
            public override IAuthorizationCodeFlow Flow
            {
                get { return flow; }
            }
    
            public override string GetUserId(Controller controller)
            {
                // In this sample we use the session to store the user identifiers.
                // That's not the best practice, because you should have a logic to identify
                // a user. You might want to use "OpenID Connect".
                // You can read more about the protocol in the following link:
                // https://developers.google.com/accounts/docs/OAuth2Login.
                var user = controller.Session["UserID"];
                if (user == null) return null;
                return user.ToString();
            }
        }
    }
    

    GoogleController

    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Gmail.v1;
    using Google.Apis.Services;
    
    namespace MyApp.Areas.Integration.Controllers
    {
        public class GoogleController : Controller
        {
            public async Task IndexAsync(CancellationToken cancellationToken)
            {
                if (Session["UserID"] == null)
                {
                    Response.Redirect("~/Login.aspx", true);
                }
    
                var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
    
                if (result.Credential != null)
                {
                    var service = new GmailService(new BaseClientService.Initializer
                    {
                        HttpClientInitializer = result.Credential,
                        ApplicationName = "Tenix Gmail Integration"
                    });
                }
            }
        }
    }
    

    TenixDataStore类

    using System;
    using System.Threading.Tasks;
    using DataBaseUtilitiesTEN;
    using Google.Apis.Json;
    using Google.Apis.Util.Store;
    using Newtonsoft.Json.Linq;
    using Synergy.Extensions;
    using Tenix.Domain.Data.Respositories;
    using Tenix.Domain.Model.Integration;
    using Tenix.Domain.Services;
    
    namespace MyApp.Areas.Integration.Controllers
    {
        public class TenixDataStore : IDataStore
        {
            private readonly string conStr = ConnectionStrings.GeneralInfo;
            private CredentialService _service;
    
            public TenixDataStore()
            {
                _service = new CredentialService(new CredentialRepository(conStr));
            }
    
            public Task StoreAsync<T>(string key, T value)
            {
                if (string.IsNullOrEmpty(key))
                    throw new ArgumentException("Key MUST have a value");
    
                var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
                var jObject = JObject.Parse(serialized);
    
                var access_token = jObject.SelectToken("access_token");
                var refresh_token = jObject.SelectToken("refresh_token");
    
                if (access_token == null) 
                    throw new ArgumentException("Missing access token");
    
                if (refresh_token == null)
                    throw new ArgumentException("Missing refresh token");
    
                _service.SaveUserCredentials(new UserCredential
                {
                    EmailAddress = key,
                    AccessToken = (string)access_token,
                    RefreshToken = (string)refresh_token
                });
    
                return Task.Delay(0);
            }
    
            public Task DeleteAsync<T>(string key)
            {
                _service.DeleteCredentials(key);
                return Task.Delay(0);
            }
    
            public Task<T> GetAsync<T>(string userId)
            {
                var credentials = _service.GetUserCredentials(userId.To<int>());
                var completionSource = new TaskCompletionSource<T>();
    
                if (!string.IsNullOrEmpty(credentials.AccessToken))
                    completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(credentials.AccessToken));
    
                return completionSource.Task;
            }
    
            public Task ClearAsync()
            {
                return Task.Delay(0);
            }
        }
    }
    

    AuthCallbackController

    using Google.Apis.Auth.OAuth2.Mvc;
    
    namespace MyApp.Areas.Integration.Controllers
    {
        public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
        {
            protected override FlowMetadata FlowData
            {
                get { return new AppFlowMetadata(); }
            }
        }
    }
    

1 个答案:

答案 0 :(得分:1)

花了好几天试图解决这个问题并且没有在google api .net库上取得任何进展之后,我最终选择了我自己的实现,reading their documentation之后至少是我能完全理解的东西。如果有人可以使用代码,这就是我最终的结果。仍然需要做一些重构,但此时它正在发挥作用。

只需确保将AuthorizeResponse和Authorize路由注册为授权重定向uris。

public class GoogleController : Controller
{
    private readonly CredentialService _credentialService;
    private readonly GoogleEndpoints _endpoints;

    public GoogleController()
    {
        _endpoints = new GoogleEndpoints();
        _credentialService = new CredentialService(new CredentialRepository(ConnectionStrings.GeneralInfo));
    }

    private string AuthorizeUrl
    {
        get
        {
            return "/Integration/Google/Authorize";
        }
    }

    private string AuthorizeResponseUrl
    {
        get
        {
            return "/Integration/Google/AuthorizeResponse";
        }
    }

    private string SaveResponseUrl
    {
        get
        {
            return "/Integration/Google/SaveResponse";
        }
    }

    public void Authorize()
    {
        if (Session["UserID"] == null || Session["Email"] == null)
        {
            Response.Redirect("~/Login.aspx", true);
            Session["LoginSource"] = AuthorizeUrl;
            Response.End();
        }
        else
        {
            if (Session["SessionId"] == null || Session["SessionId"].ToString().Trim().Length == 0)
                Session["SessionId"] = _credentialService.CreateSessionId(Session["UserID"].To<int>());

            var url = _endpoints.AuthorizationEndpoint + "?" +
                      "client_id=" + APIConstants.GMailApiKey + "&" +
                      "response_type=code&" +
                      "scope=openid%20email&" +
                      "redirect_uri=" + AuthorizeResponseUrl + "&" +
                      "state=" + Session["SessionId"] + "&" +
                      "login_hint=" + Session["Email"] + "&" +
                      "access_type=offline";

            Response.Redirect(url);
        }
    }

    public ActionResult AuthorizeResponse()
    {
        var state = Request.QueryString["state"];
        if (state == Session["SessionId"].ToString())
        {
            var code = Request.QueryString["code"];
            var values = new Dictionary<string, object>
            {
                {"code", code},
                {"redirect_uri", AuthorizeResponseUrl},
                {"client_id", APIConstants.GMailApiKey},
                {"client_secret", APIConstants.GmailApiSecret},
                {"grant_type", "authorization_code"},
                {"scope", ""}
            };

            var webmethods = new WebMethods();
            var tokenResponse = webmethods.Post(_endpoints.TokenEndpoint, values);

            var jobject = JObject.Parse(tokenResponse);
            var access_token = jobject.SelectToken("access_token");
            var refresh_token = jobject.SelectToken("refresh_token");

            if (access_token == null || access_token.ToString().Trim().Length == 0)
            {
                //notify devs something went wrong
                return View(new GoogleAuthResponse(tokenResponse, false));
            }

            var credentials = _credentialService.GetUserCredentials(Session["SessionId"].ToString());

            credentials.AccessToken = access_token.ToString();
            credentials.RefreshToken = refresh_token.ToString();
            credentials.EmployeeId = Session["UserId"].To<int>();

            _credentialService.SaveUserCredentials(credentials);

            return View(new GoogleAuthResponse("Integration successful!", true));
        }

        return View(new GoogleAuthResponse("Missing state information.", false));
    }
}

帮助类来获取谷歌端点。

public class GoogleEndpoints
{
    public GoogleEndpoints()
    {
        using (var client = new WebClient())
        {
            var response = client.DownloadString("https://accounts.google.com/.well-known/openid-configuration");
            var jobject = JObject.Parse(response);
            AuthorizationEndpoint = jobject.SelectToken("authorization_endpoint").ToString();
            TokenEndpoint = jobject.SelectToken("token_endpoint").ToString();
        }
    }

    public string AuthorizationEndpoint { get; private set; }

    public string TokenEndpoint { get; private set; }
}

控制器使用另外两个辅助类来解析json并发布表单数据,但这应该非常简单。