Google API V 3.0.Net库和Google OAuth2如何处理刷新令牌



在我的应用程序中,我使用Google API V 3.0.Net库和Google OAuth2来同步Google日历和outlook日历。我正在使用以下代码获取Google.Apis.Calendar.v3.CalendarService服务对象。在身份验证过程中,我存储了Json文件,并从中请求Google.Apis.Auth.Outh2.UserCredential对象。

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

请求服务对象代码为:

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

上面的代码可以很好地获取Calendarservice对象。我的问题是,我的Json文件有刷新和访问令牌。当访问令牌过期时,上面的代码如何处理刷新令牌以获得服务?因为我需要经常调用Calendarservice对象,所以我喜欢为Calendarservice对象实现singleton模式。如何在不频繁调用GetGoogleOAuthCredential的情况下获得日历服务?感谢任何帮助/指导。

过去两天我自己都在想这个问题。除非指定"access_type=offline",否则库不会自动刷新令牌。

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

我要粘贴我正在使用的代码,如果有什么你不明白的地方,就去问吧。我已经读了很多文章,我现在已经很好地完成了这项工作,所以有一些注释代码,它还没有被重构。我希望这能帮助到别人。我使用的NuGet软件包如下:

Google.Apis.Auth.MVC

谷歌.Apis.Calendar.v3

代码:

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

}

控制器调用Google API 的方法

    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 = "johndoe@gmail.com" }
            //        }
            //};
            //Event recurringEvent = service.Events.Insert(myEvent, "primary").Execute();
            return View();
        }
        else
        {
            return new RedirectResult(result.RedirectUri);
        }
    }

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

实体框架6 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);
    }
}

GoogleAuthorizationCodeFlow的派生类。启用长期刷新令牌,自动"刷新"令牌,这只意味着获得一个新的访问令牌。

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

这就是客户端库的底部!这个魔术是自动为你完成的:)

UserCredential同时实现IHttpExecuteInterceptor和IHttpUnsuccessfulResponseHandler。因此,每当访问令牌即将过期或已经过期时,客户端都会调用授权服务器来刷新令牌并获得新的访问令牌(在接下来的60分钟内有效)。

请访问了解更多信息https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#credentials

对我来说,客户端库没有进行刷新,甚至没有创建刷新令牌
我检查令牌是否过期并刷新。(代币的使用寿命为1小时)。资质Token.IsExpired会告诉你它是否过期,凭据。令牌。RefreshToken(userid)将刷新必要的令牌。

相关内容

  • 没有找到相关文章

最新更新