7

W mojej aplikacji korzystam z biblioteki Google API V 3.0 .Net z Google OAuth2 do synchronizacji kalendarza Google z kalendarzem programu Outlook. Używam poniższego kodu, aby pobrać obiekt usługi Google.Apis.Calendar.v3.CalendarService. Podczas uwierzytelniania zapisałem plik Json, a następnie żądam obiektu Google.Apis.Auth.OAuth2.UserCredential.Jak korzystać z biblioteki Google API V 3.0 .Net i Google OAuth2 Obsługa tokenu odświeżania

private Google.Apis.Auth.OAuth2.UserCredential GetGoogleOAuthCredential() 
{ 
    GoogleTokenModel _TokenData = new GoogleTokenModel(); 
    String JsonFilelocation = "jsonFileLocation; 
    Google.Apis.Auth.OAuth2.UserCredential credential = null; 
    using (var stream = new FileStream(JsonFilelocation, FileMode.Open, 
        FileAccess.Read)) 
    { 
     Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.Folder = "Tasks.Auth.Store"; 
     credential = Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(
     Google.Apis.Auth.OAuth2.GoogleClientSecrets.Load(stream).Secrets, 
     new[] { Google.Apis.Calendar.v3.CalendarService.Scope.Calendar }, 
     "user", 
     CancellationToken.None, 
     new FileDataStore("OGSync.Auth.Store")).Result; 
    } 
    return credential; 
} 

Żądanie kodu wynikowego serwisowy:

Google.Apis.Calendar.v3.CalendarService _V3calendarService = new Google.Apis.Calendar.v3.CalendarService(new Google.Apis.Services.BaseClientService.Initializer() 
{ 
HttpClientInitializer = GetGoogleOAuthCredential(), 
ApplicationName = "TestApplication", 
}); 

Powyższy kod działa dobrze, aby uzyskać obiekt Calendarservice. Moje pytanie brzmi: mój plik Json ma odświeżające i dostępowe tokeny. w jaki sposób powyższy kod obsługuje odświeżenie tokena w celu uzyskania usługi po wygaśnięciu tokena dostępu? Ponieważ muszę często wywoływać obiekt Calendarservice, lubię implementować wzorzec singleton dla obiektu calenderService. Jak uzyskać usługę Calendarservice bez częstego dzwonienia do GetGoogleOAuthCredential? Każda pomoc/wskazówki są mile widziane.

+0

Sprawdź również http://stackoverflow.com/a/24972426/833846 –

Odpowiedz

2

To jest zalążek biblioteki klienta! ta magia jest zrobiona automatycznie :)

UserCredential implementuje IHttpExecuteInterceptor i IHttpUnsuccessfulResponseHandler. więc za każdym razem, gdy token dostępu wygasa lub już wygasł, klient nawiązuje połączenie z serwerem autoryzacji, aby odświeżyć token i uzyskać nowy token dostępu (który jest ważny przez następne 60 minut).

Czytaj więcej na ten temat na https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#credentials

+0

ok dzięki, wtedy za każdym razem, kiedy muszę usługę czy muszę połączyć się z serwerem autoryzacji. czy istnieje sposób sprawdzenia tokena, czy wygasł, czy nie i czy wywołanie serwera jest konieczne? –

+0

Biblioteka robi to za Ciebie. – peleyal

+0

UserCredential nie odświeży tokena podczas korzystania z klienta IMAP. może będzie, gdy użyje obiektu usługi Google API, ale nie we wszystkich przypadkach. – skyfree

1

Dla mnie biblioteka klient nie zrobić orzeźwiający, nawet tworząc token odświeżania.
Sprawdzam, czy token wygasł i odśwież. (Token ma 1 godzinę życia). credential.Token.IsExpired poinformuje Cię, czy wygasło, credential.Token.RefreshToken (identyfikator użytkownika) odświeży niezbędny token.

12

Spędziłem ostatnie dwa dni sam to sobie wymyśliłem. Biblioteka nie odświeża tokenów automatycznie, chyba że wybierzesz "access_type = offline".

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth

będę wkleić kod używam i czy jest coś czego nie rozumiesz, po prostu zapytaj. Czytałem tak wiele postów i litteraly działało teraz, więc jest jakiś skomentowany kod i nie został jeszcze naprawiony. Mam nadzieję, że to pomoże komuś. Pakiety Nuget Używam są takie:

Google.Apis.Auth.MVC

Google.Apis.Calendar.v3

Kod:

AuthCallbackController:

