Authenticating users

Authentication lets the Extensible Service Proxy (ESP) identify the users calling your service's methods and then, based on this, decide whether to let them use that method (authorization). This page describes how authentication works with Cloud Endpoints for gRPC services, including how to configure ESP in a gRPC service to support authenticated requests, and how to call authenticated methods from a gRPC client.

ESP supports multiple authentication methods, including Firebase , Auth0 , and Google ID tokens , all of which can be set up as part of your gRPC API Configuration. In each case the client needs to provide an identifying JSON web token (JWT) in their requests. The ESP validates the token on behalf of your API, so you don't need to add any special authentication code yourself.

While both authentication and API key requirements let you restrict who can call your service's methods, they don't provide the same level of security, and provide different information to the called service. You can find out more about the differences between API keys and authentication, and when it's appropriate to use each scheme, in When and why to use API keys .

For a complete working example using authentication, see Authenticating using a service account , which adds authentication to the Bookstore service from our Tutorials .

Configuring authentication for ESP

You configure authentication for a Endpoints for gRPC service in its gRPC service configuration YAML file, by using the authentication section. You specify the authentication method and details of the authentication source as providers , where:

  • The id value is used to identify the auth provider when used in rules : this typically uses the name of the authentication method, but it doesn't have to.

  • The issuer value is the issuer of the required tokens, and hence specifies the authentication method.

  • The jwks_uri value is the URI of the provider's public key, used to validate tokens. Some authentication methods don't require you to specify this, such as Google ID tokens where the ESP obtains the information automatically.

  • The jwt_locations is used to define the locations to extract the JWT.

You can define multiple security providers in the same file, but each one must have a different issuer . See AuthProvider for more information.

You specify the API methods that you want to use these authentication requirements using rules , as described in AuthenticationRule .

The following examples show how to set up ESP in a gRPC service for some supported authentication methods:

firebase

To support Firebase authentication:

  authentication 
 : 
  
 providers 
 : 
  
 - 
  
 id 
 : 
  
 firebase 
  
 jwks_uri 
 : 
  
 https 
 :// 
 www 
 . 
 googleapis 
 . 
 com 
 /service_accounts/v1/metadata/x509/s 
 ecuretoken 
 @ 
system.gserviceaccount.com  
 # 
  
 Replace 
  
 FIREBASE 
 - 
 PROJECT 
 - 
 ID 
  
 with 
  
 your 
  
 Firebase 
  
 project 
  
 ID 
  
 issuer 
 : 
  
 https 
 :// 
 securetoken 
 . 
 google 
 . 
 com 
 / 
 FIREBASE 
 - 
 PROJECT 
 - 
 ID 
  
 audiences 
 : 
  
 "FIREBASE-PROJECT-ID" 
  
 # 
  
 Optional 
 . 
  
 jwt_locations 
 : 
  
 # 
  
 expect 
  
 header 
  
 "jwt-header-foo" 
 : 
  
 "jwt-prefix-foo<TOKEN>" 
  
 - 
  
 header 
 : 
  
 "jwt-header-foo" 
  
 value_prefix 
 : 
  
 "jwt-prefix-foo" 
  
 - 
  
 query 
 : 
  
 "jwt_query_bar" 
  
 rules 
 : 
  
 - 
  
 selector 
 : 
  
 "*" 
  
 requirements 
 : 
  
 - 
  
 provider_id 
 : 
  
 firebase 
 

auth0

To support Auth0 authentication:

  authentication 
 : 
  
 providers 
 : 
  
 - 
  
 id 
 : 
  
 auth0_jwk 
  
 # 
  
 Replace 
  
 YOUR 
 - 
 ACCOUNT 
 - 
 NAME 
  
 with 
  
 your 
  
 service 
  
 account 
 ' 
 s 
  
 email 
  
 address 
 . 
  
 issuer 
 : 
  
 https 
 :// 
 YOUR 
 - 
 ACCOUNT 
 - 
 NAME 
 . 
 auth0 
 . 
 com 
 / 
  
 jwks_uri 
 : 
  
 "https://YOUR-ACCOUNT-NAME.auth0.com/.well-known/jwks.json" 
  
 # 
  
 Optional 
 . 
  
 Replace 
  
 YOUR 
 - 
 CLIENT 
 - 
 ID 
  
 with 
  
 your 
  
 client 
  
 ID 
  
 audiences 
 : 
  
 "YOUR-CLIENT-ID" 
  
 rules 
 : 
  
 - 
  
 selector 
 : 
  
 "*" 
  
 requirements 
 : 
  
 - 
  
 provider_id 
 : 
  
 auth0_jwk 
 

Google ID Token

