Restricting API Access with API Keys

You can use API keys to restrict access to specific API methods or all methods in an API. This page describes how to restrict API access to those clients that have an API key and also shows how to create an API key.

If you set an API key requirement in your API, requests to the protected method, class, or API are rejected unless they have a key generated in your project or within other projects belonging to developers with whom you have granted access to enable your API. For more information, see Sharing APIs protected by API key.

Restricting or granting access to all API methods

By default in gRPC services, all API methods require an API key to access them.

To specify that an API key is not required to access your API:

  1. Open your project's gRPC API Configuration file in a text editor and find or add a usage section:

  2. In your usage section, specify an allow_unregistered_calls rule as follows. The wildcard "*" in the selector means that the rule applies to all methods in the API. You can find out more about selectors in Configuring a gRPC API Service.

    usage:
      rules:
      # All methods can be called without an API Key.
      - selector: "*"
        allow_unregistered_calls: true
    

Removing API key restriction for a method

To turn off API key validation for a particular method even when you've restricted API access for the API:

  1. Open your project's gRPC API Configuration file in a text editor and find or add a usage section:

  2. In your usage section, specify an allow_unregistered_calls rule as follows. The selector means that the rule applies just to the specified method - in this case, ListShelves. You can find out more about selectors in Configuring a gRPC API Service.

    usage:
      rules:
      # ListShelves method can be called without an API Key.
      - selector: endpoints.examples.bookstore.Bookstore.ListShelves
        allow_unregistered_calls: true
    

Calling an API using an API Key

Calling an API varies depending on whether you call from a gRPC client or an HTTP client.

gRPC clients

If a method requires an API key, gRPC clients need to pass the key value as x-api-key metadata with their method call. Select a tab below to see an example of how to do this when calling the Bookstore in Python, Java, Go, or Node.js:

Python

def run(host, port, api_key, auth_token, timeout):
    """Makes a basic ListShelves call against a gRPC Bookstore server."""

    channel = grpc.insecure_channel('{}:{}'.format(host, port))

    stub = bookstore_pb2.BookstoreStub(channel)
    metadata = []
    if api_key:
        metadata.append(('x-api-key', api_key))
    if auth_token:
        metadata.append(('authorization', 'Bearer ' + auth_token))
    shelves = stub.ListShelves(empty_pb2.Empty(), timeout, metadata=metadata)
    print('ListShelves: {}'.format(shelves))

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;
  }
}

Go

func main() {
	flag.Parse()

	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	ctx := context.Background()
	ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("x-api-key", *key))

	// Contact the server and print out its response.
	name := defaultName
	if len(flag.Args()) > 0 {
		name = flag.Arg(0)
	}
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.Message)
}

Node.js

function 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');
  const path = require('path');

  // Load protobuf spec for an example API
  const PROTO_PATH = path.join(__dirname, '/protos/helloworld.proto');
  const protoObj = grpc.load(PROTO_PATH).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);
    }
  });
}

HTTP clients

If you are using gRPC for Endpoints' HTTP transcoding feature, HTTP clients can send the key as a query parameter in the same way they do for OpenAPI services.

Sharing APIs protected by API key

API keys are associated with the Google Cloud Platform (GCP) project in which they have been created. If you have decided to require an API key for your API, the GCP project that the API key gets created in depends on the answers to the following questions:

  • Do you need to distinguish between the callers of your API so that you can use Cloud Endpoints features such as quotas?
  • Do all the callers of your API have their own GCP projects?
  • Do you need to set up different API key restrictions?

You can use the following decision tree as a guide for deciding which GCP project the API key should be created in. After you have made a decision, see the sections below for more details.

API key decision tree

Grant permission to enable the API

When you need to distinguish between callers of your API, and each caller has their own GCP project, you can grant users who are members of the calling projects permission to enable the API in their own GCP project. This way, users of your API can create their own API key for use with your API.

For example, suppose your team has created an API for internal use by various client programs in your company, and each client program has their own GCP project. To distinguish between callers of your API, the API key for each caller must be created in a different GCP project. You can grant your coworkers permission to enable the API in the GCP project that the client program is associated with.

To let users create their own API key:

  1. In the GCP project in which your API is configured, grant each user the permission to enable your API.
  2. Contact the users, and let them know that they can enable your API in their own GCP project and create an API key.

Create a separate GCP project for each caller

When you need to distinguish between callers of your API, and not all of the callers have GCP projects, you can create a separate GCP project and API key for each caller. Before creating the projects, give some thought to the project names so that you can easily identify the caller associated with the project.

For example, suppose you have external customers of your API, and you have no idea how the client programs that call your API were created. Perhaps some of the clients use GCP services and have a GCP project, and perhaps some do not. To distinguish between the callers, you must create a separate GCP project and API key for each caller.

To create a separate GCP project and API key for each caller:

  1. Create a separate project for each caller.
  2. In each project, enable your API and create an API key.
  3. Give the API key to each caller.

Create an API key for each caller

When you do not need to distinguish between callers of your API, but you want to add API key restrictions, you can create a separate API key for each caller in the same project.

To create an API key for each caller in the same project:

  1. In either the project that your API is configured in, or a project that your API is enabled in, create an API key for each customer that has the API key restrictions that you need.
  2. Give the API key to each caller.

Create one API key for all callers

When you do not need to distinguish between callers of your API, and you do not need to add API restrictions, but you still want to require an API key (to prevent anonymous access, for example), you can create one API key for all callers to use.

To create one API key for all callers:
  1. In either the project that your API is configured in, or a project that your API is enabled in, create an API key for all caller.
  2. Give the same API key to every caller.

Further reading

Send feedback about...

Cloud Endpoints with gRPC