2017-08-24 77 views
0

我有一個web應用程序,用戶在登錄到我們的系統後,可以訪問一個頁面,授權通過整個OAuth進程訪問gmail。該過程完成後,我接收access_token和refresh_token,並解析用戶的子屬性的id_token並將其保存到數據庫。其目的是通過用戶授權我們的應用程序,然後我們在控制檯應用程序中使用訪問和刷新令牌,然後檢索他們的電子郵件並將其與我們的內部系統同步。如何使用access_token查詢gmail api

在谷歌開發控制檯中,我創建了憑據作爲Web應用程序與適當的重定向uris。瀏覽器授權過程一切正常。然而,當我運行使用訪問令牌中的控制檯應用程序,它會打開一個瀏覽器窗口,一個谷歌的錯誤說

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應用程序中的密鑰和祕密。當我經歷登錄到我們系統的過程,然後訪問授權頁面時,它一切正常。因此,我回到我的控制檯應用程序(現在使用更新的ID),當它運行時,它會打開一個瀏覽器窗口,要求我登錄谷歌並再次驗證它!我已經完成了第一步。由於這是在應用程序服務器上運行的控制檯應用程序,因此用戶無法與其交互以授權訪問......我認爲這是訪問令牌的全部要點!

這是我的代碼。

處理授權的Web應用程序控制器。

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 = "[email protected]", 
        ToAddress = "[email protected]" 
       }); 

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

的GetAsync方法在IDataStore被返回訪問令牌和刷新令牌憑據GoogleWebAuthorizationBroker.AuthorizeAsync和它之後的是,當一個新的瀏覽器中打開要我授權的應用程序。

那麼,我應該在谷歌控制檯中設置爲Web應用類型還是其他類型?而且,既然我遇到了問題,無論我嘗試什麼,我做錯了什麼?

回答

0

您從Google開發者控制檯生成的每個OauthClientID都有一個redirect_uri。

enter image description here

+0

我明白了。我在附加的重定向uri中添加了錯誤信息。最終,我放棄了使用.net google api程序集,並最終編寫了自己的代碼。現在一切都按預期工作。 –

+0

好工作。如果你想幫助別人,你可以發佈你的答案。 – noogui

+0

代碼完成後會執行。謝謝。 –