To support authentication using a Google ID token:

  authentication 
 : 
  
 providers 
 : 
  
 - 
  
 id 
 : 
  
 google_id_token 
  
 # 
  
 This 
  
 "issuer" 
  
 field 
  
 has 
  
 to 
  
 match 
  
 the 
  
 field 
  
 "iss" 
  
 in 
  
 the 
  
 JWT 
  
 token 
 . 
  
 # 
  
 Sometime 
  
 it 
  
 is 
  
 "accounts.google.com" 
 . 
  
 issuer 
 : 
  
 https 
 :// 
 accounts 
 . 
 google 
 . 
 com 
  
 # 
  
 Optional 
 . 
  
 Replace 
  
 YOUR 
 - 
 CLIENT 
 - 
 ID 
  
 with 
  
 your 
  
 client 
  
 ID 
  
 audiences 
 : 
  
 "YOUR-CLIENT-ID" 
  
 rules 
 : 
  
 - 
  
 selector 
 : 
  
 "*" 
  
 requirements 
 : 
  
 - 
  
 provider_id 
 : 
  
 google_id_token 
 

custom

To support custom authentication:

  authentication 
 : 
  
 providers 
 : 
  
 - 
  
 id 
 : 
  
 custom_auth_id 
  
 # 
  
 The 
  
 value 
  
 below 
  
 should 
  
 be 
  
 unique 
  
 issuer 
 : 
  
 issuer 
  
 of 
  
 the 
  
 token 
  
 jwks_uri 
 : 
  
 url 
  
 to 
  
 the 
  
 public 
  
 key 
  
 # 
  
 Optional 
 . 
  
 Replace 
  
 YOUR 
 - 
 CLIENT 
 - 
 ID 
  
 with 
  
 your 
  
 client 
  
 ID 
  
 audiences 
 : 
  
 "YOUR-CLIENT-ID" 
  
 rules 
 : 
  
 - 
  
 selector 
 : 
  
 "*" 
  
 requirements 
 : 
  
 - 
  
 provider_id 
 : 
  
 custom_auth_id 
 

For Firebase authentication, the audiences field is required and must be your Firebase project ID. For all other authentication methods, it is optional. If not specified, ESP accepts all JWTs with the back-end service name in the form of https://SERVICE_NAME in the aud claim. To permit additional client IDs to access the back-end service, you can specify the allowed client IDs in the audiences field by using comma-separated values. ESP then accepts the JWTs with the whitelisted client IDs in the aud claim.

Calling an authenticated method from gRPC

If a method requires authentication, gRPC clients need to pass the authentication token as metadata with their method call, where the key is authorization and the value is Bearer <JWT_TOKEN> . See an example of how to do this when calling the Bookstore example in Python, Node.js, or Java:

Python

  def 
  
 run 
 ( 
 host 
 , 
 port 
 , 
 api_key 
 , 
 auth_token 
 , 
 timeout 
 , 
 use_tls 
 , 
 servername_override 
 , 
 ca_path 
 

Java

  private 
  
 static 
  
 final 
  
 class 
 Interceptor 
  
 implements 
  
 ClientInterceptor 
  
 { 
  
 private 
  
 final 
  
 String 
  
 apiKey 
 ; 
  
 private 
  
 final 
  
 String 
  
 authToken 
 ; 
  
 private 
  
 static 
  
 Logger 
  
 LOGGER 
  
 = 
  
 Logger 
 . 
 getLogger 
 ( 
 "InfoLogging" 
 ); 
  
 private 
  
 static 
  
 Metadata 
 . 
 Key<String> 
  
 API_KEY_HEADER 
  
 = 
  
 Metadata 
 . 
 Key 
 . 
 of 
 ( 
 "x-api-key" 
 , 
  
 Metadata 
 . 
 ASCII_STRING_MARSHALLER 
 ); 
  
 private 
  
 static 
  
 Metadata 
 . 
 Key<String> 
  
 AUTHORIZATION_HEADER 
  
 = 
  
 Metadata 
 . 
 Key 
 . 
 of 
 ( 
 "authorization" 
 , 
  
 Metadata 
 . 
 ASCII_STRING_MARSHALLER 
 ); 
  
 public 
  
 Interceptor 
 ( 
 String 
  
 apiKey 
 , 
  
 String 
  
 authToken 
 ) 
  
 { 
  
 this 
 . 
 apiKey 
  
 = 
  
 apiKey 
 ; 
  
 this 
 . 
 authToken 
  
 = 
  
 authToken 
 ; 
  
 } 
  
 @Override 
  
 public 
  
< ReqT 
 , 
  
 RespT 
>  
 ClientCall<ReqT 
 , 
  
 RespT 
>  
 interceptCall 
 ( 
  
 MethodDescriptor<ReqT 
 , 
 RespT 
>  
 method 
 , 
  
 CallOptions 
  
 callOptions 
 , 
  
 Channel 
  
 next 
 ) 
  
 { 
  
 LOGGER 
 . 
 info 
 ( 
 "Intercepted " 
  
 + 
  
 method 
 . 
 getFullMethodName 
 ()); 
  
 ClientCall<ReqT 
 , 
  
 RespT 
>  
 call 
  
 = 
  
 next 
 . 
 newCall 
 ( 
 method 
 , 
  
 callOptions 
 ); 
  
 call 
  
 = 
  
 new 
  
 ForwardingClientCall 
 . 
 SimpleForwardingClientCall<ReqT 
 , 
  
 RespT 
> ( 
 call 
 ) 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 start 
 ( 
 Listener<RespT> 
  
 responseListener 
 , 
  
 Metadata 
  
 headers 
 ) 
  
 { 
  
 if 
  
 ( 
 apiKey 
  
 != 
  
 null 
 && 
 ! 
 apiKey 
 . 
 isEmpty 
 ()) 
  
 { 
  
 LOGGER 
 . 
 info 
 ( 
 "Attaching API Key: " 
  
 + 
  
 apiKey 
 ); 
  
 headers 
 . 
 put 
 ( 
 API_KEY_HEADER 
 , 
  
 apiKey 
 ); 
  
 } 
  
 if 
  
 ( 
 authToken 
  
 != 
  
 null 
 && 
 ! 
 authToken 
 . 
 isEmpty 
 ()) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Attaching auth token" 
 ); 
  
 headers 
 . 
 put 
 ( 
 AUTHORIZATION_HEADER 
 , 
  
 "Bearer " 
  
 + 
  
 authToken 
 ); 
  
 } 
  
 super 
 . 
 start 
 ( 
 responseListener 
 , 
  
 headers 
 ); 
  
 } 
  
 }; 
  
 return 
  
 call 
 ; 
  
 } 
 } 
 

