如何使用access_token查询gmail api

时间:2017-08-24 12:54:02

标签: google-api gmail-api google-oauth2 google-openid

我有一个网络应用程序,用户登录我们的系统后,可以转到页面,通过谷歌授权整个OAuth流程的gmail访问权限。该过程完成后,我接收access_token和refresh_token,并解析用户子属性的id_token并将其保存到数据库。这样做的目的是,通过授权我们的应用程序的用户,我们然后在控制台应用程序中使用访问和刷新令牌,然后检索他们的电子邮件并将其与我们的内部系统同步。

在google开发者控制台中,我使用相应的重定向uris将凭据创建为Web应用程序。浏览器授权过程一切正常。但是,当我使用访问令牌运行控制台应用程序时,它会打开一个浏览器窗口,并显示谷歌错误

Error: redirect_uri_mismatch

The redirect URI in the request, http://127.0.0.1:57590/authorize/, does not match the ones authorized for the OAuth client.

由于这是一个控制台应用程序,因此没有重定向uri。所以这让我想知道我是否需要将凭证设置为"其他"申请类型。我创建了一个新的凭据,并用我的Web应用程序中的clientid和secret替换了新的凭据。当我完成登录系统然后访问授权页面的过程时,一切正常。然后我回到我的控制台应用程序(现在使用更新的ID),当它运行时,它打开一个浏览器窗口,要求我登录谷歌并再次验证它!我已经在第一步中完成了这项工作。由于这是在应用服务器上运行的控制台应用,因此用户无法与其进行交互以授权访问...我认为这是访问令牌的全部意义!?

这是我的代码。

处理授权的网络应用控制器。

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

    private string AuthorizeUrl{ get { return ...; }}
    private string AuthorizeResponseUrl{ get { return ...; }}
    private string SaveResponseUrl{ get { return ...; }}

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

    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 https://www.googleapis.com/auth/gmail.readonly&" +
                      "redirect_uri=" + AuthorizeResponseUrl + "&" +
                      "state=" + Session["SessionId"] + "&" +
                      "login_hint=" + Session["Email"] + "&" +
                      "access_type=offline&prompt=consent";

            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");
            var id_token = jobject.SelectToken("id_token");

            if (access_token == null || access_token.ToString().Trim().Length == 0)
            {
                var emailService = new EmailRouterService(new EmailRouterRepository(ConnectionStrings.EmailRouter));
                emailService.SendEmail(new Email
                {
                    Body = tokenResponse,
                    Subject = "Error setting up google for " + Session["Email"] + ". ",
                    FromAddress = "TenixErrors@synergydatasystems.com",
                    ToAddress = "tendevs@synergydatasystems.com"
                });

                return View(new GoogleAuthResponse(tokenResponse, false));
            }


            var idTokenEls = id_token.ToString().Split('.');
            var idTokenString = base64Decode(idTokenEls[1]);
            var id_token_json = JObject.Parse(idTokenString);

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

            credentials.AccessToken = access_token.ToString();
            credentials.RefreshToken = refresh_token.ToString();
            credentials.TEEmployeeId = Session["UserId"].To<int>();
            credentials.EmailAddress = id_token_json.SelectToken("email").ToString();
            credentials.GoogleSubKey = id_token_json.SelectToken("sub").ToString();


            _credentialService.SaveUserCredentials(credentials);

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

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

    public string base64Decode(string data)
    {
        ....
    }
}

控制台应用正在使用.net google api库。

public class GmailUserSync
{
    private readonly CredentialService _credentialService;

    public GmailUserSync(CredentialService credentialService)
    {
        _credentialService = credentialService;
    }

    public void SyncThreads()
    {
        var secrets = new ClientSecrets
        {
            ClientId = APIConstants.GMailApiKey,
            ClientSecret = APIConstants.GmailApiSecret
        };

        var credentials = _credentialService.GetAllCredentials();

        foreach (var c in credentials)
        {
            var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                secrets,
                new[] { GmailService.Scope.GmailReadonly },
                c.GoogleSubKey,
                CancellationToken.None,
                new TenixDataStore(_credentialService)).Result;

            var service = new GmailService(new BaseClientService.Initializer
            {
                HttpClientInitializer = credential,
                ApplicationName = "Gmail Integration"
            });

            var request = service.Users.Messages.List("me");

            // List messages.
            var messages = request.Execute().Messages;
            Console.WriteLine("Messages:");

            if (messages != null && messages.Count > 0)
                foreach (var message in messages)
                {
                    Console.WriteLine("From : " + message.ThreadId);
                }

            else
                Console.WriteLine("No labels found.");
        }

        Console.Read();
    }
}

我的IDataStore实现。

public class TenixDataStore : IDataStore
{
    private readonly ICredentialService _service;

    public TenixDataStore(ICredentialService credentialService)
    {
        _service = credentialService;
    }

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

        var credential = _service.GetUserCredentialsFromGoogleSubId(key);

        credential.AccessToken = (string) access_token;
        credential.RefreshToken = (string) refresh_token;

        _service.SaveUserCredentials(credential);

        return Task.Delay(0);
    }

    public Task DeleteAsync<T>(string key)
    {
        return Task.Delay(0);
    }

    public Task<T> GetAsync<T>(string googleSubId)
    {
        var credentials = _service.GetUserCredentialsFromGoogleSubId(googleSubId);
        var completionSource = new TaskCompletionSource<T>();

        if (!string.IsNullOrEmpty(credentials.RefreshToken))
        {
            var jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(credentials);
            completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(jsonData));
        }
        else
            completionSource.SetResult(default(T));

        return completionSource.Task;
    }

    public Task ClearAsync()
    {
        return Task.Delay(0);
    }
}

IDataStore中的GetAsync方法将访问令牌和刷新令牌凭据返回给GoogleWebAuthorizationBroker.AuthorizeAsync,之后就是新浏览器打开,要求我授权该应用程序。

那么,我应该将这个设置在谷歌控制台中作为网络应用类型还是其他类型?而且,既然我试着以任何方式遇到问题,那么我做错了什么?

1 个答案:

答案 0 :(得分:0)

您从Google Dev Console生成的每个OauthClientID都有一个redirect_uri。

enter image description here