简体   繁体   English

使用MVC从Google API获取访问令牌的困惑

[英]Confusion on getting access token from google api with mvc

I've been trying to follow a number of tutorials I can find to have an mvc application allow a user to authenticate the app and get the access and refresh tokens back. 我一直在尝试着一些教程,我发现它们可以使mvc应用程序允许用户对应用程序进行身份验证,并获得访问权限和刷新令牌。 Unfortunately I can't find any that are clear enough to where I can follow what's going on. 不幸的是,我找不到足够清楚的地方来跟踪发生的事情。 I started with google's sample code and then found some others like this one and this one . 我开始与谷歌的示例代码 ,然后发现了一些其他像这一个这一个

When I run my app I'm trying to go to http://localhost:61581/Integration/Google/IndexAsync it hits that method which eventually hits the AppFlowMetadata.GetUserId method and then hits my custom TenixDataStore class' GetAsync method. 当我运行我的应用程序时,我尝试转到http:// localhost:61581 / Integration / Google / IndexAsync ,该方法命中该方法,该方法最终命中AppFlowMetadata.GetUserId方法,然后命中我自定义的TenixDataStore类的GetAsync方法。

The things that are confusing are 令人困惑的是

  1. First off, am I going to the right url/method? 首先,我要转到正确的网址/方法吗? I think I am based on google's code example but not sure. 我想我是基于Google的代码示例,但不确定。
  2. I thought that the key I would get would be the email address but instead is a GUID. 我以为我会得到的密钥是电子邮件地址,而只是一个GUID。 Is that how google identifies a user? Google就是这样识别用户的吗?
  3. If I'm going to the right url, why does the page just hang and never return. 如果我要输入正确的URL,为什么页面只是挂起而永不返回。 I expected it to open a google authorization page which didn't happen. 我希望它可以打开未发生的Google授权页面。

Here's my code. 这是我的代码。

AppFlowMetadata class 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 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 class 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 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(); }
        }
    }
}

After spending days trying to figure this out and not making any headway with the google api .net libraries I ended up just going with my own implementation which after reading their documentation was at least something I could fully understand. 在花了几天的时间试图弄清楚这一点并且没有在google api .net库上取得任何进展之后,我最终还是采用了自己的实现,在阅读了它们的文档之后,我至少可以完全理解这一点。 In case anyone could use the code, here's what I ended up with. 万一任何人都可以使用该代码,这就是我最终得到的结果。 Still need to do some refactoring, but at this point it's working. 仍然需要进行一些重构,但是此时它正在工作。

Just need to make sure the AuthorizeResponse and Authorize routes are registered as authorized redirect uris. 只需确保将AuthorizeResponse和Authorize路由注册为授权重定向uri。

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

And the helper class to get the google endpoints. 和帮助程序类来获取google端点。

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

The controller uses another couple of helper classes for parsing the json and posting the form data, but that should be pretty straightforward. 控制器使用另外两个辅助类来解析json和发布表单数据,但这应该非常简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Asp.Net MVc获取Google文档api刷新令牌和访问令牌 - Get the google docs api refresh token and access token using Asp.Net MVc 从Web API控制器获取Live SDK访问令牌 - Getting Live SDK Access Token from Web API Controller MVC应用中的Google OAuth访问令牌到期? - Google OAuth access token expiration in MVC app? 手动授予访问令牌MVC Web API - Granting access token manually MVC Web API 我尝试使用移动端通过 web api 提供的 access_token 从谷歌 api 获取用户信息,但出现 401 错误 - I try to fetch user information from google api using access_token which provided by mobile end through web api but getting 401 error 在ASP.Net MVC应用程序中从适用于.Net的Facebook SDK获取Facebook用户访问令牌 - Getting Facebook user access token from Facebook SDK for .Net in ASP.Net MVC app 使用我的API中的访问令牌对C#MVC Web应用程序进行身份验证的最佳方法 - Best way to authenticate a C# MVC web app using an access token from my API 具有Facebook访问令牌的MVC 5 Web API到RegisterExternal而不需要Cookie - MVC 5 Web API with Facebook access token to RegisterExternal without need of Cookie 具有现有访问令牌的Google Drive API .Net客户端简单授权 - Google Drive api .Net Client Simple Authorization with existing access token 生成令牌时拒绝Google API授权访问 - Google API authorization access denied while generating token
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM