1

在 SE 上已经有几个问题,但我已经阅读了所有我能找到的似乎相关的内容,但我仍然不完全在那里。

我得到了一个身份验证码,所以现在我需要用它来交换一个访问令牌和一个刷新令牌。然而,谷歌返回奇妙的非特定错误“invalid_request”。这是我的代码:

private const string  BaseAccessTokenUrl = "https://accounts.google.com/o/oauth2/token";
private const string  ContentType        = "application/x-www-form-urlencoded";

public static string  GetRefreshToken(string clientId, string clientSecret, string authCode)
    {
    Dictionary<string, string>  parameters = new Dictionary<string, string>
        {
        { "code",          authCode },
        { "client_id",     clientId },
        { "client_secret", clientSecret },
        { "redirect_uri",  "http://localhost" },
        { "grant_type",    "authorization_code" }
        };
    string  rawJson = WebUtilities.Post(BaseAccessTokenUrl, parameters, ContentType);
    return rawJson;  // TODO: Parse out the actual refresh token
    }

我的Post()方法 URL 编码参数键和值并将它们连接起来:

public static string  Post(string uri, Dictionary<string, string> properties, string contentType = "application/x-www-form-urlencoded")
    {
    string  content = String.Join("&", from kvp in properties select UrlEncode(kvp.Key) + "=" + UrlEncode(kvp.Value) );
    return Post(uri, content);
    }

双参数Post()方法只是处理将内容转换为字节,添加内容长度等,然后返回响应的内容,即使它以WebException. 如果有任何兴趣,我可以包括它。

授权码看起来不错,和我见过的其他类似:62个字符,以“4/”开头。我从Google API 控制台小心复制的客户端 ID、密码和重定向 URL 。该应用程序注册为“其他”应用程序,我正在从 Windows 机器连接。

根据这篇这篇文章,我尝试了非 URL 编码,没有任何变化。 OAuth Playground建议 URL 编码是正确的。

根据这篇文章这篇文章,属性连接在一行上。

根据这篇文章,我已经尝试approval_prompt=force过授权请求,但新的身份验证代码并没有更好地工作。授权码会过期吗?我通常会在几秒钟内使用新代码。

根据Google 文档这篇文章,我使用的是内容类型“application/x-www-form-encoded”。

我的授权请求适用于范围“ https://www.googleapis.com/auth/analytics.readonly ”。

根据这篇文章,参数中没有前导问号。

一个Google .NET OAuth 库,但我无法让它轻松运行,如果我有选择的话,大约 50,000 行代码比我想学习的要多。我更喜欢从头开始写一些干净的东西,而不是盲目地复制一堆库,货物崇拜风格。

4

1 回答 1

6

找到了。redirect_uri用于请求令牌的令牌需要与获取授权码时使用的令牌相匹配。这是我获取身份验证代码的工作代码:

private const string  BaseAuthorizationUrl = "https://accounts.google.com/o/oauth2/auth";
public string  GetAuthorizationUrl(string clientId, IEnumerable<string> scopes)
    {
    var  parameters = new Dictionary<string, string>
        {
        { "response_type",   "code" },
        { "client_id",       clientId },
        { "redirect_uri",    RedirectUrl },
        { "scope",           String.Join(" ", scopes) },
        { "approval_prompt", "auto" }
        };
    return WebUtilities.BuildUrl(BaseAuthorizationUrl, parameters);
    }

...这是我获取访问令牌和刷新令牌的代码:

private const string  BaseAccessTokenUrl = "https://accounts.google.com/o/oauth2/token";
public void  GetTokens(string clientId, string clientSecret, string authorizationCode, out string accessToken, out string refreshToken)
    {
    var  parameters = new Dictionary<string, string>
        {
        { "code",          authorizationCode },
        { "redirect_uri",  RedirectUrl },  // Must match that used when authorizing an app
        { "client_id",     clientId },
        { "scope",         String.Empty },
        { "client_secret", clientSecret },
        { "grant_type",    "authorization_code" }
        };
    string   rawJson    = WebUtilities.Post(BaseAccessTokenUrl, parameters, "application/x-www-form-urlencoded");
    dynamic  parsedJson = JsonUtilities.DeserializeObject(rawJson);
    accessToken  = parsedJson.access_token;
    refreshToken = parsedJson.refresh_token;
    }

...这是获取新访问令牌的代码:

public string  GetAccessToken(string clientId, string clientSecret, string refreshToken)
    {
    var  parameters = new Dictionary<string, string>
        {
        { "client_id",     clientId },
        { "client_secret", clientSecret },
        { "refresh_token", refreshToken },
        { "grant_type",    "refresh_token" }
        };
    string   rawJson    = WebUtilities.Post(BaseAccessTokenUrl, parameters, "application/x-www-form-urlencoded");
    dynamic  parsedJson = JsonUtilities.DeserializeObject(rawJson);
    return parsedJson.access_token;
    }
于 2013-02-15T20:16:05.240 回答