title: Cannot get access token from OAuth Google API for installed applications
body: <p>I'm creating an OAuth authentication flow so users of my installed application can access their private Google spreadsheet documents.  I'm coding with Adobe ExtendScript, so I cannot use the Javascript client libraries Google provides.  I've read <a href="https://developers.google.com/accounts/docs/OAuth2InstalledApp" rel="nofollow">Google's OAuth 2.0 documentation for installed applications</a> quite a few times, but need help with one aspect of the OAuth flow.  I am able to get an authorization code by launching a browser from the installed app, and having the user submit their credentials, and then copy and pasting the authorization code back into the app.  However, when I POST to the endpoint for exchanging the authorization code for an access token, it doesn't work.  The body of the response from google shows this:</p>
  
  <pre><code>Moved Temporarily
  The document has moved &lt;A HREF="https://accounts.google.com/o/oauth2/token"&gt;here&lt;/A&gt;.
  </code></pre>
  
  <p>And yet, I made the POST call to that exact same URL found in the href tag.  So, I'm not sure why google would tell me that the end point has moved temporarily when I POSTed to that same URL.  Here is the code I'm using to generate the POST.</p>
  
  <pre><code>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);
  
  }
  </code></pre>
  
  <p>The post is made fine, but does anybody know why this 'Moved Temporarily' notice would come up in google's response?  Any suggestions are much appreciated!</p>
  
  <p>EDIT: Just to clarify, here is the request my script is making in the raw:</p>
  
  <pre><code>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}&amp;client_id={my_client_id}&amp;client_secret={my_client_secret}&amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob&amp;grant_type=authorization_code&amp;
  </code></pre>
  
  <p>If I use the authorization code in cURL, along with the other params, I do get a successful response from Google's OAuth servers.  So, obviously something is up with the way my sockets are interacting with Google's endpoints, but I'm not sure what.  Is it possible that some components need to be URI encoded, while others do not?  Here is the cURL I am using:</p>
  
  <pre><code>#!/bin/bash
  AUTHCODE="$1"
  
  POSTCONTENT="code=$AUTHCODE&amp;client_id={my_client_id}&amp;client_secret={my_client_secret}&amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob&amp;grant_type=authorization_code&amp;"
  
  echo $POSTCONTENT
  
  curl -v --data $POSTCONTENT https://accounts.google.com/o/oauth2/token
  </code></pre>
  
  <p>EDIT 2:  So, since Sockets do not support SSL in Extendscript, I wrote a function that uses OS level calls to invoke the request.  If on OSX, we can assume that we have access to cURL, but on Windows, we have to write a VBScript that gets executed via the <code>cscript</code> host in the command line.  For ExtendScript, here is the function that makes the web request:</p>
  
  <pre><code>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;
  }
  </code></pre>
  
  <p>And here is the VBScript script that is called from the ExtendScript library.  It takes three params, all strings:</p>
  
  <pre><code>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 
  </code></pre>
  
  <p>You can use this in ExtendScript for any API endpoint, but the response will always be a string.  So, in the case of Google's OAuth Endpoints, you'll get a string that looks like JSON.  So, y ou'll have to parse it with something like JSON.parse().</p>