[AuthorizationCodeActionFilter] 
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController 
{ 
protected static readonly ILogger Logger = ApplicationContext.Logger.ForType<AuthCallbackController>(); 

/// <summary>Gets the authorization code flow.</summary> 
protected IAuthorizationCodeFlow Flow { get { return FlowData.Flow; } } 

/// <summary> 
/// Gets the user identifier. Potential logic is to use session variables to retrieve that information. 
/// </summary> 
protected string UserId { get { return FlowData.GetUserId(this); } } 

/// <summary> 
/// The authorization callback which receives an authorization code which contains an error or a code. 
/// If a code is available the method exchange the coed with an access token and redirect back to the original 
/// page which initialized the auth process (using the state parameter). 
/// <para> 
/// The current timeout is set to 10 seconds. You can change the default behavior by setting 
/// <see cref="System.Web.Mvc.AsyncTimeoutAttribute"/> with a different value on your controller. 
/// </para> 
/// </summary> 
/// <param name="authorizationCode">Authorization code response which contains the code or an error.</param> 
/// <param name="taskCancellationToken">Cancellation token to cancel operation.</param> 
/// <returns> 
/// Redirect action to the state parameter or <see cref="OnTokenError"/> in case of an error. 
/// </returns> 
[AsyncTimeout(60000)] 
public async override Task<ActionResult> IndexAsync(AuthorizationCodeResponseUrl authorizationCode, 
    CancellationToken taskCancellationToken) 
{ 
    if (string.IsNullOrEmpty(authorizationCode.Code)) 
    { 
     var errorResponse = new TokenErrorResponse(authorizationCode); 
     Logger.Info("Received an error. The response is: {0}", errorResponse); 
     Debug.WriteLine("Received an error. The response is: {0}", errorResponse); 
     return OnTokenError(errorResponse); 
    } 

    Logger.Debug("Received \"{0}\" code", authorizationCode.Code); 
    Debug.WriteLine("Received \"{0}\" code", authorizationCode.Code); 


    var returnUrl = Request.Url.ToString(); 
    returnUrl = returnUrl.Substring(0, returnUrl.IndexOf("?")); 

    var token = await Flow.ExchangeCodeForTokenAsync(UserId, authorizationCode.Code, returnUrl, 
     taskCancellationToken).ConfigureAwait(false); 

    // Extract the right state. 
    var oauthState = await AuthWebUtility.ExtracRedirectFromState(Flow.DataStore, UserId, 
     authorizationCode.State).ConfigureAwait(false); 

    return new RedirectResult(oauthState); 
} 

protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData 
{ 
    get { return new AppFlowMetadata(); } 
} 

protected override ActionResult OnTokenError(TokenErrorResponse errorResponse) 
{ 
    throw new TokenResponseException(errorResponse); 
} 


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

}

Metoda Regulator wywołaniem interfejsu API Google

public async Task<ActionResult> GoogleCalendarAsync(CancellationToken cancellationToken) 
    { 
     var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()). 
      AuthorizeAsync(cancellationToken); 

     if (result.Credential != null) 
     { 
      //var ttt = await result.Credential.RevokeTokenAsync(cancellationToken); 

      //bool x = await result.Credential.RefreshTokenAsync(cancellationToken); 

      var service = new CalendarService(new BaseClientService.Initializer() 
      { 
       HttpClientInitializer = result.Credential, 
       ApplicationName = "GoogleApplication", 
      }); 
      var t = service.Calendars; 

      var tt = service.CalendarList.List(); 

      // Define parameters of request. 
      EventsResource.ListRequest request = service.Events.List("primary"); 
      request.TimeMin = DateTime.Now; 
      request.ShowDeleted = false; 
      request.SingleEvents = true; 
      request.MaxResults = 10; 
      request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime; 

      // List events. 
      Events events = request.Execute(); 
      Debug.WriteLine("Upcoming events:"); 
      if (events.Items != null && events.Items.Count > 0) 
      { 
       foreach (var eventItem in events.Items) 
       { 
        string when = eventItem.Start.DateTime.ToString(); 
        if (String.IsNullOrEmpty(when)) 
        { 
         when = eventItem.Start.Date; 
        } 
        Debug.WriteLine("{0} ({1})", eventItem.Summary, when); 
       } 
      } 
      else 
      { 
       Debug.WriteLine("No upcoming events found."); 
      } 


      //Event myEvent = new Event 
      //{ 
      // Summary = "Appointment", 
      // Location = "Somewhere", 
      // Start = new EventDateTime() 
      //  { 
      //   DateTime = new DateTime(2014, 6, 2, 10, 0, 0), 
      //   TimeZone = "America/Los_Angeles" 
      //  }, 
      // End = new EventDateTime() 
      //  { 
      //   DateTime = new DateTime(2014, 6, 2, 10, 30, 0), 
      //   TimeZone = "America/Los_Angeles" 
      //  }, 
      // Recurrence = new String[] { 
      //  "RRULE:FREQ=WEEKLY;BYDAY=MO" 
      //  }, 
      // Attendees = new List<EventAttendee>() 
      //  { 
      //  new EventAttendee() { Email = "[email protected]" } 
      //  } 
      //}; 

      //Event recurringEvent = service.Events.Insert(myEvent, "primary").Execute(); 

      return View(); 
     } 
     else 
     { 
      return new RedirectResult(result.RedirectUri); 
     } 
    } 

pochodnego klasę FlowMetadata

