从Google API响应数据库中保存OAuth令牌? [英] Saving OAuth tokens from Google API response to database?

查看:284
本文介绍了从Google API响应数据库中保存OAuth令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个MVC应用程序,该应用程序将验证我们网站上要求我们将事件发布到日历中的用户。我安装了.NET C#客户端库以及Auth和MVC包。



使用Google.Apis.Auth.OAuth2.Mvc我无法访问我的 IDataStore 实现中的 TokenResponse ,而是将URL保存为令牌,例如localhost:6055 / calendar / calendarasync / deantest04556734

我发现的所有示例似乎都过时了,没有使用Mvc包,也没有实现DataStore来保存到数据库,所以我已使用使用Daimto示例中的代码片段和官方示例开始。



任何人都可以指向正确的方向或查看我的代码的任何问题吗?

Database> DataStore

 public class DatabaseDataStore:IDataStore 
{
私有SqlConnection连接;
$ b $ public DatabaseDataStore(SqlConnection sqlConn)
{
if(sqlConn!= null)
{
connection = sqlConn;

if(connection.State!= ConnectionState.Open)
connection.Open();
}
}

///< summary>
///存储给定键的给定值。它在
///< see cref =FolderPath/>中创建一个新文件(命名为< see cref =GenerateStoredKey/>)。
///< / summary>
///< typeparam name =T>要存储在数据存储中的类型< / typeparam>
///< param name =key>键< / param>
///< param name =value>要存储在数据存储区中的值< / param>
public Task StoreAsync< T>(string key,T value)
{
if(string.IsNullOrEmpty(key))
{
throw new ArgumentException(Key必须有一个值);
}

var serialised = NewtonsoftJsonSerializer.Instance.Serialize(value);
string userId = getUserId(key);

if(userId == null)
{
insertUserData(key,serialized);
}
else
{
updateUserData(userId,key,serialized);
}

return Task.Delay(0);
}

///< summary>
///返回给定键的存储值或< c> null< / c>如果匹配文件(<参见cref =GenerateStoredKey/>
/// in<参见cref =FolderPath/>不存在)
///< / summary>
///< typeparam name =T>要检索的类型< / typeparam>
///< param name =key>数据存储< / param>
///<返回>存储对象< /返回>
public Task< T> GetAsync< T>(字符串键)
{
// Key是用AuthorizeAsync发送的用户字符串
if(string.IsNullOrEmpty(key))
{
throw new ArgumentException(Key必须有一个值);
}

TaskCompletionSource< T> tcs = new TaskCompletionSource< T>();
string refreshToken = null;

//尝试并在数据库中找到行。
using(SqlCommand cmd = new SqlCommand(Calendar_GetRefreshToken,connection))
{
cmd.CommandType = Comman dType.StoredProcedure;
cmd.CommandTimeout = 2700;
尝试
{
cmd.Parameters.AddWithValue(@ username,key);
SqlDataReader reader = cmd.ExecuteReader(); $(阅读器。阅读())
{
refreshToken = reader [RefreshToken]。
}
reader.Dispose();

if(refreshToken == null)
{
//我们没有记录,所以我们请求用户。
tcs.SetResult(default(T));
}
else
{
try
{
//我们有它我们使用它。
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize< T>(refreshToken));
}
catch(Exception ex)
{
tcs.SetException(ex);


$ b catch(Exception ex)
{
//logger.Error(\"Method:CheckLocalProperty - id:+ propId + - 错误:+ ex.Message);
返回null;
}
}

return tcs.Task;
}

///< summary>
///清除数据存储中的所有值。此方法将删除< see cref =FolderPath/>中的所有文件。
///< / summary>
public Task ClearAsync()
{
//删除表中的所有数据。
string truncateString =truncate table [dbo]。[tblCactusGoogleUsers];
SqlCommand commandins = new SqlCommand(truncateString,connection);
commandins.ExecuteNonQuery();

return Task.Delay(0);
}

///< summary>
///删除给定的键。它删除< see cref =GenerateStoredKey/>在<请参阅cref =FolderPath/>中的命名文件。
///< / summary>
///< param name =key>要从数据存储区删除的密钥< / param>
public Task DeleteAsync< T>(string key)
{
if(string.IsNullOrEmpty(key))
{
throw new ArgumentException(Key must have a值);
}

deleteUserData(key);

return Task.Delay(0);
}

///< summary>根据键和类的类型创建唯一的存储键。< / summary>
///< param name =key>对象键< / param>
///< param name =t>要存储或检索的类型< / param>
public static string GenerateStoredKey(string key,Type t)
{
return string.Format({0} - {1},t.FullName,key);

$ b私有字符串getUserId(字符串值)
{
using(SqlCommand cmd = new SqlCommand(Calendar_GetUserId,connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
尝试
{
cmd.Parameters.AddWithValue(@ username,value);
SqlDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
return reader [UserId]。ToString();
}
reader.Dispose();
}
catch(Exception ex)
{
//logger.Error(\"Method:CheckLocalProperty - id:+ propId + - Error:+ ex.Message) ;
返回null;
}
}
返回null;


private void insertUserData(string key,string value)
{
using(SqlCommand cmd = new SqlCommand(Calendar_InsertUser,connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
尝试
{
cmd.Parameters.AddWithValue(@ token,value);
cmd.Parameters.AddWithValue(@ username,key);
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//logger.Error(\"Method:insertUserData - id:+ key + - Error:+ ex.Message) ;



$ b private void updateUserData(string userId,string key,string value)
{
using(SqlCommand cmd =新的SqlCommand(Calendar_UpdateUser,连接))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
尝试
{
cmd.Parameters.AddWithValue(@ userid,userId);
cmd.Parameters.AddWithValue(@ username,key);
cmd.Parameters.AddWithValue(@ token,value);
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//logger.Error(\"Method:updateUserData - id:+ key + - Error:+ ex.Message) ;



$ b private void deleteUserData(string key)
{
using(SqlCommand cmd = new SqlCommand(Calendar_DeleteUser ,connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
尝试
{
cmd.Parameters.AddWithValue(@ username,key);
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//logger.Error(\"Method:deleteUserData - id:+ key + - Error:+ ex.Message) ;
}
}
}
}

FlowMetadata

  public class AppAuthFlowMetadata:FlowMetadata 
{
private SqlConnection connection;
private readonly IAuthorizationCodeFlow流程;
$ b $ public AppAuthFlowMetadata(SqlConnection sqlConn,string clientId,string clientSecret)
{
if(sqlConn!= null)
{
connection = sqlConn;

if(connection.State!= ConnectionState.Open)
connection.Open();

flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret
},
Scopes = new [] {
CalendarService.Scope.Calendar
},
DataStore = new DatabaseDataStore(connection)
});
}
else
{
throw new ArgumentException(sqlConn is null);



public override string GetUserId(Controller controller)
{
/ * TODO - 从表单获取UserId * /
返回controller.User.Identity.Name;
}

public override IAuthorizationCodeFlow Flow
{
get {return flow; }




控制器 p>

 公共类CalendarController:控制器
{
CalendarService服务;
string CLIENT_ID = ConfigurationManager.AppSettings [GoogleClientID]。ToString();
string CLIENT_SECRET = ConfigurationManager.AppSettings [GoogleClientSecret]。ToString();

[授权]
公共异步任务< ActionResult> CalendarAsync(CancellationToken cancellationToken)
{
ViewBag.Message =您的日历页面。;
$ b $ var result =等待新的AuthorizationCodeMvcApp(this,new AppAuthFlowMetadata(
new SqlConnection(ConfigurationManager.ConnectionStrings [HFConnString] .ConnectionString),
CLIENT_ID,
CLIENT_SECRET )
).AuthorizeAsync(cancellationToken);

if(result.Credential == null)
返回新的RedirectResult(result.RedirectUri);

service = new CalendarService(新BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName =ASP.NET Google API MVC示例
});
...
}
}


解决方案

在最近两天里花费了我自己的时间。我要粘贴我使用的代码,如果有什么你不明白的,就问。我已经阅读了很多文章,而且我现在正在努力工作,所以有一些评论代码,它还没有被重构。我希望这会帮助某人。我使用的NuGet包是这些:

Google.Apis.Auth.MVC



Google.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>获取授权码流。< / summary>
protected IAuthorizationCodeFlow Flow {get {return FlowData.Flow; }}

///< summary>
///获取用户标识符。潜在的逻辑是使用会话变量来检索该信息。
///< / summary>
保护字符串UserId {get {return FlowData.GetUserId(this); }}

///< summary>
///接收包含错误或代码的授权码的授权回调。
///如果有可用的代码,该方法会将该coed与访问令牌交换并重定向回原始的
///页面,该页面初始化auth过程(使用状态参数)。
///< para>
///当前超时设置为10秒。您可以通过设置
///< see cref =System.Web.Mvc.AsyncTimeoutAttribute/>来更改默认行为。在你的控制器上有不同的值。
///< / para>
///< / summary>
///< param name =authorizationCode>包含代码或错误的授权码回应< / param>
///< param name =taskCancellationToken>取消令牌取消操作。< / param>
///<返回>
///将动作重定向到状态参数或<请参阅cref =OnTokenError/>在出现错误的情况下。
///< / returns>
[AsyncTimeout(60000)]
公共异步覆盖任务< ActionResult> IndexAsync(AuthorizationCodeResponseUrl authorizationCode,
CancellationToken taskCancellationToken)
{
if(string.IsNullOrEmpty(authorizationCode.Code))
{
var errorResponse = new TokenErrorResponse(authorizationCode);
Logger.Info(收到错误,响应是:{0},errorResponse);
Debug.WriteLine(收到一个错误,响应是:{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);

//提取正确的状态。
var oauthState = await AuthWebUtility.ExtracRedirectFromState(Flow.DataStore,UserId,
authorizationCode.State).ConfigureAwait(false);

返回新的RedirectResult(oauthState);
}

保护覆盖Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
{
get {return new AppFlowMetadata(); }
}
$ b保护覆盖ActionResult OnTokenError(TokenErrorResponse errorResponse)
{
throw new TokenResponseException(errorResponse);
}


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

}



控制器调用Google API的方法

  public async Task< ActionResult> GoogleCalendarAsync(CancellationToken cancellationToken)
{
var result =等待新的AuthorizationCodeMvcApp(this,new AppFlowMetadata())。
AuthorizeAsync(cancellationToken);

if(result.Credential!= null)
{
// var ttt =等待结果.Credential.RevokeTokenAsync(cancellationToken);

// bool x =等待结果.Credential.RefreshTokenAsync(cancellationToken);

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

var tt = service.CalendarList.List();

//定义请求的参数。
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;

//列出事件。
Events events = request.Execute();
Debug.WriteLine(即将发生的事件:);
if(events.Items!= null&& event.Items.Count> 0)
{
foreach(event.tems中的var eventItem)
{
string when = eventItem.Start.DateTime.ToString();
if(String.IsNullOrEmpty(when))
{
when = eventItem.Start.Date;
}
Debug.WriteLine({0}({1}),eventItem.Summary,when);
}
}
else
{
Debug.WriteLine(找不到即将发生的事件);
}


// 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
//},
//重复= new String [] {
//RRULE:FREQ = WEEKLY; BYDAY = MO
//},
// Attendees = new List< EventAttendee>()
// {
// new EventAttendee(){电子邮件=johndoe @ gmail。 com}
//}
//};

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

return View();
}
else
{
返回新的RedirectResult(result.RedirectUri);




$ b $ Flow $元的派生类

  public class AppFlowMetadata:FlowMetadata 
{
// static readonly string server = ConfigurationManager.AppSettings [DatabaseServer];
//静态只读字符串serverUser = ConfigurationManager.AppSettings [DatabaseUser];
//静态只读字符串serverPassword = ConfigurationManager.AppSettings [DatabaseUserPassword];
//静态只读字符串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 =
新的ForceOfflineGoogleAuthorizationCodeFlow(新的GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets =新的ClientSecrets
{
ClientId =yourClientId,
ClientSecret =yourClientSecret


Scopes = new []
{
CalendarService.Scope.Calendar, //管理您的日历
//CalendarService.Scope.CalendarReadonly //查看您的日历
},
DataStore = new EFDataStore(),
});

public override string GetUserId(Controller controller)
{
//在这个例子中,我们使用session来存储用户标识符。
//这不是最好的做法,因为你应该有一个逻辑来识别
//一个用户。您可能想使用OpenID Connect。
//您可以在以下链接阅读有关该协议的更多信息:
// 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; }




$ b $实体框架6 DataStore类

  public class EFDataStore:IDataStore 
{
public async任务ClearAsync()
{
using( var context = new ApplicationDbContext())
{
var objectContext =((IObjectContextAdapter)context).ObjectContext;
等待objectContext.ExecuteStoreCommandAsync(TRUNCATE TABLE [Items]);


$ b $ public async任务DeleteAsync< T>(string key)
{
if(string.IsNullOrEmpty(key))
{
抛出新的ArgumentException(Key必须有一个值);


使用(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必须有一个值);


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



公共异步任务StoreAsync< T>(字符串键,T值)
{
if(string.IsNullOrEmpty(key))
{
抛出新的ArgumentException(Key必须有一个值);


使用(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);




$ b

GoogleAuthorizationCodeFlow的派生类。使用长寿命的刷新标记来自动刷新标记,这意味着获取新的标记。

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

 内部类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,
Ap provalPrompt =force
};




$ b

GoogleAuthItem与EFDataStore一起使用



$









$ b $ Max
$ b $ public string Key {get;组; }

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

public DbSet< GoogleAuthItem> GoogleAuthItems {get;组; }


I'm building an MVC app that will authenticate users on our website that want us to post events into their calendars. I've installed the .NET C# client library as well as the Auth and MVC packages.

Using Google.Apis.Auth.OAuth2.Mvc I'm having trouble accessing the TokenResponse in my implementation of IDataStore, instead it saves the URL as the token i.e. localhost:6055/calendar/calendarasync/deantest04556734

All of the examples that I've found seem to be out of date, not using the Mvc package and not implementing a DataStore to save to a database so I've usedUsing bits of code from daimto's example and the official examples to get started.

Can anyone point me in the right direction or see any issues with my code?

DatabaseDataStore

public class DatabaseDataStore : IDataStore
{
    private SqlConnection connection;

    public DatabaseDataStore(SqlConnection sqlConn)
    {
        if (sqlConn != null)
        {
            connection = sqlConn;

            if(connection.State != ConnectionState.Open)
                connection.Open();
        }
    }

    /// <summary>
    /// Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in 
    /// <see cref="FolderPath"/>.
    /// </summary>
    /// <typeparam name="T">The type to store in the data store</typeparam>
    /// <param name="key">The key</param>
    /// <param name="value">The value to store in the data store</param>
    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);
        string userId = getUserId(key);

        if (userId == null)
        {
            insertUserData(key, serialized);
        }
        else
        {
            updateUserData(userId, key, serialized);
        }

        return Task.Delay(0);
    }

    /// <summary>
    /// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
    /// in <see cref="FolderPath"/> doesn't exist.
    /// </summary>
    /// <typeparam name="T">The type to retrieve</typeparam>
    /// <param name="key">The key to retrieve from the data store</param>
    /// <returns>The stored object</returns>
    public Task<T> GetAsync<T>(string key)
    {
        //Key is the user string sent with AuthorizeAsync
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("Key MUST have a value");
        }

        TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
        string refreshToken = null;

        // Try and find the Row in the DB.
        using (SqlCommand cmd = new SqlCommand("Calendar_GetRefreshToken", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 2700;
            try
            {
                cmd.Parameters.AddWithValue("@username", key);
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    refreshToken = reader["RefreshToken"].ToString();
                }
                reader.Dispose();

                if (refreshToken == null)
                {
                    // we don't have a record so we request it of the user.
                    tcs.SetResult(default(T));
                }
                else
                {
                    try
                    {
                        // we have it we use that.
                        tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(refreshToken));
                    }
                    catch (Exception ex)
                    {
                        tcs.SetException(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                //logger.Error("Method:CheckLocalProperty - id: " + propId + " - Error:" + ex.Message);
                return null;
            }
        }

        return tcs.Task;
    }

    /// <summary>
    /// Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
    /// </summary>
    public Task ClearAsync()
    {
        // Removes all data from the Table.
        string truncateString = "truncate table [dbo].[tblCactusGoogleUsers] ";
        SqlCommand commandins = new SqlCommand(truncateString, connection);
        commandins.ExecuteNonQuery();

        return Task.Delay(0);
    }

    /// <summary>
    /// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in <see cref="FolderPath"/>.
    /// </summary>
    /// <param name="key">The key to delete from the data store</param>
    public Task DeleteAsync<T>(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("Key MUST have a value");
        }

        deleteUserData(key);

        return Task.Delay(0);
    }

    /// <summary>Creates a unique stored key based on the key and the class type.</summary>
    /// <param name="key">The object key</param>
    /// <param name="t">The type to store or retrieve</param>
    public static string GenerateStoredKey(string key, Type t)
    {
        return string.Format("{0}-{1}", t.FullName, key);
    }

    private string getUserId(string value)
    {
        using (SqlCommand cmd = new SqlCommand("Calendar_GetUserId", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 2700;
            try
            {
                cmd.Parameters.AddWithValue("@username", value);
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    return reader["UserId"].ToString();
                }
                reader.Dispose();
            }
            catch (Exception ex)
            {
                //logger.Error("Method:CheckLocalProperty - id: " + propId + " - Error:" + ex.Message);
                return null;
            }
        }
        return null;
    }

    private void insertUserData(string key, string value)
    {
        using (SqlCommand cmd = new SqlCommand("Calendar_InsertUser", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 2700;
            try
            {
                cmd.Parameters.AddWithValue("@token", value);
                cmd.Parameters.AddWithValue("@username", key);
                cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                //logger.Error("Method:insertUserData - id: " + key + " - Error:" + ex.Message);
            }
        }
    }

    private void updateUserData(string userId, string key, string value)
    {
        using (SqlCommand cmd = new SqlCommand("Calendar_UpdateUser", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 2700;
            try
            {
                cmd.Parameters.AddWithValue("@userid", userId);
                cmd.Parameters.AddWithValue("@username", key);
                cmd.Parameters.AddWithValue("@token", value);
                cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                //logger.Error("Method:updateUserData - id: " + key + " - Error:" + ex.Message);
            }
        }
    }

    private void deleteUserData(string key)
    {
        using (SqlCommand cmd = new SqlCommand("Calendar_DeleteUser", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 2700;
            try
            {
                cmd.Parameters.AddWithValue("@username", key);
                cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                //logger.Error("Method:deleteUserData - id: " + key + " - Error:" + ex.Message);
            }
        }
    }
}

FlowMetadata

public class AppAuthFlowMetadata : FlowMetadata
{
    private SqlConnection connection;
    private readonly IAuthorizationCodeFlow flow;

    public AppAuthFlowMetadata(SqlConnection sqlConn, string clientId, string clientSecret)
    {
        if (sqlConn != null)
        {
            connection = sqlConn;

            if (connection.State != ConnectionState.Open)
                connection.Open();

            flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
            {
                ClientSecrets = new ClientSecrets
                {
                    ClientId = clientId,
                    ClientSecret = clientSecret
                },
                Scopes = new[] { 
                    CalendarService.Scope.Calendar
                },
                DataStore = new DatabaseDataStore(connection)
            });
        }
        else
        {
            throw new ArgumentException("sqlConn is null");
        }
    }

    public override string GetUserId(Controller controller)
    {
        /* TODO - Get UserId from form post */
        return controller.User.Identity.Name;
    }

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

Controller

public class CalendarController : Controller
{
    CalendarService service;
    string CLIENT_ID = ConfigurationManager.AppSettings["GoogleClientID"].ToString();
    string CLIENT_SECRET = ConfigurationManager.AppSettings["GoogleClientSecret"].ToString();

    [Authorize]
    public async Task<ActionResult> CalendarAsync(CancellationToken cancellationToken)
    {
        ViewBag.Message = "Your calendar page.";

        var result = await new AuthorizationCodeMvcApp(this, new AppAuthFlowMetadata(
                        new SqlConnection(ConfigurationManager.ConnectionStrings["HFConnString"].ConnectionString),
                        CLIENT_ID,
                        CLIENT_SECRET)
                     ).AuthorizeAsync(cancellationToken);

        if (result.Credential == null)
            return new RedirectResult(result.RedirectUri);

        service = new CalendarService(new BaseClientService.Initializer
            {
                HttpClientInitializer = result.Credential,
                ApplicationName = "ASP.NET Google APIs MVC Sample"
            });
        ...
    }
}

解决方案

Spent the last two days figuring this out myself. I'm gonna paste the code I'm using and if there is anything you don't understand, just ask. I have read so many posts and I litteraly got this working right now so there is some commented code and it has not been refactored yet. I hope this will help someone. The NuGet packages I'm using are these:

Google.Apis.Auth.MVC

Google.Apis.Calendar.v3

Code:

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

}

Method for Controller calling 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);
        }
    }

Derived class of 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 DataStore class

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

Derived class for GoogleAuthorizationCodeFlow. Enabling long-lived refresh token that take care of automatically "refreshing" the token, which simply means getting a new access token.

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 is used with 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; }

这篇关于从Google API响应数据库中保存OAuth令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