Google OAuth 在本地工作,但在服务器上超时
Google OAuth works locally, but times out on server
在本地测试时,我能够进行身份验证并使用 Oauth2Service
检索创建帐户的基本信息。一旦上线,用户就可以访问 google 同意页面,但是一旦他们选择了一个帐户(或输入了他们的 google 凭据),它就会停留在加载屏幕上,直到达到我预定义的超时60 秒。
我目前收到以下错误:
The operation has timed out.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.TimeoutException: The operation has timed out.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[TimeoutException: The operation has timed out.]
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.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
System.Web.Mvc.Async.WrappedAsyncVoid
1.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);
对于上下文,这里是包含class:
[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.核心
- Google.Apis.Oauth2.v2
其他注意事项:
- 托管在 Windows Web Server 2008 (IIS 7)
- 作为子域托管
- 我是 subdomain/domain
的经过验证的所有者
- 子域有 SSL
- 子域和域已添加到 Google API 项目经理的 'allowed domains' 列表中
- 在 Google API 管理器
中为 OAuth 客户端 ID 设置了重定向 URL
- 已验证 API 管理器和应用程序之间的 ClientID 和 ClientSecret 匹配
- Token storage in database is setup and working
- OAuth 客户端中设置了隐私政策
- Server's system clock is valid.
关于如何解决这个问题有什么建议或想法吗?
如果有任何其他信息有帮助,请告诉我!
将解决方案迁移到 Azure,而不是将其托管在我自己的服务器上,并解决了问题。
在本地测试时,我能够进行身份验证并使用 Oauth2Service
检索创建帐户的基本信息。一旦上线,用户就可以访问 google 同意页面,但是一旦他们选择了一个帐户(或输入了他们的 google 凭据),它就会停留在加载屏幕上,直到达到我预定义的超时60 秒。
我目前收到以下错误:
The operation has timed out.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.TimeoutException: The operation has timed out.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[TimeoutException: The operation has timed out.] 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.WrappedAsyncVoid
1.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);
对于上下文,这里是包含class:
[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.核心
- Google.Apis.Oauth2.v2
其他注意事项:
- 托管在 Windows Web Server 2008 (IIS 7)
- 作为子域托管
- 我是 subdomain/domain 的经过验证的所有者
- 子域有 SSL
- 子域和域已添加到 Google API 项目经理的 'allowed domains' 列表中
- 在 Google API 管理器 中为 OAuth 客户端 ID 设置了重定向 URL
- 已验证 API 管理器和应用程序之间的 ClientID 和 ClientSecret 匹配
- Token storage in database is setup and working
- OAuth 客户端中设置了隐私政策
- Server's system clock is valid.
关于如何解决这个问题有什么建议或想法吗? 如果有任何其他信息有帮助,请告诉我!
将解决方案迁移到 Azure,而不是将其托管在我自己的服务器上,并解决了问题。