From 6ecc915ee31682376758476a3a8b522fd22c27fb Mon Sep 17 00:00:00 2001 From: Limin Wang Date: Wed, 27 Jul 2016 17:17:24 -0700 Subject: [PATCH 1/3] Auth samples for Endpoints. Added three samples: 1. Use App Engine default service account client (without key file). 2. Use non-default service account client (without key file). 3. Use Google ID token client (without key file) --- appengine/flexible/endpoints/README.md | 76 ++++++++++++++- .../service_to_service_gae_default/app.yaml | 7 ++ .../service_to_service_gae_default/main.py | 81 ++++++++++++++++ .../app.yaml | 7 ++ .../main.py | 96 +++++++++++++++++++ .../service_to_service_non_default/app.yaml | 7 ++ .../appengine_config.py | 3 + .../service_to_service_non_default/main.py | 93 ++++++++++++++++++ appengine/flexible/endpoints/requirements.txt | 1 + appengine/flexible/endpoints/swagger.yaml | 45 ++++++++- 10 files changed, 408 insertions(+), 8 deletions(-) create mode 100644 appengine/flexible/endpoints/clients/service_to_service_gae_default/app.yaml create mode 100644 appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py create mode 100644 appengine/flexible/endpoints/clients/service_to_service_google_id_token/app.yaml create mode 100644 appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py create mode 100644 appengine/flexible/endpoints/clients/service_to_service_non_default/app.yaml create mode 100644 appengine/flexible/endpoints/clients/service_to_service_non_default/appengine_config.py create mode 100644 appengine/flexible/endpoints/clients/service_to_service_non_default/main.py diff --git a/appengine/flexible/endpoints/README.md b/appengine/flexible/endpoints/README.md index a31b9b86c5ef..6337f23e9e61 100644 --- a/appengine/flexible/endpoints/README.md +++ b/appengine/flexible/endpoints/README.md @@ -57,9 +57,9 @@ With the API key, you can use the echo client to access the API: $ python clients/echo-client.py https://YOUR-PROJECT-ID.appspot.com YOUR-API-KEY -### Using the JWT client. +### Using the JWT client (with key file) -The JWT client demonstrates how to use service accounts to authenticate to endpoints. To use the client, you'll need both an API key (as described in the echo client section) and a service account. To create a service account: +The JWT client demonstrates how to use a service account to authenticate to endpoints with the service account's private key file. To use the client, you'll need both an API key (as described in the echo client section) and a service account. To create a service account: 1. Open the Credentials page of the API Manager in the [Cloud Console](https://console.cloud.google.com/apis/credentials). 2. Click 'Create credentials'. @@ -76,7 +76,7 @@ Now you can use the JWT client to make requests to the API: $ python clients/google-jwt-client.py https://YOUR-PROJECT-ID.appspot.com YOUR-API-KEY /path/to/service-account.json -### Using the ID Token client. +### Using the ID Token client (with key file) The ID Token client demonstrates how to use user credentials to authenticate to endpoints. To use the client, you'll need both an API key (as described in the echo client section) and a OAuth2 client ID. To create a client ID: @@ -93,3 +93,73 @@ To use the client ID for authentication: Now you can use the client ID to make requests to the API: $ python clients/google-id-token-client.py https://YOUR-PROJECT-ID.appspot.com YOUR-API-KEY /path/to/client-id.json + +### Using the App Engine default service account client (no key file needed) + +The App Engine default service account client demonstrates how to use the Google App Engine default service account to authenticate to endpoints. +We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). + +To use the App Engine default service account for authentication: + +1. Update the `gae_default_service_account`'s `x-issuer` and `x-jwks_uri` in `swagger.yaml` with your client project ID. +2. Redeploy your server application. +3. Update clients/service_to_service_gae_default/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. +4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_gae_default directory. +This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use +the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). + + appcfg.py -A -V v1 update . + +Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using +the client's service account. + +### Using the service account client (no key file needed) + +The service account client demonstrates how to use a non-default service account to authenticate to endpoints. +We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). + +In the example, we use Google Cloud Identity and Access Management (IAM) API to create a JSON Web Token (JWT) for a service account, and use it to call an Endpoints API. + +To use the client, you will need to enable "Service Account Actor" role for App Engine default service account: +1. Go to [IAM page] of your client project (https://console.cloud.google.com/iam-admin/iam). +2. For App Engine default service account, from “Role(s)” drop-down menu, select “Project”-“Service Account Actor”, and Save. + +You also need to install Google API python library because the client code (main.py) uses googleapiclient, +which is a python library that needs to be uploaded to App Engine with your application code. After you run "pip install -t lib -r requirements", +Google API python client library should have already been installed under 'lib' directory. Additional information can be found +[here](https://cloud.google.com/appengine/docs/python/tools/using-libraries-python-27#requesting_a_library). + +To use the client for authentication: +1. Update the `google_service_account`'s `x-issuer` and `x-jwks_uri` in `swagger.yaml` with your service account email. +2. Redeploy your server application. +3. Update clients/service_to_service_non_default/main.py, replace 'YOUR-SERVICE-ACCOUNT-EMAIL', 'YOUR-SERVER-PROJECT-ID' and 'YOUR-CLIENT-PROJECT-ID' +with your service account email, your server project ID, and your client project ID. +4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_non_default directory. +This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use +the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). + + appcfg.py -A -V v1 update . + +Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using +the client's service account. + +### Using the ID token client (no key file needed) + +This example demonstrates how to authenticate to endpoints from Google App Engine default service account using Google ID token. +In the example, we first create a JSON Web Token (JWT) using the App Engine default service account. We then request a Google +ID token using the JWT, and call an Endpoints API using the Google ID token. + +We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). + +To use the client for authentication: +1. Update the `google_id_token`'s audiences, replace `YOUR-SERVER-PROJECT-ID` with your server project ID. +2. Redeploy your server application. +3. Update clients/service_to_service_google_id_token/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. +4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_google_id_token directory. +This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use +the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). + + appcfg.py -A -V v1 update . + +Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API from +the client's service account using Google ID token. diff --git a/appengine/flexible/endpoints/clients/service_to_service_gae_default/app.yaml b/appengine/flexible/endpoints/clients/service_to_service_gae_default/app.yaml new file mode 100644 index 000000000000..f041d384c057 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_gae_default/app.yaml @@ -0,0 +1,7 @@ +runtime: python27 +api_version: 1 +threadsafe: true + +handlers: +- url: /.* + script: main.app diff --git a/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py b/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py new file mode 100644 index 000000000000..03552da1c306 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py @@ -0,0 +1,81 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0(the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http: // www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Example of calling a Google Cloud Endpoint API with a JWT signed by +Google App Engine Default Service Account.""" + +import base64 +import httplib +import json +import time + +from google.appengine.api import app_identity +import webapp2 + +DEFAUTL_SERVICE_ACCOUNT = 'YOUR-CLIENT-PROJECT-ID@appspot.gserviceaccount.com' +HOST = "YOUR-SERVER-PROJECT-ID.appspot.com" + + +def generate_jwt(): + """Generates a signed JSON Web Token using the Google App Engine default + service account.""" + now = int(time.time()) + + header_json = json.dumps({ + "typ": "JWT", + "alg": "RS256"}) + + payload_json = json.dumps({ + 'iat': now, + # expires after one hour. + "exp": now + 3600, + # iss is the Google App Engine default service account email. + 'iss': DEFAUTL_SERVICE_ACCOUNT, + 'sub': DEFAUTL_SERVICE_ACCOUNT, + # aud must match 'audience' in the security configuration in your + # swagger spec.It can be any string. + 'aud': 'echo.endpoints.sample.google.com', + "email": DEFAUTL_SERVICE_ACCOUNT + }) + + headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) + (key_name, signature) = app_identity.sign_blob(headerAndPayload) + signed_jwt = '{}.{}'.format(headerAndPayload, + base64.urlsafe_b64encode(signature)) + + return signed_jwt + + +def make_request(signed_jwt): + """Makes a request to the auth info endpoint for Google JWTs.""" + headers = {'Authorization': 'Bearer {}'.format(signed_jwt)} + conn = httplib.HTTPSConnection(HOST) + conn.request("GET", '/auth/info/googlejwt', None, headers) + res = conn.getresponse() + conn.close() + return res.read() + + +class MainPage(webapp2.RequestHandler): + def get(self): + self.response.headers['Content-Type'] = 'text/plain' + signed_jwt = generate_jwt() + res = make_request(signed_jwt) + self.response.write(res) + + +app = webapp2.WSGIApplication([ + ('/', MainPage), +], debug=True) diff --git a/appengine/flexible/endpoints/clients/service_to_service_google_id_token/app.yaml b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/app.yaml new file mode 100644 index 000000000000..f041d384c057 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/app.yaml @@ -0,0 +1,7 @@ +runtime: python27 +api_version: 1 +threadsafe: true + +handlers: +- url: /.* + script: main.app diff --git a/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py new file mode 100644 index 000000000000..cbf3924c0d93 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py @@ -0,0 +1,96 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0(the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http: // www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Example of calling a Google Cloud Endpoint API from Google App Engine +Default Service Account using Google ID token.""" + +import base64 +import httplib +import json +import time +import urllib + +from google.appengine.api import app_identity +import webapp2 + +DEFAUTL_SERVICE_ACCOUNT = "YOUR-CLIENT-PROJECT-ID@appspot.gserviceaccount.com" +HOST = "YOUR-SERVER-PROJECT-ID.appspot.com" +TARGET_AUD = "YOUR-SERVER-PROJECT-ID@appspot.gserviceaccount.com" + + +def generate_jwt(): + """Generates a signed JSON Web Token using the Google App Engine default + service account.""" + now = int(time.time()) + + header_json = json.dumps({ + "typ": "JWT", + "alg": "RS256"}) + + payload_json = json.dumps({ + "iat": now, + # expires after one hour. + "exp": now + 3600, + # iss is the Google App Engine default service account email. + "iss": DEFAUTL_SERVICE_ACCOUNT, + # scope must match 'audience' for google_id_token in the security + # configuration in your swagger spec. + "scope": TARGET_AUD, + # aud must be Google token endpoints URL. + "aud": "https://www.googleapis.com/oauth2/v4/token" + }) + + headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) + (key_name, signature) = app_identity.sign_blob(headerAndPayload) + signed_jwt = '{}.{}'.format(headerAndPayload, + base64.urlsafe_b64encode(signature)) + + return signed_jwt + + +def get_id_token(): + """Request a Google ID token using a JWT.""" + params = urllib.urlencode({ + 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', + 'assertion': generate_jwt()}) + headers = {"Content-Type": "application/x-www-form-urlencoded"} + conn = httplib.HTTPSConnection("www.googleapis.com") + conn.request("POST", "/oauth2/v4/token", params, headers) + res = json.loads(conn.getresponse().read()) + conn.close() + return res['id_token'] + + +def make_request(token): + """Makes a request to the auth info endpoint for Google ID token.""" + headers = {'Authorization': 'Bearer {}'.format(token)} + conn = httplib.HTTPSConnection(HOST) + conn.request("GET", '/auth/info/googleidtoken', None, headers) + res = conn.getresponse() + conn.close() + return res.read() + + +class MainPage(webapp2.RequestHandler): + def get(self): + self.response.headers['Content-Type'] = 'text/plain' + token = get_id_token() + res = make_request(token) + self.response.write(res) + + +app = webapp2.WSGIApplication([ + ('/', MainPage), +], debug=True) diff --git a/appengine/flexible/endpoints/clients/service_to_service_non_default/app.yaml b/appengine/flexible/endpoints/clients/service_to_service_non_default/app.yaml new file mode 100644 index 000000000000..f041d384c057 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_non_default/app.yaml @@ -0,0 +1,7 @@ +runtime: python27 +api_version: 1 +threadsafe: true + +handlers: +- url: /.* + script: main.app diff --git a/appengine/flexible/endpoints/clients/service_to_service_non_default/appengine_config.py b/appengine/flexible/endpoints/clients/service_to_service_non_default/appengine_config.py new file mode 100644 index 000000000000..f4489ff9680b --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_non_default/appengine_config.py @@ -0,0 +1,3 @@ +from google.appengine.ext import vendor + +vendor.add('lib') diff --git a/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py b/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py new file mode 100644 index 000000000000..e9dbac7dee06 --- /dev/null +++ b/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py @@ -0,0 +1,93 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0(the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http: // www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Example of calling a Google Cloud Endpoint API with a JWT signed by a +Service Account.""" + +import base64 +import httplib +import json +import time + +from googleapiclient.discovery import build +import httplib2 +from oauth2client.contrib.appengine import AppAssertionCredentials +import webapp2 + +SERVICE_ACCOUNT_EMAIL = "YOUR-SERVICE-ACCOUNT-EMAIL" +HOST = "YOUR-SERVER-PROJECT-ID.appspot.com" +SERVICE_ACCOUNT = \ + "projects/YOUR-CLIENT-PROJECT-ID/serviceAccounts/YOUR-SERVICE-ACCOUNT-EMAIL" + + +def generate_jwt(): + """Generates a signed JSON Web Token using a service account.""" + credentials = AppAssertionCredentials( + 'https://www.googleapis.com/auth/iam') + http_auth = credentials.authorize(httplib2.Http()) + service = build(serviceName='iam', version='v1', http=http_auth) + + now = int(time.time()) + + header_json = json.dumps({ + "typ": "JWT", + "alg": "RS256"}) + + payload_json = json.dumps({ + 'iat': now, + # expires after one hour. + "exp": now + 3600, + # iss is the service account email. + 'iss': SERVICE_ACCOUNT_EMAIL, + 'sub': SERVICE_ACCOUNT_EMAIL, + # aud must match 'audience' in the security configuration in your + # swagger spec.It can be any string. + 'aud': 'echo.endpoints.sample.google.com', + "email": SERVICE_ACCOUNT_EMAIL + }) + + headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) + slist = service.projects().serviceAccounts().signBlob( + name=SERVICE_ACCOUNT, + body={'bytesToSign': base64.b64encode(headerAndPayload)}) + res = slist.execute() + signature = base64.urlsafe_b64encode( + base64.decodestring(res['signature'])) + signed_jwt = '{}.{}'.format(headerAndPayload, signature) + + return signed_jwt + + +def make_request(signed_jwt): + """Makes a request to the auth info endpoint for Google JWTs.""" + headers = {'Authorization': 'Bearer {}'.format(signed_jwt)} + conn = httplib.HTTPSConnection(HOST) + conn.request("GET", '/auth/info/googlejwt', None, headers) + res = conn.getresponse() + conn.close() + return res.read() + + +class MainPage(webapp2.RequestHandler): + def get(self): + self.response.headers['Content-Type'] = 'text/plain' + signed_jwt = generate_jwt() + res = make_request(signed_jwt) + self.response.write(res) + + +app = webapp2.WSGIApplication([ + ('/', MainPage), +], debug=True) diff --git a/appengine/flexible/endpoints/requirements.txt b/appengine/flexible/endpoints/requirements.txt index 65cbd791ff18..748bdbea6829 100644 --- a/appengine/flexible/endpoints/requirements.txt +++ b/appengine/flexible/endpoints/requirements.txt @@ -5,3 +5,4 @@ gcloud==0.17.0 six==1.10.0 pyyaml==3.11 requests==2.10.0 +google-api-python-client diff --git a/appengine/flexible/endpoints/swagger.yaml b/appengine/flexible/endpoints/swagger.yaml index 8792ee42b208..b2f6c8d5245b 100644 --- a/appengine/flexible/endpoints/swagger.yaml +++ b/appengine/flexible/endpoints/swagger.yaml @@ -44,11 +44,21 @@ paths: schema: $ref: "#/definitions/authInfoResponse" x-security: - - google_jwt: - audiences: - # This must match the "aud" field in the JWT. You can add multiple - # audiences to accept JWTs from multiple clients. - - "echo.endpoints.sample.google.com" + - google_jwt: + audiences: + # This must match the "aud" field in the JWT. You can add multiple + # audiences to accept JWTs from multiple clients. + - "echo.endpoints.sample.google.com" + - gae_default_service_account: + audiences: + # This must match the "aud" field in the JWT. You can add multiple + # audiences to accept JWTs from multiple clients. + - "echo.endpoints.sample.google.com" + - google_service_account: + audiences: + # This must match the "aud" field in the JWT. You can add multiple + # audiences to accept JWTs from multiple clients. + - "echo.endpoints.sample.google.com" "/auth/info/googleidtoken": get: description: "Returns the requests' authentication information." @@ -66,6 +76,7 @@ paths: # Your OAuth2 client's Client ID must be added here. You can add # multiple client IDs to accept tokens from multiple clients. - "YOUR-CLIENT-ID" + - "YOUR-SERVER-PROJECT-ID@appspot.gserviceaccount.com" "/auth/info/firebase": get: description: "Returns the requests' authentication information." @@ -81,6 +92,7 @@ paths: - firebase: audiences: - "YOUR-PROJECT-ID" + definitions: echoMessage: properties: @@ -92,6 +104,7 @@ definitions: type: "string" email: type: "string" + securityDefinitions: # This section configures basic authentication with an API key. api_key: @@ -109,6 +122,28 @@ securityDefinitions: x-issuer: "jwt-client.endpoints.sample.google.com" # Update this with your service account's email address. x-jwks_uri: "https://www.googleapis.com/service_accounts/v1/jwk/YOUR-SERVICE-ACCOUNT-EMAIL" + # This section configures authentication using Google App Engine default + # service account to sign a json web token. This is mostly used for + # server-to-server communication. + gae_default_service_account: + authorizationUrl: "" + flow: "implicit" + type: "oauth2" + # Replace YOUR-CLIENT-PROJECT-ID with your client project ID. + x-issuer: "YOUR-CLIENT-PROJECT-ID@appspot.gserviceaccount.com" + # Replace YOUR-CLIENT-PROJECT-ID with your client project ID. + x-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/YOUR-CLIENT-PROJECT-ID@appspot.gserviceaccount.com" + # This section configures authentication using a service account + # to sign a json web token. This is mostly used for server-to-server + # communication. + google_service_account: + authorizationUrl: "" + flow: "implicit" + type: "oauth2" + # Replace YOUR-SERVICE-ACCOUNT-EMAIL with your service account email. + x-issuer: "YOUR-SERVICE-ACCOUNT-EMAIL" + # Replace YOUR-SERVICE-ACCOUNT-EMAIL with your service account email. + x-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/YOUR-SERVICE-ACCOUNT-EMAIL" # This section configures authentication using Google OAuth2 ID Tokens. # ID Tokens can be obtained using OAuth2 clients, and can be used to access # your API on behalf of a particular user. From 4e8801c0714b801d2bc52ccf6865f53a116119ee Mon Sep 17 00:00:00 2001 From: Limin Wang Date: Fri, 29 Jul 2016 15:49:56 -0700 Subject: [PATCH 2/3] Auth Samples (addressed 2nd pass of comments) --- appengine/flexible/endpoints/README.md | 21 ++++++++----------- .../service_to_service_gae_default/main.py | 10 +++++---- .../main.py | 10 +++++---- .../service_to_service_non_default/main.py | 5 +++-- appengine/flexible/endpoints/requirements.txt | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/appengine/flexible/endpoints/README.md b/appengine/flexible/endpoints/README.md index 6337f23e9e61..6e7d1baa9da0 100644 --- a/appengine/flexible/endpoints/README.md +++ b/appengine/flexible/endpoints/README.md @@ -98,17 +98,16 @@ Now you can use the client ID to make requests to the API: The App Engine default service account client demonstrates how to use the Google App Engine default service account to authenticate to endpoints. We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). +Both server and client projects are running Google App Engine standard applications. To use the App Engine default service account for authentication: 1. Update the `gae_default_service_account`'s `x-issuer` and `x-jwks_uri` in `swagger.yaml` with your client project ID. 2. Redeploy your server application. 3. Update clients/service_to_service_gae_default/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. -4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_gae_default directory. -This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use -the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). +4. Upload your application to Google App Engine by invoking the following command. - appcfg.py -A -V v1 update . + gcloud app deploy app.yaml --project= --promote Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using the client's service account. @@ -117,6 +116,7 @@ the client's service account. The service account client demonstrates how to use a non-default service account to authenticate to endpoints. We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). +Both server and client projects are running Google App Engine standard applications. In the example, we use Google Cloud Identity and Access Management (IAM) API to create a JSON Web Token (JWT) for a service account, and use it to call an Endpoints API. @@ -134,11 +134,9 @@ To use the client for authentication: 2. Redeploy your server application. 3. Update clients/service_to_service_non_default/main.py, replace 'YOUR-SERVICE-ACCOUNT-EMAIL', 'YOUR-SERVER-PROJECT-ID' and 'YOUR-CLIENT-PROJECT-ID' with your service account email, your server project ID, and your client project ID. -4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_non_default directory. -This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use -the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). +4. Upload your application to Google App Engine by invoking the following command. - appcfg.py -A -V v1 update . + gcloud app deploy app.yaml --project= --promote Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using the client's service account. @@ -150,16 +148,15 @@ In the example, we first create a JSON Web Token (JWT) using the App Engine defa ID token using the JWT, and call an Endpoints API using the Google ID token. We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). +Both server and client projects are running Google App Engine standard applications. To use the client for authentication: 1. Update the `google_id_token`'s audiences, replace `YOUR-SERVER-PROJECT-ID` with your server project ID. 2. Redeploy your server application. 3. Update clients/service_to_service_google_id_token/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. -4. Upload your application to Google App Engine by invoking the following command under clients/service_to_service_google_id_token directory. -This opens a browser window for you to sign in using your Google account. You'll be providing the project ID as the argument for -A. Use -the -V argument to specify a version name. Additional information on how to deploy an app to Google Cloud App Engine can be found [here](https://cloud.google.com/appengine/docs/python/quickstart). +4. Upload your application to Google App Engine by invoking the following command. - appcfg.py -A -V v1 update . + gcloud app deploy app.yaml --project= --promote Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API from the client's service account using Google ID token. diff --git a/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py b/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py index 03552da1c306..5ff84986fa0d 100644 --- a/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py +++ b/appengine/flexible/endpoints/clients/service_to_service_gae_default/main.py @@ -49,11 +49,13 @@ def generate_jwt(): "email": DEFAUTL_SERVICE_ACCOUNT }) - headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), - base64.urlsafe_b64encode(payload_json)) + headerAndPayload = '{}.{}'.format( + base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) (key_name, signature) = app_identity.sign_blob(headerAndPayload) - signed_jwt = '{}.{}'.format(headerAndPayload, - base64.urlsafe_b64encode(signature)) + signed_jwt = '{}.{}'.format( + headerAndPayload, + base64.urlsafe_b64encode(signature)) return signed_jwt diff --git a/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py index cbf3924c0d93..fb22c5ebf4a4 100644 --- a/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py +++ b/appengine/flexible/endpoints/clients/service_to_service_google_id_token/main.py @@ -51,11 +51,13 @@ def generate_jwt(): "aud": "https://www.googleapis.com/oauth2/v4/token" }) - headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), - base64.urlsafe_b64encode(payload_json)) + headerAndPayload = '{}.{}'.format( + base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) (key_name, signature) = app_identity.sign_blob(headerAndPayload) - signed_jwt = '{}.{}'.format(headerAndPayload, - base64.urlsafe_b64encode(signature)) + signed_jwt = '{}.{}'.format( + headerAndPayload, + base64.urlsafe_b64encode(signature)) return signed_jwt diff --git a/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py b/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py index e9dbac7dee06..504d0d03d8f4 100644 --- a/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py +++ b/appengine/flexible/endpoints/clients/service_to_service_non_default/main.py @@ -57,8 +57,9 @@ def generate_jwt(): "email": SERVICE_ACCOUNT_EMAIL }) - headerAndPayload = '{}.{}'.format(base64.urlsafe_b64encode(header_json), - base64.urlsafe_b64encode(payload_json)) + headerAndPayload = '{}.{}'.format( + base64.urlsafe_b64encode(header_json), + base64.urlsafe_b64encode(payload_json)) slist = service.projects().serviceAccounts().signBlob( name=SERVICE_ACCOUNT, body={'bytesToSign': base64.b64encode(headerAndPayload)}) diff --git a/appengine/flexible/endpoints/requirements.txt b/appengine/flexible/endpoints/requirements.txt index 748bdbea6829..773eba479840 100644 --- a/appengine/flexible/endpoints/requirements.txt +++ b/appengine/flexible/endpoints/requirements.txt @@ -5,4 +5,4 @@ gcloud==0.17.0 six==1.10.0 pyyaml==3.11 requests==2.10.0 -google-api-python-client +google-api-python-client==1.5.1 From f6931dbff9eefd80fcd93d2b7be6fee865fbfcf5 Mon Sep 17 00:00:00 2001 From: Limin Wang Date: Tue, 2 Aug 2016 10:56:19 -0700 Subject: [PATCH 3/3] Service to Service auth sample. Updated README. --- appengine/flexible/endpoints/README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/appengine/flexible/endpoints/README.md b/appengine/flexible/endpoints/README.md index 6e7d1baa9da0..523687c12619 100644 --- a/appengine/flexible/endpoints/README.md +++ b/appengine/flexible/endpoints/README.md @@ -97,17 +97,16 @@ Now you can use the client ID to make requests to the API: ### Using the App Engine default service account client (no key file needed) The App Engine default service account client demonstrates how to use the Google App Engine default service account to authenticate to endpoints. -We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). -Both server and client projects are running Google App Engine standard applications. +We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). The client project is running Google App Engine standard application. To use the App Engine default service account for authentication: 1. Update the `gae_default_service_account`'s `x-issuer` and `x-jwks_uri` in `swagger.yaml` with your client project ID. 2. Redeploy your server application. 3. Update clients/service_to_service_gae_default/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. -4. Upload your application to Google App Engine by invoking the following command. +4. Upload your application to Google App Engine by invoking the following command. Note that you need to provide project ID in the command because there are two projects (server and client projects) here and gcloud needs to know which project to pick. - gcloud app deploy app.yaml --project= --promote + gcloud app deploy app.yaml --project= Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using the client's service account. @@ -116,7 +115,7 @@ the client's service account. The service account client demonstrates how to use a non-default service account to authenticate to endpoints. We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). -Both server and client projects are running Google App Engine standard applications. +The client project is running Google App Engine standard application. In the example, we use Google Cloud Identity and Access Management (IAM) API to create a JSON Web Token (JWT) for a service account, and use it to call an Endpoints API. @@ -134,9 +133,9 @@ To use the client for authentication: 2. Redeploy your server application. 3. Update clients/service_to_service_non_default/main.py, replace 'YOUR-SERVICE-ACCOUNT-EMAIL', 'YOUR-SERVER-PROJECT-ID' and 'YOUR-CLIENT-PROJECT-ID' with your service account email, your server project ID, and your client project ID. -4. Upload your application to Google App Engine by invoking the following command. +4. Upload your application to Google App Engine by invoking the following command. Note that you need to provide project ID in the command because there are two projects (server and client projects) here and gcloud needs to know which project to pick. - gcloud app deploy app.yaml --project= --promote + gcloud app deploy app.yaml --project= Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API using the client's service account. @@ -148,15 +147,15 @@ In the example, we first create a JSON Web Token (JWT) using the App Engine defa ID token using the JWT, and call an Endpoints API using the Google ID token. We refer to the project that serves API requests as the server project. You also need to create a client project in the [Cloud Console](https://console.cloud.google.com). -Both server and client projects are running Google App Engine standard applications. +The client project is running Google App Engine standard application. To use the client for authentication: 1. Update the `google_id_token`'s audiences, replace `YOUR-SERVER-PROJECT-ID` with your server project ID. 2. Redeploy your server application. 3. Update clients/service_to_service_google_id_token/main.py, replace 'YOUR-CLIENT-PROJECT-ID' and 'YOUR-SERVER-PROJECT-ID' with your client project ID and your server project ID. -4. Upload your application to Google App Engine by invoking the following command. +4. Upload your application to Google App Engine by invoking the following command. Note that you need to provide project ID in the command because there are two projects (server and client projects) here and gcloud needs to know which project to pick. - gcloud app deploy app.yaml --project= --promote + gcloud app deploy app.yaml --project= Your client app is now deployed at https://.appspot.com. When you access https://.appspot.com, your client calls your server project API from the client's service account using Google ID token.