Welcome to Part 5 of the Google Cloud Endpoints Tutorial.
The full series:
- Part 1 : We looked at writing a Google Cloud Endpoints class manually by using the various annotations and Exception classes that are available. We create a Quotes API that provided a JSON + REST based interface to manage Quotes (add, modify, delete and retrieve quotes).
- Part 2 : We generated the Google Cloud Endpoints class from a JDO Annotated Entity class by using the Code Generation tools provided in the library.
- Part 3 : Generated the Cloud Endpoints Client Library for Android and wrote an Android application that invokes the Endpoints API.
- Part 5: Securing our API
- Part 7 : Calling a Secured API from an Android client.
I have also published a list of Cloud Endpoints Tips:
- Check Endpoint Deployment Status
- Throw the Right Exception Classes
- Understand Injected Types
- Understand @API and @APIMethod Annotations
- Using Cursor and Limit parameters
- The @APIResourceProperty Annotation
In this episode
Security is a key consideration for any application that you host on the public internet. This is especially important when you are writing an API for others to use in their client applications. You need to ensure that the API that you have written is secure, allows only authenticated users to perform operations and so on.
What do you need ?
- You have a working development environment for Google App Engine. This includes the Google Eclipse plugin.
- The API Project 2 (Quotes Endpoint Project) loaded in your Development Environment. This project has now been updated to secure certain operations, which we shall cover in this episode. So, if you have an older version of it, you might want to sync with the latest code. Even if you do not want to load it, just follow along in the tutorial to see how to secure your API.
My Development Environment
This remains the same, no changes at all. My development environment is given below:
- Eclipse Juno
- Google Eclipse plugin with App Engine SDK 1.8.8.
- Mac + Windows machine (I kept switching from one to another, to keep everyone happy ;-))
Securing the Quotes API
Let us recap quickly on what we have done so far. We have written a Quotes API hosted on Google App Engine that allows us to perform CRUD operations via a HTTP REST API. A Quote record contains a couple of attributes, the author and the famous quote that the author gave.
If you look at the API Methods, we had the following methods exposed:
To secure the API, we need to first determine if we are interested in securing all the methods that we expose or only certain methods. This flexibility is provided to you by Google Cloud Endpoints.
So, what we are going to see in this operation is enabling security for one of the methods: insertQuote. What this means is that if you are not an authorized user, that any call to the insertQuote method will fail. Once you understand how to do it for one method, it should be straight forward for you to extend it to other methods.
Sounds good? Lets move on.
Google has clearly documented the steps that we need to follow to add authorization to our API and we will follow that as specified.
Step 1 : Specify the Client Ids
The first thing we need to decide is what kind of clients are we expecting for our API. The usual suspects are a Web client, Android client, iOS client and so on. What this means is that we need to create these client Ids and both the Server side i.e. our API Implementation and the Client need to be aware of these Client Ids, so that the Client can be identified with the Server.
To do that, I will assume that since you have been reading these tutorial series, you have also created an App Engine project. If not, this is the time to do it and there is no other way out from this :-)
To create the client Ids, you will need to visit the Google Cloud Console for your App Engine project. In case, you are yet to deploy your API to the App Engine instance, I suggest this is a good time to create the application.
In my case, I already deployed this API to an App Engine instance (mybackendapi). So I directly visit the Google Cloud Console for my account and from my list of projects, I select the particular API project (in my case again, it is the mybackendapi project)
You will see a similar screen as shown below:
Go ahead and click on APIs & auth link from the left menu and then the Credentials link as shown below:
We need to generate a Web Client here and in the later episode, I shall demonstrate how to generate an Android client. But for now, let us just generate the Web Client ID.
Click on the Create New Client Id button that you see in RED. This lets you create Client Ids for Web, Android and more. You will see a screen as shown below:
You can specify multiple origins here (one on each line). So I have specified the localhost and also the my appengine application origin. The redirect URI is not applicable here and is automatically populated for you. Let that be as is.
Click on the Create Client ID button. This will generate the Client Id and you will see that in your console as shown below:
The next step is important and is something that you might forget. It happened to me, at least the first time.
Click on the Consent screen link. This is the screen that is shown when you are authorizing the application on your behalf. You can fill in all the details but at a minimum, you must fill our the Email address and Product name.
Click on Save button to save your settings for the Consent Screen.
Now that we have created the Client Ids, we need to ultimately provide this information to our Server API i.e. Endpoints API. To help keep all the IDs in one place, Google documentation shows the use of a Constants.java file and that is a good approach to go with. So we will create a Constants.java file and place our Client Ids in that as shown below:
Note the following:
- We have defined the constant WEB_CLIENT_ID that should contain the value of the Client ID that we just created.
- The next thing to note is the EMAIL_SCOPE. This is the OAuth 2.0 Scope.
- The ANDROID_CLIENT_ID, we have not yet generated. But once we generate it (as we will see in a later episode, you will need to put it here).
- There is also the ANDROID_AUDIENCE that is given the same value as the WEB_CLIENT_ID. We shall use this in later episode when we create our Android client.
Step 2 : Add the Client Ids, Scope and Audiences to your @API or @APIMethod
We need to first make a decision whether we want to protect all the methods or only some methods. If you want to protect all the methods, you need to deal with the @API annotation or if you are interested in protecting specific methods, then you need to worry about the @APIMethod (i.e. the methods in your class).
As described earlier, we are going to protect only the insertQuote method, so we shall we enhancing the @APIMethod annotation for the insertQuote method as shown below:
Let us take a look at the important points here:
- We have added 3 attributes to the @APIMethod for the insertQuote method. They are scopes, clientIds and audiences as covered earlier.
- Note that the clientIds also contains an additional one : com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID and this is required because we are going to use the API Explorer for testing out our API. Makes sense doesn’t it ?
Step 3 : Add the Add a User parameter to your methods
Keep your focus on the above Gist for the insertQuote method. For every method that you want to authorize for, you need to add a User parameter at the end as we have done. If the user is authorized correctly, an instance of the com.google.appengine.api.users.User class will be passed in here.
But you will need to be careful here. You cannot assume that the object will be populated always. If the Authorization fails, the User object will be null. So you need to do your own little check here as we have.
We check for null value and it that is indeed the case, we throw a com.google.api.server.spi.response.UnauthorizedException, which will be translated by the Endpoints layer into a correct HTTP 401 Status Code. The rest of the code is the standard code from the previous episodes.
That is all you need to do to the methods. In case you wish to apply this to other methods, you can do that.
Step 4 : Regenerate your APIs
This step is not really needed at this point till we revisit our client applications but safe to say that it is important that you generate the Client Libraries again because you have modified the details for the APIs. To generate your Client Libraries again, it should be familiar to you but I will say it again.
Testing your secured API via the API Explorer
All right ! We have done the hard work of securing the API ? Or have we … let’s find that out!
In my case, I am testing the same in my local dev environment. I visit http://localhost:8888/_ah/api/explorer and I visit the insertQuote method. Remember that is the method that we secured and want to test out.
We simply provide the Quote Author and Message and click on Execute. If you have done things carefully and correctly till now, you will see an error pop up as shown below. It clearly states that you need to be authenticated.
Note that, the Response is correctly thrown back as we had done in our code. We had thrown an UnauthorizedException and that is translated back into a HTTP 401 Status Code correctly as shown below.
Since we are using the API Explorer client over here, we need to login over here. In the right corner, you will find a Toggle as shown below:
Click on the Toggle switch to turn it ON. This brings up the typically OAuth dance sequence. The first popup shows the SCOPE that we had defined on our API. Remember the EMAIL_SCOPE constant. That is selected by default. Simply click on the Authorize button.
This will bring up a Google login. Just login or select the Account (in case you have multiple Google Accounts). Complete the Login successfully and you will be back in the API explorer with the Toggle switched on to ON as shown below:
Now, fire off the insertQuote method again. And this time, all is well. Notice in the POST Request HTTP Headers that the Authorization Header is generated by the API Explorer.
This completes all the work right from generating a Client ID, specifying Client Id details in the methods that we want to secure and then testing it out via the API explorer.
Project Source Code
I have updated the source code for the MyAPIProject2. You can download the entire source code over here.