I spent many wasted hours this morning debugging a Google OAuth 2.0 response error, and felt foolish when I discovered what I was missing – the content-type header.
After doing some testing, it appears that some HTTP clients, such as curl, automatically assign an appropriate content-type header for you.
ColdFusions's CFHTTP does not. But ColdFusion is not to blame – the developer knows the type of content they are posting better than any server can take a guess at.
If you're posting to a service, for example, attempting to retrive an OAuth 2.0 access token, the error message returned may not always indicate that the server doesn't understand your content type.
For instance, I would receive the following error from Google's OAuth 2.0, which is what inspired me to write this post:
Required parameter is missing: grant_type
If you happen to run into the same issue with Google OAuth 2.0, keep these 3 things in mind:
- Google expects your authorization code, client id, client_secret, grant_type and redirect uri to be in the body of the POST request, not in the headers.
- Google expects a content-type of application/x-www-form-urlencoded.
- Be sure to Url Encode the client_secret and authorization code. Url Encode all parameters to be safe.
While both are in the Google OAuth 2.0 documentation, they don't really stand out.
So, in the end, if you're a friend to ColdFusion, or can at least make sense of the code, this may help:
<cffunction name="getAccessToken">
<cfargument name="code" required="false" default="" type="string">
<cfset postBody = "code=" & UrlEncodedFormat(arguments.code) & "&">
<cfset postBody = postBody & "client_id=" & UrlEncodedFormat(variables.client_id) & "&">
<cfset postBody = postBody & "client_secret=" & UrlEncodedFormat(variables.client_secret) & "&">
<cfset postBody = postBody & "redirect_uri=" & UrlEncodedFormat(variables.callback) & "&">
<cfset postBody = postBody & "grant_type=authorization_code">
<cfhttp method="post" url="https://accounts.google.com/o/oauth2/token">
<cfhttpparam name="Content-Type" type="header" value="application/x-www-form-urlencoded">
<cfhttpparam type="body" value="#postBody#">
</cfhttp>
</cffunction>