I did not really understand google api despite the fact I’ve already developed several apps that use google apis, even in different languages such as php, python, c++, and javascript. When I searched on the Internet on how to use google apis, I soon got a bunch of api libs, packages, sample codes from github or google official website. What I did is downloading the API SDK/codes, editing some code such as the client id, client secret,etc., adding some functional code, then the code worked and my task was accomplished. But I never know deeply about the apis. I even did not know the difference between api key and client id. I did not understand the logic and reason of using OAuth.
What is API?
In the simplest form, API is like a url such as http://myprogrammingnotes.com/iwanttosee/resource1. You paste the url in the address bar of your browser and press the enter key, then the result is displayed in the browser. Wu la, you just called an API.
I would like to see all API callings are as simple as this, but they do not think so. They want you to create an account before using their API. This way, they can charge you for using their API. Even they decide to let you use their API for free, they still want you to create an account. They may send customized ads to you, collect your privacy, or limit your calls, etc. Account-based API management brings them too many benefits(some even seem technically reasonable) so they need to make things harder. Now you can understand why the following API call fails:
curl "https://youtube.googleapis.com/youtube/v3/captions?part=id&videoId=liJVSwOiiwg" --header "Accept: application/json" { "error": { "code": 403, "message": "The request is missing a valid API key.", "errors": [ { "message": "The request is missing a valid API key.", "domain": "global", "reason": "forbidden" } ], "status": "PERMISSION_DENIED" } }
The above is a Youtube API call that retrieves the caption id(s) of a video. Note that you must quote the url and the custom header with double quotes(not single quotes). The video is specified by the url parameter videoId, which can be found as the v parameter of the video’s url https://www.youtube.com/watch?v=liJVSwOiiwg. You can also find the video id in the share url of the video: https://youtu.be/liJVSwOiiwg.
Now, they require you to pass a key parameter to the url to call the API like https://youtube.googleapis.com/youtube/v3/captions?part=id&videoId=liJVSwOiiwg&key=xxxx. The key parameter can only be obtained in your account(api key). The key parameter associates your API callings with your account. They get the statistics about your usage of API.
After appending the key parameter, the api call can retrieve the caption id successfully. But they are not satisfied yet. Only experienced developers know how to create an api key and use it in application(developed by themselves in most cases). Ordinary people only know user name and password. They do not know how to develop apps to use their API. But do not worry, you, ordinary people, just need to create accounts and deposit money in your accounts. There are plenty of people in the world who can develop all kinds of strange apps/websites using APIs for you to burn the money in your account. You just need to memorize your user name and password. When the apps/websites want to call the API, they visit the API authorization endpoint(a special url). Typically, you are brought to a webpage in your browser showing a consent window. If you agree the apps to burn your money or allow the websites to fetch your privacy from your account, you just login the account and click on the “Allow” button. The API authorization server will issue an auth code to the app/website so that they can access the api later without your intervention. This is called OAuth 2.0 protocol.
OAuth 2.0
Using API cannot be easier after OAuth was invented. OAuth separates the API cost payer and the API consumer. It creates and uses the information asymmetry between the two. The auth code and the subsequent access token encode the payer’s id in them and the cost is attributed to the payer’s account, not the consumer(the app developer). Now, you cannot use some APIs happily even you use the key parameter.
curl "https://www.googleapis.com/youtube/v3/captions/somecaptionid?key=xxxxxxxxx" { "error": { "code": 401, "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", "errors": [ { "message": "Login Required.", "domain": "global", "reason": "required", "location": "Authorization", "locationType": "header" } ], "status": "UNAUTHENTICATED" } }
The above api call retrieves the caption specified by somecaptionid. The result is the api call is unauthenticated. They say, developers, we won’t charge you based on your key, go and find more people to use your app, we will charge from their accounts.
Okay, let’s find a victim to use my app. Before my app can download video captions for him, he need to visit the following url in a browser to authorize the API call of my app.
https://accounts.google.com/o/oauth2/v2/auth?client_id=xxxx.apps.googleusercontent.com&response_type=code&scope=https://www.googleapis.com/auth/youtube.force-ssl&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob
The client_id(a required parameter) is my(the developer’s) client id which is generated in my account. The response_type is code which asks the authorization server to return an auth code. Authorize what? Authorize my app to use the caption.download api(corresponding to the scope parameter). The redirect_uri is which url to redirect to after user makes a choice. Regardless the user chooses to allow or deny the api access, the user will be brought to the url. Note that redirect_uri is not a url for server-to-server call. The authorization server won’t call this url with authorization code provided. After user makes the choice, the authorization server will send back a 302 redirection response, in which a Location header will contain the authorization code in the url(redirect_uri). The result is the user’s browser will navigate to the redirection url. Typically, my app will listen on the url’s host(such as loopback http://127.0.0.1:80) to get the auth code. But in this case, the redirect_uri is kind of special: urn:ietf:wg:oauth:2.0:oob, which means the auth code will be sent to the same window as the consent window. Note also that the url needs to be encoded(escaped) because it contains some illegal characters in the scope and redirect_uri parameter. The correct authorization url is:
https://accounts.google.com/o/oauth2/v2/auth?client_id=xxxx.apps.googleusercontent.com&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&access_type=offline&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob
Now the user has gotten the auth code. He should feed it to my app which in turn exchanges the auth code for an access token from the server.
curl --data client_id=xxxx.apps.googleusercontent.com --data client_secret=yyyy --data code=zzzz --data redirect_uri=urn:ietf:wg:oauth:2.0:oob --data grant_type=authorization_code https://www.googleapis.com/oauth2/v4/token
We know the –data option for the curl command will make curl issue a POST http request. The client_id and the client_secret are gotten from my(the developer’s) account. The code is the auth code obtained by the user in previous step. All the data options are sent in the http payload. The response is like:
{ "access_token": "xxxxxxxxxx", "expires_in": 3200, "refresh_token": "yyyyyyyyy", "scope": "https://www.googleapis.com/auth/youtube.force-ssl", "token_type": "Bearer" }
Note that the client_id in getting the access token must match the client_id in getting the authorization code. Otherwise, you will get the error:
{
“error”: “unauthorized_client”,
“error_description”: “Unauthorized”
}
and the authorization code will be revoked because the authorization server thinks the authorization code it issued has been intercepted by another unauthorized client. Now,even you provide the correct client_id and client_secret, you will still fail in getting the access token with the following error:
{
“error”: “invalid_grant”,
“error_description”: “Bad Request”
}
You will have to re-initialize the authorization process to get a new authorization code.
If you provide a matching client_id but a wrong client_secret when requesting the access token, you will get the error:
{
“error”: “invalid_client”,
“error_description”: “Unauthorized”
}
But the authorization code is still valid. You only need to provide the correct client_secret to be successful in getting the access token. The authorization code records the information about who(the resource owner) authorizes which client to get access token, which is the essential of OAuth protocol. Because the authorization code grant type has two separated steps in obtaining the access token, the client must provide the client_secret to prove its identity in the second step, otherwise any client who gets the auth code would have access to the resource.
After you get an access token, the authorization code is expired. If you try to get another access token using the same authorization code, you will get the error:
{
“error”: “invalid_grant”,
“error_description”: “Bad Request”
}
We have gotten the access token. The next step is call the api to do the real work.
use API
The OAuth process is the same for all google apis. But the usages of different google apis are quite different. Take the youtube caption.download api for example. You should call it as:
curl "https://youtube.googleapis.com/youtube/v3/captions/captionid?key=xxxx" --header "Authorization: Bearer yyyyy" --header "Accept: application/json"
yyyy is the access token which is delivered to server via the http Authorization header, not the payload. I do not know why the access token is called Bearer here. You may get the following response:
{ "error": { "code": 403, "message": "The permissions associated with the request are not sufficient to download the caption track. The request might not be properly authorized, or the video order might not have enabled third-party contributions for this caption.", "errors": [ { "message": "The permissions associated with the request are not sufficient to download the caption track. The request might not be properly authorized, or the video order might not have enabled third-party contributions for this caption.", "domain": "youtube.caption", "reason": "forbidden", "location": "id", "locationType": "parameter" } ] } }
The response is an error because googleapis does not allow you to download captions of videos that are not uploaded by yourself.
Conclusion
In the past, I thought google apis can only be used by programming based on existing API SDKs. They provide php SDK, java SDK, python SDK, C++ SDK, etc. But actually it is not necessarily. Calling API using curl is also cool.