Google OAuth2 из .NET Web API - PullRequest
       16

Google OAuth2 из .NET Web API

0 голосов
/ 03 мая 2019

Прежде всего, я хотел бы сказать, что есть шанс, что на этот вопрос уже был дан ответ в другом месте, но, пожалуйста, внимательно прочитайте мой вопрос, прежде чем прийти к такому выводу, потому что я видел так много примеров, которые сейчас что-то делают в по-другому или на другом языке, используя компоненты, которые я не могу легко интерпретировать в то, что я знаю.

Я пытаюсь создать веб-API ASP.NET, который может авторизоваться через Google или получить access_token для дальнейшего взаимодействия с API Google REST. Это все, что я хочу, чтобы это было возможно - более поздняя передача с API Google выходит за рамки моего Web API - это просто access_token (либо с согласия пользователя, либо с помощью маркера обновления, если пользователь уже дал на это согласие).

Я пробовал использовать несколько разных подходов, в том числе использовать клиентскую библиотеку Google API для .NET. Я не собираюсь публиковать код на данном этапе, так как сейчас я в значительной степени смущен тем, какой подход следует использовать в этом сценарии.

Мое последнее чтение было таким: https://developers.google.com/identity/protocols/OAuth2WebServer, и мне бы очень хотелось, чтобы они добавили сюда несколько примеров C #, а не только PHP, Python и Ruby.

Для того, что я пытаюсь сделать, я не уверен, какой тип учетных данных я должен использовать Oauth или Сервисная учетная запись и если OAuth тогда это должно быть для типа приложения Веб-приложение или Другое ?

Кажется, что есть большая разница, которую я выбираю, но я не смог найти руководство, которое объясняет это, так что нет никаких сомнений, что использовать. Однако я знаю, что некоторые вещи, которые я пробовал, работали с моего локального компьютера, но не сразу, как только они были опубликованы в IIS.

Так что, если вы можете объяснить мне, какой из многих подходов является правильным для моего сценария, это будет очень ценно - любые примеры кода также приветствуются.

Подводя итог:

Я хочу создать веб-API ASP.NET, который может получить токен доступа от Google (токен доступа для взаимодействия с API Google) и ничего больше. Мне необходимо получить токен доступа через токен обновления, поэтому владелец учетной записи Google должен предоставить доступ только один раз.

Любое дальнейшее общение с API Google будет происходить через обычные вызовы REST и выходит за рамки моего Web API.

1 Ответ

0 голосов
/ 11 мая 2019

У меня была та же проблема, и я шел по тем же дорогам, что и вы, но, имея некоторый опыт написания реализаций OAuth, это было не так уж сложно. Может быть, вы можете использовать то, что я сделал, что, кажется, работает для меня. Вам нужно будет установить RestSharp или использовать другой HttpClient.

Сначала я написал GoogleAuthService, который решает несколько основных проблем. Получает URL-адрес авторизации, обменивает код авторизации для access_token и обновляет access_token с refresh_token.

GoogleAuthService

    public class GoogleAuthService : IGoogleAuthService
    {
        /// <summary>
        /// Step 1 in authorization process
        /// </summary>
        /// <param name="appId"></param>
        /// <returns></returns>
        public dynamic AuthorizationUrl(string appId)
        {
            var qs = HttpUtility.ParseQueryString("");
            qs.Add("client_id", CloudConfigurationManager.GetSetting("ga:clientId"));
            qs.Add("redirect_uri", CloudConfigurationManager.GetSetting("ga:redirectUri"));
            qs.Add("scope", CloudConfigurationManager.GetSetting("ga:scopes"));
            qs.Add("access_type", "offline");
            qs.Add("state", $"appid={appId}");
            qs.Add("response_type", "code");
            return new { Url = $"{CloudConfigurationManager.GetSetting("ga:authUrl")}?{qs.ToString()}" };
        }

        /// <summary>
        /// Take the code that came back from Google and exchange it for an access_token
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<GoogleAccessTokenResponse> AccessToken(string code)
        {
            var client = new RestClient(CloudConfigurationManager.GetSetting("ga:tokenUrl"));
            var request = new RestRequest();

            request.AddParameter("code", code, ParameterType.GetOrPost);
            request.AddParameter("client_id", CloudConfigurationManager.GetSetting("ga:clientId"), ParameterType.GetOrPost);
            request.AddParameter("client_secret", CloudConfigurationManager.GetSetting("ga:clientSecret"), ParameterType.GetOrPost);
            request.AddParameter("redirect_uri", CloudConfigurationManager.GetSetting("ga:redirectUri"), ParameterType.GetOrPost);
            request.AddParameter("grant_type", "authorization_code", ParameterType.GetOrPost);

            var response = await client.ExecuteTaskAsync<GoogleAccessTokenResponse>(request, Method.POST);
            return response.Data;
        }

        /// <summary>
        /// Take an offline refresh_token and get a new acceses_token
        /// </summary>
        /// <param name="refreshToken"></param>
        /// <returns></returns>
        public async Task<GoogleRefreshTokenResponse> RefreshToken(string refreshToken)
        {
            var client = new RestClient(CloudConfigurationManager.GetSetting("ga:tokenUrl"));
            var request = new RestRequest();

            request.AddParameter("refresh_token", refreshToken, ParameterType.GetOrPost);
            request.AddParameter("client_id", CloudConfigurationManager.GetSetting("ga:clientId"), ParameterType.GetOrPost);
            request.AddParameter("client_secret", CloudConfigurationManager.GetSetting("ga:clientSecret"), ParameterType.GetOrPost);
            request.AddParameter("grant_type", "refresh_token", ParameterType.GetOrPost);

            var response = await client.ExecuteTaskAsync<GoogleRefreshTokenResponse>(request, Method.POST);
            return response.Data;
        }
    }

