2017-08-24 5 views
0

J'ai une application Web où l'utilisateur, après s'être connecté à notre système, peut accéder à une page pour autoriser l'accès gmail à travers tout le processus OAuth avec google. Le processus se termine avec la réception de access_token et refresh_token et l'analyse de l'id_token pour la sous-propriété de l'utilisateur et son enregistrement dans une base de données. Le but de ceci est que par l'utilisateur autorisant notre application, nous utilisons ensuite les jetons d'accès et d'actualisation dans une application de console pour ensuite récupérer leurs e-mails et les synchroniser avec notre système interne.Comment utiliser access_token pour interroger l'API gi

Dans la console de développement google, j'ai créé les informations d'identification en tant qu'application Web avec l'URL de redirection appropriée. Le processus d'autorisation du navigateur fonctionne correctement. Cependant quand je lance l'application de la console en utilisant le jeton d'accès, il ouvre une fenêtre de navigateur avec une erreur google disant

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. 

Puisque c'est une application de la console, il n'y a pas de redirection uri. Cela m'a fait me demander si j'avais besoin de définir les informations d'identification comme un "autre" type d'application. J'ai créé une nouvelle référence et remplacé le clientid et le secret dans mon application web avec les nouveaux. Lorsque je passe par le processus de connexion à notre système et que je consulte la page d'autorisation, tout fonctionne correctement. Alors je retourne à mon application de console (maintenant en utilisant les identifiants mis à jour) et quand il fonctionne, il ouvre une fenêtre de navigateur me demandant de me connecter à google et de l'authentifier encore une fois! Je l'ai déjà fait dans la première étape. Comme il s'agit d'une application console fonctionnant sur un serveur d'application, un utilisateur ne peut pas interagir avec lui pour autoriser l'accès ... Je pensais que c'était tout le point des jetons d'accès !?

Voici le code que j'ai.

Le contrôleur d'application Web qui gère l'autorisation.

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) 
    { 
     .... 
    } 
} 

L'application console utilise les bibliothèques .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(); 
    } 
} 

Et mon implémentation de l'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); 
    } 
} 

La méthode GetAsync dans le IDataStore renvoie le jeton et les informations d'identification de jeton de rafraîchissement accès au GoogleWebAuthorizationBroker.AuthorizeAsync et après le fait qui est quand un nouveau navigateur ouvre me demandant d'autoriser l'application. Donc, devrais-je le configurer dans la console google comme un type d'application Web ou un autre type? Et comme je rencontre un problème, j'essaie, qu'est-ce que je fais de mal?

Répondre

0

Chaque OauthClientID que vous avez généré à partir de votre console Google Dev Console dispose d'une adresse de redirection.

enter image description here

+0

Je comprends cela. Et j'ai ajouté dans l'uri de redirection supplémentaire que le message d'erreur a donné. Finalement, j'ai abandonné l'utilisation des assemblages .net google api et j'ai fini par coder mon propre. Maintenant tout fonctionne comme prévu. –

+0

bon travail. vous pouvez poster votre réponse si vous voulez aider les autres. – noogui

+0

Va faire une fois le code est terminé. Merci. –