Along with authenticating users, you might need to allow other services to interact with your API. While client applications can provide users with a web sign-in prompt to submit their credentials, you need another approach for secure service-to-service communication. This page shows the approach that we recommend to implement authentication between services and provides sample code.
Overview
To identify a service that sends requests to your API, you use a service account. The calling service uses the service account's private key to sign a secure JSON Web Token (JWT) and sends the signed JWT in the request to your API.
To implement service-to-service authentication in your API and calling service:
- Create a service account and key for the calling service to use.
- Add support for authentication in the OpenAPI document for your Cloud Endpoints service.
Add code to the calling service that:
- Creates a JWT and signs it with the service account's private key.
- Sends the signed JWT in a request to the API.
ESP validates that the claims in the JWT match the configuration in your OpenAPI document before forwarding the request to your API. ESP doesn't check for Cloud Identity permissions that you have granted on the service account.
Prerequisites
This page assumes that you have already:
Creating a service account with a key
You need a service account with a private key file that the calling service uses to sign the JWT. If you have more than one service sending requests to your API, you can create one service account to represent all the calling services. If you need to differentiate between the services—for example, they might have different permissions—you can create a service account and key for each calling service.
This section shows how to use the Google Cloud console and the gcloud
command-line tool to create the service account and private key file and to
assign the service account the
Service Account Token Creator
role. For information on using an API to do this task, see
Creating and managing service accounts.
To create a service account and key:
Google Cloud console
Create a service account:
In the Google Cloud console, go to the Create service account page.
Select the project that you want to use.
In the Service account name field, enter a name.
Optional: In the Service account description field, enter a description.
Click Create.
Click the Select a role field. Under All roles, select Service Account > Service Account Token Creator.
Click Done.
Do not close your browser window. You will use it in the next step.
Create a service account key:
- In the Google Cloud console, click the email address for the service account that you created.
- Click Keys.
- Click Add key, then Create new key.
- Click Create. A JSON file that contains the service account's private key downloads to your computer.
- Click Close.
gcloud
You can run the following commands by using the Google Cloud CLI on your local machine, or within Cloud Shell.
Set the default account for
gcloud
. If you have more than one account, make sure to choose the account that is in the Google Cloud project that you want to use.gcloud auth login
Display the project IDs for your Google Cloud projects.
gcloud projects list
Set the default project. Replace
PROJECT_ID
with the Google Cloud project ID that you want to use.gcloud config set project PROJECT_ID
Create a service account. Replace
SA_NAME
andSA_DISPLAY_NAME
with the name and display name that you want to use.gcloud iam service-accounts create SA_NAME \ --display-name "SA_DISPLAY_NAME"
Display the email address for the service account that you just created.
gcloud iam service-accounts list
Add the Service Account Token Creator role. Replace
SA_EMAIL_ADDRESS
with the service account's email address.gcloud projects add-iam-policy-binding PROJECT_ID \ --member serviceAccount:SA_EMAIL_ADDRESS \ --role roles/iam.serviceAccountTokenCreator
Create a service account key file in the current working directory. Replace
FILE_NAME
with the name that you want to use for key file. By default, thegcloud
command creates a JSON file.gcloud iam service-accounts keys create FILE_NAME.json \ --iam-account SA_EMAIL_ADDRESS
See the
gcloud
reference
for more information about the previous commands.
For information on safeguarding the private key, see Best practices for managing credentials.
Configuring your API to support authentication
You must have a security requirement object and a security definitions object in your OpenAPI document for ESP to validate the claims in the signed JWT.
Add the service account as an issuer in your OpenAPI document.
securityDefinitions: DEFINITION_NAME: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "SA_EMAIL_ADDRESS" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS"
- Replace
DEFINITION_NAME
with a string that identifies this security definition. You might want to replace it with the service account name or a name that identifies the calling service. - Replace
SA_EMAIL_ADDRESS
with the service account's email address. - You can define multiple security definitions in your OpenAPI document, but
each definition must have a different
x-google-issuer
. If you have created separate service accounts for each calling service, you can create a security definition for each service account, for example:
securityDefinitions: service-1: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-1@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-1@example-project-12345.iam.gserviceaccount.com" service-2: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-2@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-2@example-project-12345.iam.gserviceaccount.com"
- Replace
Optionally, add
x-google-audiences
to thesecurityDefinitions
section. If you don't addx-google-audiences
, ESP requires that the"aud"
(audience) claim in the JWT is in the formathttps://SERVICE_NAME
, where SERVICE_NAME is the name of your Endpoints service, which you have configured in thehost
field of your OpenAPI document unless the flag--disable_jwt_audience_service_name_check
is used. If the flag is used andx-google-audiences
is not specified, JWTaud
field is not checked.Optionally, add
x-google-jwt-locations
to thesecurityDefinitions
section. You can use this value to define a custom JWT location. The default JWT locations are theAuthorization
header (prefixed by "Bearer "), theX-Goog-Iap-Jwt-Assertion
header, or theaccess_token
query parameter. Note:- If you specify the
x-google-jwt-locations
, Endpoints then ignores all default locations. x-google-jwt-locations
is only supported by ESPv2.
- If you specify the
Add a
security
section at either the top level of the file (not indented or nested) to apply to the entire API, or at the method level to apply to a specific method. If you usesecurity
sections at both the API level and at the method level, the method-level settings override the API-level settings.security: - DEFINITION_NAME: []
- Replace
DEFINITION_NAME
with the name that you used in thesecurityDefinitions
section. If you have more than one definition in the
securityDefinitions
section, add them in thesecurity
section, for example:security: - service-1: [] - service-2: []
- Replace
Deploy your updated OpenAPI document. Replace
OPENAPI_DOC
with the name of your OpenAPI document.gcloud endpoints services deploy OPENAPI_DOC
Before ESP forwards a request to your API, ESP verifies:
- The signature of the JWT by using the public key, which is located at the URI
specified in the
x-google-jwks_uri
field in your OpenAPI document. - That the
"iss"
(issuer) claim in the JWT matches the value specified in thex-google-issuer
field. - That the
"aud"
(audience) claim in the JWT contains your Endpoints service name or matches one of the values that you specified in thex-google-audiences
field. - That the token isn't expired by using the
"exp"
(expiration time) claim.
For more information about x-google-issuer
, x-google-jwks_uri
,
x-google-audiences
and x-google-jwt-locations
, see OpenAPI extensions.
Making an authenticated request to an Endpoints API
To make an authenticated request, the calling service sends a JWT signed by the service account that you specified in the OpenAPI document. The calling service must:
- Create a JWT and sign it with the service account's private key.
- Send the signed JWT in a request to the API.
The following sample code demonstrates this process for select languages. To make an authenticated request in other languages, reference jwt.io for a list of supported libraries.
-
In the calling service, add the following function and pass it the following
parameters:
Java -
saKeyfile
: The full path to the service account's private key file. -
saEmail
: The service account's email address. -
audience
: If you added thex-google-audiences
field to your OpenAPI document, setaudience
to one of the values that you specified forx-google-audiences
. Otherwise, setaudience
tohttps://SERVICE_NAME
, whereSERVICE_NAME
is your Endpoints service name. -
expiryLength
: The JWT expiration time, in seconds.
Python -
sa_keyfile
: The full path to the service account's private key file. -
sa_email
: The service account's email address. -
audience
: If you added thex-google-audiences
field to your OpenAPI document, setaudience
to one of the values that you specified forx-google-audiences
. Otherwise, setaudience
tohttps://SERVICE_NAME
, whereSERVICE_NAME
is your Endpoints service name. -
expiry_length
: The JWT expiration time, in seconds.
Go -
saKeyfile
: The full path to the service account's private key file. -
saEmail
: The service account's email address. -
audience
: If you added thex-google-audiences
field to your OpenAPI document, setaudience
to one of the values that you specified forx-google-audiences
. Otherwise, setaudience
tohttps://SERVICE_NAME
, whereSERVICE_NAME
is your Endpoints service name. -
expiryLength
: The JWT expiration time, in seconds.
The function creates a JWT, signs it by using the private key file, and returns the signed JWT.
Java Python Go -
-
In the calling service, add the following function to send the signed JWT
in the
Authorization: Bearer
header in the request to the API:Java Python Go
When you send a request by using a JWT, for security reasons, we
recommend that you put the authentication token in the Authorization: Bearer
header. For example:
curl --request POST \ --header "Authorization: Bearer ${TOKEN}" \ "${ENDPOINTS_HOST}/echo"
where ENDPOINTS_HOST
and TOKEN
are environment variables containing your
API host name and authentication token, respectively.
Receiving authenticated results in your API
ESP usually forwards all headers it receives. However, it overrides the
original Authorization
header when the backend address is specified by
x-google-backend
in OpenAPI specification or BackendRule
in gRPC service configuration.
ESP will send the authentication result in the X-Endpoint-API-UserInfo
to the backend API. We recommend using this header instead of the original
Authorization
header. This header is a string that base64url
encodes
a JSON object. The JSON object format differs between ESPv2 and ESP.
For ESPv2, the JSON object is exactly the original JWT payload. For ESP,
the JSON object uses different field names and put original JWT payload under claims
field.
See Handle JWTs in the backend service
for more information on the format.