GoogleAccessTokenResponse

public class GoogleAccessTokenResponse
{
    /// <summary>
    /// Initial token used to gain access
    /// </summary>
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
    /// <summary>
    /// Use to get new token
    /// </summary>
    [JsonProperty("refresh_token")]
    public string RefreshToken { get; set; }
    /// <summary>
    /// Measured in seconds
    /// </summary>
    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }
    /// <summary>
    /// Should always be "Bearer"
    /// </summary>
    [JsonProperty("token_type")]
    public string TokenType { get; set; }
}

GoogleRefreshTokenResponse

public class GoogleRefreshTokenResponse
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }
    [JsonProperty("token_type")]
    public string TokenType { get; set; }
}

Наконец, вам понадобится обработчик обратного вызова, чтобы принять код авторизации.

GoogleOAuthController

public class GoogleOAuthController : Controller
{
    private readonly ITenantGoogleAuthenticationService service;
    private readonly ITenantService tenantService;
    private readonly IGoogleAuthService googleAuthService; 
    public GoogleOAuthController(ITenantGoogleAuthenticationService service, ITenantService tenantService, IGoogleAuthService googleAuthService)
    {
        this.service = service;
        this.tenantService = tenantService;
        this.googleAuthService = googleAuthService;
    }
    public async Task<ActionResult> Callback(GoogleAuthResponse model)
    {
        try
        {
            var response = await this.googleAuthService.AccessToken(model.Code);

            var qs = HttpUtility.ParseQueryString(model.State);
            var appid = qs["appid"];
            var tenant = await this.tenantService.TenantByAppId(appid);
            var webTenant = await this.tenantService.GetWebTenant(appid);
            var result = await this.service.GoogleAuthenticationSave(new TenantGoogleAuthenticationViewModel
            {
                AccessToken = response.AccessToken,
                Expires = DateTime.Now.AddSeconds(response.ExpiresIn),
                RefreshToken = response.RefreshToken,
                TenantId = tenant.Id
            }, webTenant);
            return new RedirectResult("/");
        }
        catch (Exception ex)
        {
            return Content(ex.Message);
        }
    }
}

Модель для того, что отправлено вам в Обратный звонок

GoogleAuthResponse

public class GoogleAuthResponse
{
    public string State { get; set; }
    public string Code { get; set; }
    public string Scope { get; set; }
}

Не беспокойтесь о Tenant коде, так как он специфичен для моей системы и не должен иметь никакого отношения к этой реализации. Я использую «appid» для идентификации пользователей моего приложения, и Google достаточно хорош, чтобы позволить мне передать это им в AuthorizationUrl, и они приятно передают его мне.

Таким образом, вы в основном вызываете GoogleAuthService.AuthorizationUrl () для получения URL. Перенаправить пользователя на этот URL. Убедитесь, что вы настроили ga: scopes в своем файле Web.config. Когда пользователь соглашается на все ваши запросы безопасности, они будут перенаправлены обратно на GoogleOAuthController и выполнят действие Callback , где вы возьмете код и обменяете его на access_token . На данный момент вы можете делать то же, что и я, и просто сохранить его в своей базе данных, чтобы вы могли использовать его позже. Похоже, по умолчанию срок его действия истекает примерно через час, поэтому вы, скорее всего, будете вызывать RefreshToken перед каждым использованием, но это ваше дело.

...