Adding a Method Protected by OAuth
In this tutorial, we are adding a new protected method called
greetings.authed
.
Any client can access your Endpoints API methods unless you protect them with OAuth 2.0. In some scenarios, you may want to restrict access to some or all of the API methods.
To protect a method in the backend API, you need to do the following:
-
Use the
allowed_client_ids
argument in the@endpoints.api
decorator to provide a whitelist of allowed clients. -
Add the email scope (
endpoints.EMAIL_SCOPE
) to your@endpoints.api
decorator. -
Add a current user check (
endpoints.get_current_user
) to each method you wish to protect.
Adding an OAuth 2.0-protected method
To protect a method by OAuth:
-
In the
helloworld_api.py
file created previously, add the following client ID whitelist under the imports:import endpoints from protorpc import messages from protorpc import message_types from protorpc import remote WEB_CLIENT_ID = 'replace this with your web client application ID' ANDROID_CLIENT_ID = 'replace this with your Android client ID' IOS_CLIENT_ID = 'replace this with your iOS client ID' ANDROID_AUDIENCE = WEB_CLIENT_ID package = 'Hello'
In the snippet above, we show only dummy values for web, Android, and iOS clients. You replace each of these with a real client ID when you add the corresponding client.
For more information on creating client IDs, see Creating OAuth 2.0 client IDs .
-
In the
@endpoints.api
add theallowed_client_ids
argument, supplying the list of client IDs that you want to authenticate, and add the scopes argument, specifying the email scope as shown:@endpoints.api(name='helloworld', version='v1', allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID, endpoints.API_EXPLORER_CLIENT_ID], audiences=[ANDROID_AUDIENCE], scopes=[endpoints.EMAIL_SCOPE]) class HelloWorldApi(remote.Service): """helloworld API v1."""
In the snippet above, notice the ID
endpoints.API_EXPLORER_CLIENT_ID
; this is needed for testing against the API Explorer in production.Also, notice the use of the
audiences
argument (ANDROID_AUDIENCE
), which is defined as the web client. This argument is required for Android clients and is not used by other clients. As shown, it must be set to the web client ID. For more information, see Allowed Client IDs and Audiences .Finally, notice that we supplied the email scope,
endpoints.EMAIL_SCOPE
. Although you can add other scopes, you must always include the email scope if you use OAuth. -
After the method definition for
greetings.getGreeting
, add the following code:@endpoints.method(message_types.VoidMessage, Greeting, path='hellogreeting/authed', http_method='POST', name='greetings.authed') def greeting_authed(self, request): current_user = endpoints.get_current_user() email = (current_user.email() if current_user is not None else 'Anonymous') return Greeting(message='hello %s' % (email,))
-
When you are finished, your file should look like this:
"""Hello World API implemented using Google Cloud Endpoints. Defined here are the ProtoRPC messages needed to define Schemas for methods as well as those methods defined in an API. """ import endpoints from protorpc import messages from protorpc import message_types from protorpc import remote WEB_CLIENT_ID = 'replace this with your web client application ID' ANDROID_CLIENT_ID = 'replace this with your Android client ID' IOS_CLIENT_ID = 'replace this with your iOS client ID' ANDROID_AUDIENCE = WEB_CLIENT_ID package = 'Hello' class Greeting(messages.Message): """Greeting that stores a message.""" message = messages.StringField(1) class GreetingCollection(messages.Message): """Collection of Greetings.""" items = messages.MessageField(Greeting, 1, repeated=True) STORED_GREETINGS = GreetingCollection(items=[ Greeting(message='hello world!'), Greeting(message='goodbye world!'), ]) @endpoints.api(name='helloworld', version='v1', allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID, endpoints.API_EXPLORER_CLIENT_ID], audiences=[ANDROID_AUDIENCE], scopes=[endpoints.EMAIL_SCOPE]) class HelloWorldApi(remote.Service): """helloworld API v1.""" MULTIPLY_METHOD_RESOURCE = endpoints.ResourceContainer( Greeting, times=messages.IntegerField(2, variant=messages.Variant.INT32, required=True)) @endpoints.method(MULTIPLY_METHOD_RESOURCE, Greeting, path='hellogreeting/{times}', http_method='POST', name='greetings.multiply') def greetings_multiply(self, request): return Greeting(message=request.message * request.times) @endpoints.method(message_types.VoidMessage, GreetingCollection, path='hellogreeting', http_method='GET', name='greetings.listGreeting') def greetings_list(self, unused_request): return STORED_GREETINGS ID_RESOURCE = endpoints.ResourceContainer( message_types.VoidMessage, id=messages.IntegerField(1, variant=messages.Variant.INT32)) @endpoints.method(ID_RESOURCE, Greeting, path='hellogreeting/{id}', http_method='GET', name='greetings.getGreeting') def greeting_get(self, request): try: return STORED_GREETINGS.items[request.id] except (IndexError, TypeError): raise endpoints.NotFoundException('Greeting %s not found.' % (request.id,)) @endpoints.method(message_types.VoidMessage, Greeting, path='hellogreeting/authed', http_method='POST', name='greetings.authed') def greeting_authed(self, request): current_user = endpoints.get_current_user() email = (current_user.email() if current_user is not None else 'Anonymous') return Greeting(message='hello %s' % (email,)) APPLICATION = endpoints.api_server([HelloWorldApi])
Deploying and Testing
To test the backend API with real Auth, you must deploy your backend API to the App Engine project you created at the beginning of this tutorial.
To deploy and run:
-
Deploy the backend you just created to App Engine by invoking the following:
~/path/to/python/sdk/google_appengine/appcfg.py update helloworld
The first time you deploy, you may be prompted to supply your Google account email and a password before deployment is allowed. Follow the prompts to supply these.
When the backend finishes deploying, a message similar to this one is displayed:
09:08 AM Completed update of app: your-app-id, version: 1
-
Check the logs for your project to make sure the backend deployed successfully.
-
Open API Explorer at this URL in your browser:
https://your-app-id.appspot.com/_ah/api/explorer
-
In the API Explorer, click helloworld API > helloworld.greetings.authed .
-
In the upper right of the API Explorer page, locate the toggle control labeled Authorize requests using OAuth 2.0 and click it On .
-
You'll be prompted to manually supply a scope: supply the value
https://www.googleapis.com/auth/userinfo.email
and click Authorize . -
Click Execute and observe the authorization in the request, and a greeting with your user name in the response.
Code summary
If the request coming in from the client has a valid auth token or is in the
list of authorized
clientIDs
, the backend framework supplies a valid User
when
endpoints.get_current_user()
is invoked. If the incoming request does not
have a valid auth token or if the client is not on the
clientIDs
whitelist,
endpoints.get_current_user()
returns
Null
.
Accordingly, if the request comes from a client that isn't in the whitelist,
there will be no current user. Your own code must handle both the case where
the current user is null and the case where there is a valid current user. If
there is no user, for example, you could choose to return a not-authenticated
error or perform some other desired action. In this tutorial, we simply
identify the non authenticated user as
Anonymous
.
Next...
This completes the tutorial.
For the advanced topics such as using datastore and creating clients of the API backend, see the following:
- The Tic Tac Toe sample, which shows how to build a backend that uses datastore.
- Using Endpoints in an Android Client
- Using Endpoints in an iOS Client
- Using Endpoints in a JavaScript Client .