请求访问令牌时,通过OAuth 2连接到Google,“invalid_request”

时间:2013-02-14 20:56:54

标签: c# oauth-2.0 google-analytics-api

在SE上已经有几个问题,但是我已经阅读了所有我认为相似的内容,而且我还没有完全找到。

我收到了身份验证码,所以现在我需要将其换成访问令牌和刷新令牌。但是,Google会返回奇怪的非特定错误“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 /”开头。我从the Google API Console仔细复制的客户ID,密码和重定向网址。该应用程序已注册为“其他”应用程序,我正在从Windows计算机连接。

根据thisthis post,我尝试过不进行网址编码,没有任何变化。 The OAuth Playground表明网址编码是正确的。

this postthis one,属性在一行上连接。

根据this post,我在授权请求中尝试了approval_prompt=force,但新的身份验证代码无法正常工作。验证码是否过期?我通常会在几秒钟内使用新代码。

根据the Google docsthis post,我使用的是内容类型“application / x-www-form-encoded”。

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

this post,参数中没有前导问号。

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

1 个答案:

答案 0 :(得分: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;
    }