【发布时间】:2017-06-20 14:50:38
【问题描述】:
在本地测试时,我能够进行身份验证并使用Oauth2Service 检索用于创建帐户的基本信息。一旦上线,用户就可以访问谷歌同意页面,但是一旦他们选择了一个帐户(或输入他们的谷歌凭据),它就会停留在加载屏幕上,直到它达到我预定义的 60 秒超时。
我目前收到以下错误:
操作已超时。
说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。
异常详细信息:System.TimeoutException:操作已超时。
来源错误:
在执行当前 Web 请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常起源和位置的信息。
堆栈跟踪:
[TimeoutException: 操作超时。] System.Web.Mvc.Async.c__DisplayClass8.b__4() +189 System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult) +427 System.Web.Mvc.Async.c__DisplayClass37.b__36(IAsyncResult asyncResult) +23 System.Web.Mvc.Async.AsyncInvocationWithFilters.b__3d() +112 System.Web.Mvc.Async.c__DisplayClass46.b__3f() +452 System.Web.Mvc.Async.c__DisplayClass46.b__3f() +452 System.Web.Mvc.Async.c__DisplayClass46.b__3f() +452 System.Web.Mvc.Async.c__DisplayClass33.b__32(IAsyncResult asyncResult) +15 System.Web.Mvc.Async.c__DisplayClass2b.b__1c() +37 System.Web.Mvc.Async.c__DisplayClass21.b__1e(IAsyncResult asyncResult) +241 System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29 System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +19 System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +51 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +111 System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +282
我能够缩小它挂在这条线上的位置:
var token = await Flow.ExchangeCodeForTokenAsync(UserId, authorizationCode.Code, returnUrl,
taskCancellationToken).ConfigureAwait(false);
对于上下文,这里是包含类:
[AuthorizationCodeActionFilter]
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected new IAuthorizationCodeFlow Flow { get { return FlowData.Flow; } }
protected new string UserId
{ get { return FlowData.GetUserId(this); } }
[AsyncTimeout(60000)]
public async override Task<ActionResult> IndexAsync(AuthorizationCodeResponseUrl authorizationCode,
CancellationToken taskCancellationToken)
{
if (string.IsNullOrWhiteSpace(authorizationCode.Code))
{
var errorResponse = new TokenErrorResponse(authorizationCode);
return OnTokenError(errorResponse);
}
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);
return new RedirectResult(oauthState);
}
protected override FlowMetadata FlowData
{
get { return new GoogleFlowMetaData(); }
}
protected override ActionResult OnTokenError(TokenErrorResponse errorResponse)
{
throw new TokenResponseException(errorResponse);
}
}
对于其他上下文,这是我对 FlowMetaData 的实现:
public class GoogleFlowMetaData : FlowMetadata
{
public static readonly IAuthorizationCodeFlow flow =
new ForceOfflineGoogleAuthorizationCodeFLow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = ConfigurationManager.AppSettings["Google.ClientID"],
ClientSecret = ConfigurationManager.AppSettings["Google.ClientSecret"]
},
Scopes = new[] {
Scope.UserinfoProfile,
Scope.UserinfoEmail
},
DataStore = new GoogleDataStore(new DBContext())
});
public override string GetUserId(Controller controller)
{
var user = controller.User.Identity;
if (controller.User.Identity.IsAuthenticated)
{
return controller.User.Identity.GetUserId();
}
var userId = (string)controller.Session["google_userId"];
if (string.IsNullOrWhiteSpace(userId))
{
userId = Guid.NewGuid().ToString();
controller.Session["google_userId"] = userId;
}
return userId;
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
internal class ForceOfflineGoogleAuthorizationCodeFLow : GoogleAuthorizationCodeFlow
{
public ForceOfflineGoogleAuthorizationCodeFLow(GoogleAuthorizationCodeFlow.Initializer initializer)
: base(initializer)
{
}
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
{
var ss = new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl));
ss.AccessType = "offline";
ss.ApprovalPrompt = "force";
ss.ClientId = ClientSecrets.ClientId;
ss.Scope = string.Join(" ", Scopes);
ss.RedirectUri = redirectUri;
return ss;
}
}
}
使用的 Nuget 包:
- Google.Apis
- Google.Apis.Auth
- Google.Apis.Auth.Mvc
- Google.Apis.Core
- Google.Apis.Oauth2.v2
其他注意事项:
- 托管在 Windows Web Server 2008 (IIS 7) 上
- 作为子域托管
- 我是子域/域的经过验证的所有者
- 子域有 SSL
- 子域和域已添加到项目的 Google API 管理器的“允许域”列表中
- 在 Google API Manager 中为 OAuth 客户端 ID 设置了重定向 URL
- 验证 API Manager 和应用程序之间的 ClientID 和 ClientSecret 匹配
- Token storage in database is setup and working
- 隐私政策在 OAuth 客户端中设置
- Server's system clock is valid.
关于如何解决此问题的任何建议或想法? 如果有任何其他信息可以提供帮助,请告诉我!
【问题讨论】:
标签: asp.net-mvc asynchronous oauth-2.0 google-oauth google-api-dotnet-client