gpt4 book ai didi

xamarin - 使用 xamarin.auth 获取 google api 的刷新 token

转载 作者:行者123 更新时间:2023-12-02 18:16:12 26 4
gpt4 key购买 nike

 var auth = new OAuth2Authenticator(clientId: "my client id",
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar",
authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/auth"),
redirectUrl: new Uri("https://www.googleapis.com/plus/v1/people/me"),
getUsernameAsync: null);
auth.AllowCancel = allowCancel;
List<Event> events = new List<Event>();

auth.Completed += async (sender, e) =>
{
if (!e.IsAuthenticated)
{
Toast.MakeText(_activity, "Fail to authenticate!", ToastLength.Short).Show();
return;
}

string access_token;
e.Account.Properties.TryGetValue("access_token", out access_token);

当我尝试以与检索访问 token 相同的方式检索刷新 token 时,它不起作用。

我能够获取访问 token ,但无法获取刷新 token 。我尝试从谷歌帐户中删除授权,因为我读到您仅在第一次授予访问权限时获得刷新 token ,但我仍然无法检索访问 token 。我还读到我需要将 access_type=offline 和approval_prompt=force 添加到请求中,但我不知道在哪里使用 xamarin.auth 添加它。

最佳答案

在 VS 中搜索时获得的默认包是“Xamarin Auth 1.3.0”,它不支持获取刷新 token 。

我通过制作自己的 OAuth2Authenticator 版本解决了这个问题,并通过非常有限的努力达到了相同的目标。

public class RefreshOAuth2Authenticator : WebRedirectAuthenticator

{
string clientId;
string clientSecret;
string scope;
Uri authorizeUrl;
Uri accessTokenUrl;
Uri redirectUrl;
GetUsernameAsyncFunc getUsernameAsync;

string requestState;
bool reportedForgery;

public string ClientId
{
get { return clientId; }
}

public string ClientSecret
{
get { return clientSecret; }
}

public string Scope
{
get { return scope; }
}

public Uri AuthorizeUrl
{
get { return authorizeUrl; }
}

public Uri RedirectUrl
{
get { return redirectUrl; }
}

public Uri AccessTokenUrl
{
get { return accessTokenUrl; }
}

public RefreshOAuth2Authenticator(string clientId, string scope, Uri authorizeUrl, Uri redirectUrl, GetUsernameAsyncFunc getUsernameAsync = null)
: this (redirectUrl)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException("clientId must be provided", nameof(clientId));
}
this.clientId = clientId;

this.scope = scope ?? "";

if (authorizeUrl == null)
{
throw new ArgumentNullException(nameof(authorizeUrl));
}
this.authorizeUrl = authorizeUrl;

this.getUsernameAsync = getUsernameAsync;

this.redirectUrl = redirectUrl;

accessTokenUrl = null;
}

public RefreshOAuth2Authenticator(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null)
: this (redirectUrl, clientSecret, accessTokenUrl)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException("clientId must be provided", nameof(clientId));
}
this.clientId = clientId;

if (string.IsNullOrEmpty(clientSecret))
{
throw new ArgumentException("clientSecret must be provided", nameof(clientSecret));
}
this.clientSecret = clientSecret;

this.scope = scope ?? "";

if (authorizeUrl == null)
{
throw new ArgumentNullException(nameof(authorizeUrl));
}
this.authorizeUrl = authorizeUrl;

if (accessTokenUrl == null)
{
throw new ArgumentNullException(nameof(accessTokenUrl));
}
this.accessTokenUrl = accessTokenUrl;

this.redirectUrl = redirectUrl;

this.getUsernameAsync = getUsernameAsync;
}

RefreshOAuth2Authenticator(Uri redirectUrl, string clientSecret = null, Uri accessTokenUrl = null)
: base (redirectUrl, redirectUrl)
{
this.clientSecret = clientSecret;

this.accessTokenUrl = accessTokenUrl;

this.redirectUrl = redirectUrl;

//
// Generate a unique state string to check for forgeries
//
var chars = new char[16];
var rand = new Random();
for (var i = 0; i < chars.Length; i++)
{
chars[i] = (char)rand.Next('a', 'z' + 1);
}
requestState = new string(chars);
}

bool IsImplicit { get { return accessTokenUrl == null; } }

public override Task<Uri> GetInitialUrlAsync()
{
var url = new Uri(string.Format(
"{0}?client_id={1}&redirect_uri={2}&response_type={3}&scope={4}&state={5}&access_type=offline&prompt=consent",
authorizeUrl.AbsoluteUri,
Uri.EscapeDataString(clientId),
Uri.EscapeDataString(RedirectUrl.AbsoluteUri),
IsImplicit ? "token" : "code",
Uri.EscapeDataString(scope),
Uri.EscapeDataString(requestState)));

var tcs = new TaskCompletionSource<Uri>();
tcs.SetResult(url);
return tcs.Task;
}

public async virtual Task<IDictionary<string, string>> RequestRefreshTokenAsync(string refreshToken)
{
var queryValues = new Dictionary<string, string>
{
{"refresh_token", refreshToken},
{"client_id", this.ClientId},
{"grant_type", "refresh_token"}
};

if (!string.IsNullOrEmpty(this.ClientSecret))
{
queryValues["client_secret"] = this.ClientSecret;
}

try
{
var accountProperties = await RequestAccessTokenAsync(queryValues).ConfigureAwait(false);

this.OnRetrievedAccountProperties(accountProperties);

return accountProperties;
}
catch (Exception e)
{
OnError(e);

throw; // maybe don't need this? this will throw the exception in order to maintain backward compatibility, but maybe could just return -1 or something instead?
}
}

protected override void OnPageEncountered(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
{
var all = new Dictionary<string, string>(query);
foreach (var kv in fragment)
all[kv.Key] = kv.Value;

//
// Check for forgeries
//
if (all.ContainsKey("state"))
{
if (all["state"] != requestState && !reportedForgery)
{
reportedForgery = true;
OnError("Invalid state from server. Possible forgery!");
return;
}
}

//
// Continue processing
//
base.OnPageEncountered(url, query, fragment);
}

protected override void OnRedirectPageLoaded(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
{
//
// Look for the access_token
//
if (fragment.ContainsKey("access_token"))
{
//
// We found an access_token
//
OnRetrievedAccountProperties(fragment);
}
else if (!IsImplicit)
{
//
// Look for the code
//
if (query.ContainsKey("code"))
{
var code = query["code"];
RequestAccessTokenAsync(code).ContinueWith(task =>
{
if (task.IsFaulted)
{
OnError(task.Exception);
}
else {
OnRetrievedAccountProperties(task.Result);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else {
OnError("Expected code in response, but did not receive one.");
return;
}
}
else {
OnError("Expected access_token in response, but did not receive one.");
return;
}
}

Task<IDictionary<string, string>> RequestAccessTokenAsync(string code)
{
var queryValues = new Dictionary<string, string> {
{ "grant_type", "authorization_code" },
{ "code", code },
{ "redirect_uri", RedirectUrl.AbsoluteUri },
{ "client_id", clientId },
};
if (!string.IsNullOrEmpty(clientSecret))
{
queryValues["client_secret"] = clientSecret;
}

return RequestAccessTokenAsync(queryValues);
}

protected Task<IDictionary<string, string>> RequestAccessTokenAsync(IDictionary<string, string> queryValues)
{
var query = queryValues.FormEncode();

var req = WebRequest.Create(accessTokenUrl);
req.Method = "POST";
var body = Encoding.UTF8.GetBytes(query);
req.ContentLength = body.Length;
req.ContentType = "application/x-www-form-urlencoded";
using (var s = req.GetRequestStream())
{
s.Write(body, 0, body.Length);
}
return req.GetResponseAsync().ContinueWith(task =>
{
var text = task.Result.GetResponseText();

// Parse the response
var data = text.Contains("{") ? WebEx.JsonDecode(text) : WebEx.FormDecode(text);

if (data.ContainsKey("error"))
{
throw new AuthException("Error authenticating: " + data["error"]);
}
else if (data.ContainsKey("access_token"))
{
return data;
}
else {
throw new AuthException("Expected access_token in access token response, but did not receive one.");
}
});
}

protected virtual void OnRetrievedAccountProperties(IDictionary<string, string> accountProperties)
{
//
// Now we just need a username for the account
//
if (getUsernameAsync != null)
{
getUsernameAsync(accountProperties).ContinueWith(task =>
{
if (task.IsFaulted)
{
OnError(task.Exception);
}
else {
OnSucceeded(task.Result, accountProperties);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else {
OnSucceeded("", accountProperties);
}
}

}

LoginPageRenderer调用如下:

var auth = new RefreshOAuth2Authenticator(
clientId: "client ID",
clientSecret: "client Secret",
scope: "scope", // just 'openid' in my case
authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
redirectUrl: new Uri("your redirect url"),
accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token") );

auth.GetUI();

此调用会生成一个带有刷新 token 的 AccountStore,应安全保存该 token 。

每次想要获取新的id_token时,只需要调用:

await auth.RequestRefreshTokenAsync("Refresh Token")

您将需要更多关于检查 expire_ts 和存储 token 值的逻辑,但是上面的代码应该可以帮助您继续。

注释 1:Xamarin.Auth 的一个分支现在支持刷新 token https://github.com/xamarin/Xamarin.Auth/tree/portable-bait-and-switch我没有尝试过。

注 2:我知道客户端 key 存储在应用程序内部,出于安全原因,建议不要这样做。但是,由于 Google 不支持使用已安装的凭据,因此我目前无法找到解决此问题的方法。

关于xamarin - 使用 xamarin.auth 获取 google api 的刷新 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38248989/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com