Node.js

  const 
  
 makeGrpcRequest 
  
 = 
  
 ( 
 JWT_AUTH_TOKEN 
 , 
  
 API_KEY 
 , 
  
 HOST 
 , 
  
 GREETEE 
 ) 
  
 = 
>  
 { 
  
 // Uncomment these lines to set their values 
  
 // const JWT_AUTH_TOKEN = 'YOUR_JWT_AUTH_TOKEN'; 
  
 // const API_KEY = 'YOUR_API_KEY'; 
  
 // const HOST = 'localhost:50051'; // The IP address of your endpoints host 
  
 // const GREETEE = 'world'; 
  
 // Import required libraries 
  
 const 
  
 grpc 
  
 = 
  
 require 
 ( 
 '@grpc/grpc-js' 
 ); 
  
 const 
  
 protoLoader 
  
 = 
  
 require 
 ( 
 '@grpc/proto-loader' 
 ); 
  
 const 
  
 path 
  
 = 
  
 require 
 ( 
 'path' 
 ); 
  
 // Load protobuf spec for an example API 
  
 const 
  
 PROTO_PATH 
  
 = 
  
 path 
 . 
 join 
 ( 
 __dirname 
 , 
  
 '/protos/helloworld.proto' 
 ); 
  
 const 
  
 packageDefinition 
  
 = 
  
 protoLoader 
 . 
 loadSync 
 ( 
 PROTO_PATH 
 , 
  
 { 
  
 keepCase 
 : 
  
 true 
 , 
  
 longs 
 : 
  
 String 
 , 
  
 enums 
 : 
  
 String 
 , 
  
 defaults 
 : 
  
 true 
 , 
  
 oneofs 
 : 
  
 true 
 , 
  
 }); 
  
 const 
  
 protoObj 
  
 = 
  
 grpc 
 . 
 loadPackageDefinition 
 ( 
 packageDefinition 
 ). 
 helloworld 
 ; 
  
 // Create a client for the protobuf spec 
  
 const 
  
 client 
  
 = 
  
 new 
  
 protoObj 
 . 
 Greeter 
 ( 
 HOST 
 , 
  
 grpc 
 . 
 credentials 
 . 
 createInsecure 
 ()); 
  
 // Build gRPC request 
  
 const 
  
 metadata 
  
 = 
  
 new 
  
 grpc 
 . 
 Metadata 
 (); 
  
 if 
  
 ( 
 API_KEY 
 ) 
  
 { 
  
 metadata 
 . 
 add 
 ( 
 'x-api-key' 
 , 
  
 API_KEY 
 ); 
  
 } 
  
 else 
  
 if 
  
 ( 
 JWT_AUTH_TOKEN 
 ) 
  
 { 
  
 metadata 
 . 
 add 
 ( 
 'authorization' 
 , 
  
 `Bearer 
 ${ 
 JWT_AUTH_TOKEN 
 } 
 ` 
 ); 
  
 } 
  
 // Execute gRPC request 
  
 client 
 . 
 sayHello 
 ({ 
 name 
 : 
  
 GREETEE 
 }, 
  
 metadata 
 , 
  
 ( 
 err 
 , 
  
 response 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 err 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 err 
 ); 
  
 } 
  
 if 
  
 ( 
 response 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 response 
 . 
 message 
 ); 
  
 } 
  
 }); 
 }; 
 

How the client gets a valid JWT to send depends on the authentication method.

Receiving authentication 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.

What's next

Create a Mobile Website
View Site in Mobile | Classic
Share by: