无法从OAuth Google API获取安装应用程序的访问令牌

3
我正在创建一个OAuth认证流程,以便我安装的应用程序的用户可以访问他们的私人Google电子表格文档。我使用Adobe ExtendScript进行编码,因此无法使用Google提供的Javascript客户端库。我已经多次阅读了Google针对安装应用程序的OAuth 2.0文档,但需要帮助解决OAuth流程中的一个方面。我能够通过从安装的应用程序启动浏览器,并要求用户提交其凭据,然后将授权代码复制并粘贴回应用程序来获取授权代码。但是,当我POST到用于交换授权代码以获取访问令牌的端点时,它无法正常工作。来自Google的响应正文显示如下:
Moved Temporarily
The document has moved <A HREF="https://accounts.google.com/o/oauth2/token">here</A>.

然而,我向与href标签中相同的URL发出了POST调用。所以,我不确定为什么谷歌会告诉我终点已暂时移动,当我向相同的URL进行了POST。这是我用来生成POST的代码。

function getAccessToken(authcode)
{
    var http = require('http'),
        OAuthAccessEndPoint = 'https://accounts.google.com/o/oauth2/token',
        OAuthAccessParams = {};

    OAuthAccessParams['code']             = authcode;
    OAuthAccessParams['client_id']        = '{my_client_id}';
    OAuthAccessParams['client_secret']    = '{my_client_secret}';
    OAuthAccessParams['redirect_uri']     = 'urn:ietf:wg:oauth:2.0:oob';
    OAuthAccessParams['grant_type']       = 'authorization_code';

    var response = http.post(OAuthAccessEndPoint, OAuthAccessParams);

}

这篇文章写得很好,但是有人知道为什么谷歌会在响应中出现“暂时移动”通知吗?欢迎任何建议!编辑:为了澄清,这是我的脚本在原始请求中发出的内容:
POST /o/oauth2/token HTTP/1.1
User-Agent: Adobe ExtendScript
Accept: */*
Connection: close
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 226

code={authcode}&client_id={my_client_id}&client_secret={my_client_secret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code&

如果我在cURL中使用授权代码以及其他参数,我将会从Google的OAuth服务器获得成功的响应。所以,显然我的套接字与Google的端点交互的方式存在问题,但我不确定是什么原因。是否有可能需要对某些组件进行URI编码,而对于其他组件则不需要?这是我正在使用的cURL:
#!/bin/bash
AUTHCODE="$1"

POSTCONTENT="code=$AUTHCODE&client_id={my_client_id}&client_secret={my_client_secret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code&"

echo $POSTCONTENT

curl -v --data $POSTCONTENT https://accounts.google.com/o/oauth2/token

编辑2:由于扩展脚本不支持SSL套接字,我编写了一个使用操作系统级别调用来调用请求的函数。如果在OSX上,我们可以假设我们有访问cURL的权限,但在Windows上,我们必须编写一个VBScript,通过命令行中的cscript主机执行。对于ExtendScript,这是使网络请求的函数:

function webRequest(method, endpoint, query){

    var response = null,
        wincurl  = WORKING_DIR.fsName + '\\lib\\curl.vbs',
        curlCmd = '';

    try {

        if ( os() == "Win" ) {
            curlCmd = 'cscript "' + wincurl + '" /Method:' + method + ' /URL:' + endpoint + ' /Query:' + query + ' //nologo';
        } else {
            if (method === "POST") {
                curlCmd = 'curl -s -d "' + query + '" ' + endpoint;
            } else if (method === "GET") {
                curlCmd = 'curl -s -G -d "' + query + '" ' + endpoint;
            }
        }

        response = system.callSystem(curlCmd);

    } catch (err) {

        alert("Error\nUnable to make a `"+ method +"` request to the network endpoint.  Please try again.");

    }

    return response;

}

function os(){
    var os = system.osName;
    if (!os.length) { os = $.os; }
    app_os =  ( os.indexOf("Win") != -1 )  ?  "Win" : "Mac";
    return app_os;
}

以下是从ExtendScript库调用的VBScript脚本。它需要三个参数,全部为字符串:

set namedArgs = WScript.Arguments.Named

sMethod = namedArgs.Item("Method")
sUrl = namedArgs.Item("URL")
sRequest = namedArgs.Item("Query")

HTTPPost sMethod, sUrl, sRequest

Function HTTPPost(sMethod, sUrl, sRequest)

    set oHTTP = CreateObject("Microsoft.XMLHTTP")  

    If sMethod = "POST" Then
        oHTTP.open "POST", sUrl,false
    ElseIf sMethod = "GET" Then
        oHTTP.open "GET", sUrl,false
    End If

    oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
    oHTTP.setRequestHeader "Content-Length", Len(sRequest)
    oHTTP.send sRequest

    HTTPPost = oHTTP.responseText

    WScript.Echo HTTPPost

End Function 

你可以在任何API端点中使用这个ExtendScript,但响应总是一个字符串。因此,在Google的OAuth端点的情况下,你将获得一个看起来像JSON的字符串。所以,你需要使用类似JSON.parse()的东西进行解析。

还尝试使用encodeURIComponent()将请求对象中使用的所有值进行包装。结果相同。 - ariestav
1
我不熟悉extendscript,但如果您意外地通过http而不是https发送了查询,您可能会看到类似的情况。是的,我在您的URL中看到了https,但在某些语言中,您必须给http库提供额外的参数才能使其执行https。 - Tim Bray
好的,ExtendScript基于Javascript,而我正在使用的特定库使用Socket对象来建立网络通信。在Socket对象上是否有一个属性/方法可以确保通过https安全地进行通信? - ariestav
@TimBray 嗯,你对 http vs. https 的直觉是正确的。根据 Adobe 的这篇文章(http://forums.adobe.com/message/3500864#3500864),ExtendScript 中的原生 Socket 对象不支持 SSL。我需要想出一种不同的方法来从 ExtendScript 访问 SSL。谢谢! - ariestav
我可以建议您创建一个Web服务或执行VB脚本,作为您和Google之间的桥梁吗? - Eric B.
是的,我已经做到了...你可以在这里看到解决方案:http://forums.adobe.com/message/5663858#5663858 但我也会在这里更新我的答案。 - ariestav
1个回答

3

请参见上面的编辑以获取答案。基本上,这归结为ExtendScript中的Socket对象不支持SSL。上面的解决方案通过使用ExtendScript系统调用system.callSystem()方法以获取OSX上的cURL和Windows上的VBScript来实现绕过。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接