1

I have an ASP.NET web site that access Google Drive using the .NET API v3. I followed the .NET Quickstart to create credentials and generate a token. The redirect_urls section of the JSON credential file looks like this...

"redirect_uris": [
  "http://127.0.0.1/authorize/",
  "http://127.0.0.1/authorize",
  "https://127.0.0.1/authorize/",
  "https://127.0.0.1/authorize"
],

This all works fine locally. When I debug the site, it pops up the Google oAuth screen, and asks me to authenticate. Once done, the site works fine, and the pages that access Google Drive can do so.

I now want to deploy to a staging site, so created new oAuth credentials, using the same details as before, but with the site's domain instead of 127.0.0.1. However, when I deploy the site, any attempt to load a page that accesses Google results in a time-out.

The event viewer on the server shows the following error (sensitive data obfuscated)...

System.Net.HttpListenerException (5): Access is denied.
   at System.Net.HttpListener.AddPrefixCore(String registeredPrefix)
   at System.Net.HttpListener.AddAllPrefixes()
   at System.Net.HttpListener.Start()
   at Google.Apis.Auth.OAuth2.LocalServerCodeReceiver.StartListener()
   at Google.Apis.Auth.OAuth2.LocalServerCodeReceiver.ReceiveCodeAsync(AuthorizationCodeRequestUrl url, CancellationToken taskCancellationToken)
   at Google.Apis.Auth.OAuth2.AuthorizationCodeInstalledApp.AuthorizeAsync(String userId, CancellationToken taskCancellationToken)
   at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(Initializer initializer, IEnumerable`1 scopes, String user, CancellationToken taskCancellationToken, IDataStore dataStore, ICodeReceiver codeReceiver)
   at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(ClientSecrets clientSecrets, IEnumerable`1 scopes, String user, CancellationToken taskCancellationToken, IDataStore dataStore, ICodeReceiver codeReceiver)
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToPendingTasks(Task task)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(Int32 componentId, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.CreateInitialRenderAsync(Type componentType, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.RenderComponentAsync(Type componentType, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c__11`1.<<InvokeAsync>b__11_0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.PrerenderedServerComponentAsync(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.RenderComponentAsync(ViewContext viewContext, Type componentType, RenderMode renderMode, Object parameters)
   at Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
   at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, Int32 i, Int32 count)
   at MyWebSite.Areas.General.Pages.Areas_General_Pages__Host.<ExecuteAsync>b__14_1() in C:\PathToWebSite\Areas\General\Pages\_Host.cshtml:line 24
   at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
   at MyWebSite.Areas.General.Pages.Areas_General_Pages__Host.ExecuteAsync() in C:\PathToWebSite\Areas\General\Pages\_Host.cshtml:line 6
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

I almost looks as if it's tried to pop up the oAuth page in a browser window on the server (which it won't manage, as there won't be a desktop associated with the user that runs IIS), and then times out waiting for someone to use that page. I could be wrong there, but if I'm right, I don't understand why it's trying to do this, as the whole point of creating credentials for a web application is that the person authenticating will not be on the server. However, I could be completely wrong with this assumption anyway!

The site is written using Blazor, but I don't think that's relevant, as I added an MVC controller as a test, and had exactly the same issue.

Anyone any idea why this is happening, and what I can do about it? Thanks.

6
  • 1
    I'm guessing you only added your local IPs to the authorized redirect URIs for the auth client. I would recommend having one set of creds for prod, one for staging, and one for dev. Use .NET configuration to set them per environment. Commented Dec 14, 2021 at 19:32
  • 1
    You need to whitelist the Fully Qualified Redirect URI in Google OAuth Settings including www or non-www (depending on what you're using) Commented Dec 14, 2021 at 19:56
  • @foxtrotuniform6969 Not sure what you mean. I did create a new set of credentials for the staging site (see my 3rd paragraph), and used the site's domain instead of my local IP. Did I misunderstand you? Commented Dec 15, 2021 at 15:47
  • @PramilGawande Please could you explain what you mean. Where do I whitelist a URI? If you mean adding them to the "Authorized redirect URIs" in the credentials, then I did that (see my 3rd paragraph) using the exact URI that the site uses. Please can you clarify what you mean. Thanks Commented Dec 15, 2021 at 15:50
  • @Avrohom What I meant is to add both the www and non-www versions of your URI's like http:// example.com and http:// www.example.com I ran into the same problem sometime back. Also, check the developer tools if you're getting any additional errors in the console. Commented Dec 15, 2021 at 19:07

1 Answer 1

1

If you are using the code in that quickstart, then it won't work in a web app. The code there is for a desktop app, and uses your browser to open the oAuth screen.

That page really doesn't make this clear, I picked this up from this comment on a GitHub issue. To quote (spelling mistakes included!)...

GoogleWebAuthorizationBroker.AuthorizeAsync is for iinstalled applicatons. Its going to try and open the authorization web browser on the server which is not going to work.

Your going to need something like Web applications (ASP.NET MVC)

However, the code in that link is for MVC, and I don't know if it will work with Blazor, as auth is a whole new ball game there. If you read the previous comment in that GitHub issue, you'll see the suggestion that you perform user auth in a standard ASP.NET Core part of the app, then passing the IGoogleAuthProvider into Blazor.

Hope that helps, even if it isn't what you wanted to hear!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.