У меня была та же проблема, и я шел по тем же дорогам, что и вы, но, имея некоторый опыт написания реализаций 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 перед каждым использованием, но это ваше дело.