public class AppFlowMetadata : FlowMetadata 
    { 
     //static readonly string server = ConfigurationManager.AppSettings["DatabaseServer"]; 
     //static readonly string serverUser = ConfigurationManager.AppSettings["DatabaseUser"]; 
     //static readonly string serverPassword = ConfigurationManager.AppSettings["DatabaseUserPassword"]; 
     //static readonly string serverDatabase = ConfigurationManager.AppSettings["DatabaseName"]; 
    ////new FileDataStore("Daimto.GoogleCalendar.Auth.Store") 
    ////new FileDataStore("Drive.Api.Auth.Store") 
    //static DatabaseDataStore databaseDataStore = new DatabaseDataStore(server, serverUser, serverPassword, serverDatabase); 


    private static readonly IAuthorizationCodeFlow flow = 
new ForceOfflineGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer 
{ 
    ClientSecrets = new ClientSecrets 
    { 
     ClientId = "yourClientId", 
     ClientSecret = "yourClientSecret" 

    }, 
    Scopes = new[] 
    { 
CalendarService.Scope.Calendar, // Manage your calendars 
//CalendarService.Scope.CalendarReadonly // View your Calendars 
    }, 
    DataStore = new EFDataStore(), 
}); 

    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["user"]; 
     //if (user == null) 
     //{ 
     // user = Guid.NewGuid(); 
     // controller.Session["user"] = user; 
     //} 
     //return user.ToString(); 

     //var store = new UserStore<ApplicationUser>(new ApplicationDbContext()); 
     //var manager = new UserManager<ApplicationUser>(store); 
     //var currentUser = manager.FindById(controller.User.Identity.GetUserId()); 

     return controller.User.Identity.GetUserId(); 

    } 

    public override IAuthorizationCodeFlow Flow 
    { 
     get { return flow; } 
    } 

    public override string AuthCallback 
    { 
     get { return @"/GoogleApplication/AuthCallback/IndexAsync"; } 
    } 
} 

Entity Framework 6 klasy DataStore

public class EFDataStore : IDataStore 
    { 
     public async Task ClearAsync() 
     { 
      using (var context = new ApplicationDbContext()) 
      { 
       var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
       await objectContext.ExecuteStoreCommandAsync("TRUNCATE TABLE [Items]"); 
      } 
     } 

     public async Task DeleteAsync<T>(string key) 
     { 
      if (string.IsNullOrEmpty(key)) 
      { 
       throw new ArgumentException("Key MUST have a value"); 
      } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      var item = context.GoogleAuthItems.FirstOrDefault(x => x.Key == generatedKey); 
      if (item != null) 
      { 
       context.GoogleAuthItems.Remove(item); 
       await context.SaveChangesAsync(); 
      } 
     } 
    } 

    public Task<T> GetAsync<T>(string key) 
    { 
     if (string.IsNullOrEmpty(key)) 
     { 
      throw new ArgumentException("Key MUST have a value"); 
     } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      var item = context.GoogleAuthItems.FirstOrDefault(x => x.Key == generatedKey); 
      T value = item == null ? default(T) : JsonConvert.DeserializeObject<T>(item.Value); 
      return Task.FromResult<T>(value); 
     } 
    } 

    public async Task StoreAsync<T>(string key, T value) 
    { 
     if (string.IsNullOrEmpty(key)) 
     { 
      throw new ArgumentException("Key MUST have a value"); 
     } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      string json = JsonConvert.SerializeObject(value); 

      var item = await context.GoogleAuthItems.SingleOrDefaultAsync(x => x.Key == generatedKey); 

      if (item == null) 
      { 
       context.GoogleAuthItems.Add(new GoogleAuthItem { Key = generatedKey, Value = json }); 
      } 
      else 
      { 
       item.Value = json; 
      } 

      await context.SaveChangesAsync(); 
     } 
    } 

    private static string GenerateStoredKey(string key, Type t) 
    { 
     return string.Format("{0}-{1}", t.FullName, key); 
    } 
} 

Pochodzące klasa dla GoogleAuthorizationCodeFlow. Włączenie długo działającego tokena odświeżania, który zajmuje się automatycznie "odświeżaniem" tokena, co oznacza po prostu uzyskanie nowego tokena dostępu.

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth

internal class ForceOfflineGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow 
{ 
    public ForceOfflineGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base (initializer) { } 

    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri) 
    { 
     return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl)) 
     { 
      ClientId = ClientSecrets.ClientId, 
      Scope = string.Join(" ", Scopes), 
      RedirectUri = redirectUri, 
      AccessType = "offline", 
      ApprovalPrompt = "force" 
     }; 
    } 
} 

GoogleAuthItem jest używany z EFDataStore

public class GoogleAuthItem 
{ 
    [Key] 
    [MaxLength(100)] 
    public string Key { get; set; } 

    [MaxLength(500)] 
    public string Value { get; set; } 
} 

public DbSet<GoogleAuthItem> GoogleAuthItems { get; set; } 
+0

Dzięki za udostępnienie, wydaje się duże obejście (Y) –

+0

Sprawdź również http://stackoverflow.com/a/24972426/833846 –