我一直在尝试按照一些教程,我可以找到一个mvc应用程序允许用户验证应用程序并获取访问权限并刷新令牌。不幸的是,我无法找到任何足够清楚的地方,以便我可以跟踪所发生的事情。我从谷歌的sample code开始,然后找到其他一些像this one和this one。
当我运行我的应用程序时,我试图转到http://localhost:61581/Integration/Google/IndexAsync它会遇到最终命中AppFlowMetadata.GetUserId方法的方法然后点击我的自定义TenixDataStore类' GetAsync方法。
令人困惑的事情是
这是我的代码。
AppFlowMetadata类
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Tenix.Domain.Constants;
namespace MyApp.Areas.Integration.Controllers
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = APIConstants.GMailApiKey,
ClientSecret = APIConstants.GmailApiSecret
},
Scopes = new[] {GmailService.Scope.GmailReadonly},
DataStore = new TenixDataStore()
});
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
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["UserID"];
if (user == null) return null;
return user.ToString();
}
}
}
GoogleController
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Google.Apis.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class GoogleController : Controller
{
public async Task IndexAsync(CancellationToken cancellationToken)
{
if (Session["UserID"] == null)
{
Response.Redirect("~/Login.aspx", true);
}
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "Tenix Gmail Integration"
});
}
}
}
}
TenixDataStore类
using System;
using System.Threading.Tasks;
using DataBaseUtilitiesTEN;
using Google.Apis.Json;
using Google.Apis.Util.Store;
using Newtonsoft.Json.Linq;
using Synergy.Extensions;
using Tenix.Domain.Data.Respositories;
using Tenix.Domain.Model.Integration;
using Tenix.Domain.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class TenixDataStore : IDataStore
{
private readonly string conStr = ConnectionStrings.GeneralInfo;
private CredentialService _service;
public TenixDataStore()
{
_service = new CredentialService(new CredentialRepository(conStr));
}
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");
_service.SaveUserCredentials(new UserCredential
{
EmailAddress = key,
AccessToken = (string)access_token,
RefreshToken = (string)refresh_token
});
return Task.Delay(0);
}
public Task DeleteAsync<T>(string key)
{
_service.DeleteCredentials(key);
return Task.Delay(0);
}
public Task<T> GetAsync<T>(string userId)
{
var credentials = _service.GetUserCredentials(userId.To<int>());
var completionSource = new TaskCompletionSource<T>();
if (!string.IsNullOrEmpty(credentials.AccessToken))
completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(credentials.AccessToken));
return completionSource.Task;
}
public Task ClearAsync()
{
return Task.Delay(0);
}
}
}
AuthCallbackController
using Google.Apis.Auth.OAuth2.Mvc;
namespace MyApp.Areas.Integration.Controllers
{
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData
{
get { return new AppFlowMetadata(); }
}
}
}
答案 0 :(得分:1)
花了好几天试图解决这个问题并且没有在google api .net库上取得任何进展之后,我最终选择了我自己的实现,reading their documentation之后至少是我能完全理解的东西。如果有人可以使用代码,这就是我最终的结果。仍然需要做一些重构,但此时它正在发挥作用。
只需确保将AuthorizeResponse和Authorize路由注册为授权重定向uris。
public class GoogleController : Controller
{
private readonly CredentialService _credentialService;
private readonly GoogleEndpoints _endpoints;
public GoogleController()
{
_endpoints = new GoogleEndpoints();
_credentialService = new CredentialService(new CredentialRepository(ConnectionStrings.GeneralInfo));
}
private string AuthorizeUrl
{
get
{
return "/Integration/Google/Authorize";
}
}
private string AuthorizeResponseUrl
{
get
{
return "/Integration/Google/AuthorizeResponse";
}
}
private string SaveResponseUrl
{
get
{
return "/Integration/Google/SaveResponse";
}
}
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&" +
"redirect_uri=" + AuthorizeResponseUrl + "&" +
"state=" + Session["SessionId"] + "&" +
"login_hint=" + Session["Email"] + "&" +
"access_type=offline";
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");
if (access_token == null || access_token.ToString().Trim().Length == 0)
{
//notify devs something went wrong
return View(new GoogleAuthResponse(tokenResponse, false));
}
var credentials = _credentialService.GetUserCredentials(Session["SessionId"].ToString());
credentials.AccessToken = access_token.ToString();
credentials.RefreshToken = refresh_token.ToString();
credentials.EmployeeId = Session["UserId"].To<int>();
_credentialService.SaveUserCredentials(credentials);
return View(new GoogleAuthResponse("Integration successful!", true));
}
return View(new GoogleAuthResponse("Missing state information.", false));
}
}
帮助类来获取谷歌端点。
public class GoogleEndpoints
{
public GoogleEndpoints()
{
using (var client = new WebClient())
{
var response = client.DownloadString("https://accounts.google.com/.well-known/openid-configuration");
var jobject = JObject.Parse(response);
AuthorizationEndpoint = jobject.SelectToken("authorization_endpoint").ToString();
TokenEndpoint = jobject.SelectToken("token_endpoint").ToString();
}
}
public string AuthorizationEndpoint { get; private set; }
public string TokenEndpoint { get; private set; }
}
控制器使用另外两个辅助类来解析json并发布表单数据,但这应该非常简单。