简体   繁体   English

将 ID 令牌交换为访问令牌时,Google 身份验证代码流请求令牌会生成无效的客户端错误

[英]Google Auth Code Flow requesting token generates invalid client error when exchanging ID token for Access Token

Fairly new in looking at the Google OAuth library, but have spent a while in Azure/Exchange OAuth.在查看 Google OAuth 库时相当新,但在 Azure/Exchange OAuth 中花了一段时间。 We have a wrapper around the various libraries in the Adapter Pattern, so our app code is consistent and calls the same methods.我们对适配器模式中的各种库进行了包装,因此我们的应用程序代码是一致的并调用相同的方法。 Here's what the Google one looks like这是谷歌的样子

private string _state;
private GoogleAuthorizationCodeFlow _flow;

public GoogleTokenProvider(IOAuthSettings settings, string state, string baseUrl) : base(settings, state, baseUrl)
{
    //https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-mvc
    _state = state;
    GoogleAuthorizationCodeFlow.Initializer init = new GoogleAuthorizationCodeFlow.Initializer {
        ClientSecrets = new ClientSecrets
        {
            ClientId = settings.ClientId,
            ClientSecret = settings.ClientSecret
        },
        Scopes = this.Scopes,
        DataStore = new GoogleTokenStore(settings) //Our implementation - not called yet
    };
    _flow = new GoogleAuthorizationCodeFlow(init);
}

The first step (once constructed) is to get the url to start the auth process.第一步(一旦构建)是获取 url 以启动身份验证过程。

public string GetAuthorizationUrl()
{
    //Using Flow 
    var req = _flow.CreateAuthorizationCodeRequest(this.RedirectUrl);
    var baseUrl = req.Build().AbsoluteUri;
    baseUrl += "&state=" + this._state;
    return baseUrl;

    // Manually build request
    //StringBuilder builder = new StringBuilder();
    //builder.Append("https://accounts.google.com/o/oauth2/v2/auth?");
    //builder.AppendFormat("scope={0}", String.Join("+", this.Scopes));
    //builder.Append("&access_type=offline&include_granted_scopes=true&response_type=code");
    //builder.AppendFormat("&state={0}&", this._state);
    //builder.AppendFormat("&redirect_uri={0}", this.RedirectUrl);
    //builder.AppendFormat("&client_id={0}", this.settings.ClientId);
    //return builder.ToString();
}

This works, and we're directed off to the OAuth site, we log in with our Google account.这行得通,我们被引导到 OAuth 站点,我们使用我们的 Google 帐户登录。 Our callback is fired and when debugging the callback, we obtain the code, and state passed through and call this method:我们的回调被触发,在调试回调时,我们获取代码,state 传递过来并调用这个方法:

public string AcquireTokenByAuthorizationCode(string code)
{
    Google.Apis.Auth.OAuth2.Responses.TokenResponse result = _flow.ExchangeCodeForTokenAsync("userid", code, RedirectUrl, new System.Threading.CancellationToken()).Result;
    return result.AccessToken;
}

However, this causes an error:但是,这会导致错误: 错误详情

Error:"invalid_client", Description:"Unauthorized", Uri:""

There are two queries.有两个查询。

  1. I am unsure what "UserId" should be, a lot of posts just have "userid" or "me" in there, so what it is for?我不确定“UserId”应该是什么,很多帖子里面只有“userid”或“me”,那它是干什么用的?
  2. Since the Flow class is built by the same settings, why does the client suddenly become invalid.既然Flow class都是用同样的设置搭建的,为什么客户端突然失效了。

Thanks in advance.提前致谢。

Google Configuration谷歌配置

已创建 Google 网络客户端

The intent is to be able to read the emails (Gmail) from this account目的是能够阅读此帐户中的电子邮件(Gmail)

Enabled APIs:启用的 API:

  • Gmail API Gmail API
  • Google+ API谷歌+ API

google Data Store Implementation谷歌数据存储实现

public class GoogleTokenStore : IDataStore
{
    private readonly IOAuthSettings _settings;
    public GoogleTokenStore(IOAuthSettings settings)
    {
        this._settings = settings;
    }
    private Dictionary<string, T> Decode<T>()
    {
        using (var memoryStream = new MemoryStream())
        {
            var binaryFormatter = new BinaryFormatter();
            
            memoryStream.Write(_settings.TokenStore, 0, _settings.TokenStore.Length);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return binaryFormatter.Deserialize(memoryStream) as Dictionary<string, T>;
        }
    }

    private void Encode<T>(Dictionary<string, T> store)
    {
        using (var memoryStream = new MemoryStream())
        {
            var binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, store);
            _settings.TokenStore = memoryStream.ToArray();
        }
    }

    public Task ClearAsync()
    {
        _settings.TokenStore = null;
        return Task.CompletedTask;
    }

    public Task DeleteAsync<T>(string key)
    {
        if (_settings.TokenStore == null)
        {
            return Task.CompletedTask; //THIS IS CALLED, RETURNS HERE
        }
        var store = Decode<T>();
        if (store.ContainsKey(key))
        {
            store.Remove(key);
            Encode<T>(store);
        }
        return Task.CompletedTask;
    }

    public Task<T> GetAsync<T>(string key)
    {
        var store = Decode<T>();
        if (store.ContainsKey(key))
        {
            return Task.FromResult( store[key] );
        }
        return null;
    }

    public Task StoreAsync<T>(string key, T value)
    {
        var store = Decode<T>();
        if(store != null)
        {
            store.Add(key, value);
            Encode<T>(store);
        }
        return Task.CompletedTask;
    }
}

Stack trace of the Google error Google 错误的堆栈跟踪

   at Google.Apis.Auth.OAuth2.Responses.TokenResponse.<FromHttpResponseAsync>d__36.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Requests.TokenRequestExtenstions.<ExecuteAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<ExchangeCodeForTokenAsync>d__30.MoveNext()

I have included this code as it does appear the Delete was called and on returning Task.CompletedTask the error appears (on initial call the store is null so just return Task.CompletedTask我已包含此代码,因为它确实显示了 Delete 被调用,并且在返回 Task.CompletedTask 时出现错误(在初始调用时,商店是 null 所以只需返回 Task.CompletedTask

Invalid_client error normally means that you have created one type of client on Google Developer console and are using the code for a different type of client. Invalid_client 错误通常意味着您在 Google Developer Console 上创建了一种类型的客户端,并且正在将代码用于不同类型的客户端。

There are two types of clients normally used with .net applications Desktop app, and web browser client.有两种类型的客户端通常与 .net 应用程序桌面应用程序和 web 浏览器客户端一起使用。 The code used for them is different.用于它们的代码是不同的。 You are appear to be using the MCV code.您似乎正在使用 MCV 代码。 I would double check that you have created a web browser client on Google developer console.我会仔细检查您是否在 Google 开发人员控制台上创建了 web 浏览器客户端。

update from comments从评论更新

If you check the documentation MVC如果您检查文档MVC

Check the section for AppFlowMetadata检查 AppFlowMetadata 部分

You are going to have a problem because its looking for a session var on each users browser您将遇到问题,因为它在每个用户浏览器上寻找 session var

var user = controller.Session["user"];

It uses that session var to know which users data to load from它使用 session var 来了解要从哪些用户数据加载

DataStore = new FileDataStore("Drive.Api.Auth.Store")

So if you want to make this single user make sure to always set the session var to that of the user whos data you want to load.因此,如果您想让这个单一用户确保始终将 session var 设置为您要加载的用户的数据。 check %appData% its where file data store default stores its credential files for each user.检查 %appData% 文件数据存储默认存储每个用户的凭据文件的位置。Check this if your curios what IDataStore is如果您的古玩,请检查此 IDataStore 是什么

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 发行令牌Google Api的交换授权码 - Issue Exchanging Authorization Code for Token Google Api 交换令牌代码时redirect_url不匹配错误 - redirect_url mismatch error when exchanging code for token 在 C# 中使用 OAuth 授权代码流请求 access_token 时收到 400 错误请求? - Receiving 400 Bad Request when requesting an access_token with OAuth Authorization Code Flow in C#? 请求访问令牌时,通过OAuth 2“ invalid_request”连接到Google - Connecting to Google via OAuth 2, “invalid_request” when requesting an access token Docusign - 请求 JWT 访问令牌时的“错误”:“Invalid_request”响应 - Docusign - “Error”:“Invalid_request” Response When Requesting JWT Access Token 尝试在ASP Web API和OWIN中使用Google auth_code获取access_token时获得的无效授权 - Invalid-grant when trying to get access_token with google auth_code in asp web api and OWIN Owin auth - 如何获取请求身份验证令牌的客户端的IP地址 - Owin auth - how to get IP address of client requesting the auth token 请求Facebook访问令牌服务器端时,验证代码错误 - Error validating verification code when requesting Facebook access Token Server Side 电子交易:请求访问令牌,我得到签名无效 - E-trade: requesting access token, I get signature invalid 从 API 请求访问令牌时请求无效:C#、Azure 函数、OAuth - Invalid request when requesting Access token from API: C#, Azure Function, OAuth
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM