Как установить return_uri для GoogleWebAuthorizationBroker.AuthorizeAsync?

Я пытаюсь использовать API календаря Google в своем веб-приложении, отличном от MVC.NET. (Это, по-видимому, важное различие.)

Я попытался использовать код из этого примера в Google и этот пример в Daimto вместе с некоторыми полезными подсказками из нескольких связанных постов здесь.

Я написал следующий метод:

public void GetUserCredential( String userName )
{
    String clientId = ConfigurationManager.AppSettings[ "Google.ClientId" ];            //From Google Developer console https://console.developers.google.com
    String clientSecret = ConfigurationManager.AppSettings[ "Google.ClientSecret" ];    //From Google Developer console https://console.developers.google.com
    String[] scopes = new string[] {
            Google.Apis.Calendar.v3.CalendarService.Scope.Calendar          
    };

    // here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
    UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync( new ClientSecrets
    {
        ClientId = clientId,
        ClientSecret = clientSecret             
    }, scopes, userName, CancellationToken.None, new FileDataStore( "c:\\temp" ) ).Result;

    // TODO: Replace FileDataStore with DatabaseDataStore
}

Проблема в том, что, когда вызывается страница OAuth2 от Google, redirect_uri продолжает устанавливаться в http://localhost:<some-random-port>/authorize. Я не знаю, как установить это на что-то еще, как в следующем примере URL, созданного AuthorizeAsync:

https://accounts.google.com/o/oauth2/auth?access_type=offline
    &response_type=code
    &client_id=********.apps.googleusercontent.com
    &redirect_uri=http:%2F%2Flocalhost:40839%2Fauthorize%2F
    &scope=https:%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar

Google отвечает на страницу с ошибкой redirect_uri_mismatch с сообщением:

"URI перенаправления в запросе: http://localhost: XXXXX/authorize/ не соответствует зарегистрированному URI переадресации"

Я могу зарегистрировать столько URL-адресов Redirect на странице "Учетные данные консоли разработчика Google". Я не склонен регистрировать порты 65535, и я хочу использовать страницу, отличную от /authorize на моем сайте. В частности, я хочу использовать во время разработки http://localhost:888/Pages/GoogleApiRedirect но не имею в http://localhost:888/Pages/GoogleApiRedirect, где бы я это установил, помимо того, что я сделал в консоли разработчика.

Как явным образом задаю значение redirect_uri? Я также открыт для ответа в форме "Этот подход совершенно неверен".

РЕДАКТИРОВАТЬ:

После игры с этим в прошлый день я обнаружил, что с помощью Client ID/Client Secret для собственного приложения, а не веб-приложения, я могу по крайней мере перейти на страницу авторизации веб-сайта Google, не жалуясь на redirect_uri_mismatch. Это все еще неприемлемо, потому что он по-прежнему возвращается к http://localhost:<some-random-port>/authorize, который находится вне контроля моего веб-приложения.

Ответ 1

Вы можете использовать этот код: (оригинальная идея от http://coderissues.com/info/27512300/how-to-append-login-hint-usergmail-com-to-googlewebauthorizationbroker)

dsAuthorizationBroker.RedirectUri = "my localhost redirect uri";
UserCredential credential = await dsAuthorizationBroker.AuthorizeAsync(...

dsAuthorizationBroker.cs

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Util.Store;

namespace OAuth2
{    
    public class dsAuthorizationBroker : GoogleWebAuthorizationBroker
    {
        public static string RedirectUri;

        public new static async Task<UserCredential> AuthorizeAsync(
            ClientSecrets clientSecrets,
            IEnumerable<string> scopes,
            string user,
            CancellationToken taskCancellationToken,
            IDataStore dataStore = null)
        {
            var initializer = new GoogleAuthorizationCodeFlow.Initializer
            {
                ClientSecrets = clientSecrets,
            };
            return await AuthorizeAsyncCore(initializer, scopes, user,
                taskCancellationToken, dataStore).ConfigureAwait(false);
        }

        private static async Task<UserCredential> AuthorizeAsyncCore(
            GoogleAuthorizationCodeFlow.Initializer initializer,
            IEnumerable<string> scopes,
            string user,
            CancellationToken taskCancellationToken,
            IDataStore dataStore)
        {
            initializer.Scopes = scopes;
            initializer.DataStore = dataStore ?? new FileDataStore(Folder);
            var flow = new dsAuthorizationCodeFlow(initializer);
            return await new AuthorizationCodeInstalledApp(flow, 
                new LocalServerCodeReceiver())
                .AuthorizeAsync(user, taskCancellationToken).ConfigureAwait(false);
        }
    }


    public class dsAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
    {
        public dsAuthorizationCodeFlow(Initializer initializer)
            : base(initializer) { }

        public override AuthorizationCodeRequestUrl
                       CreateAuthorizationCodeRequest(string redirectUri)
        {
            return base.CreateAuthorizationCodeRequest(dsAuthorizationBroker.RedirectUri);
        }
    }    
}