Autentica usuarios

La autenticación le permite al proxy de servicio extensible (ESP) identificar a los usuarios que llaman a los métodos del servicio y, luego, basándose en esto, decidir si les permite usar ese método (autorización). En esta página, se describe cómo funciona la autenticación con los servicios de Cloud Endpoints para gRPC, incluso la forma de configurar el ESP en un servicio de gRPC para admitir solicitudes autenticadas y cómo llamar a métodos autenticados desde un cliente gRPC.

El ESP admite varios métodos de autenticación, entre ellos, Firebase, Auth0, y tokens de ID de Google, los cuales pueden configurarse como parte de la configuración de la API de gRPC. En cada caso, el cliente debe proporcionar un token web JSON (JWT) de identificación en sus solicitudes. El ESP valida el token en nombre de la API, por lo que no necesitas agregar ningún código de autenticación especial.

Si bien los requisitos de autenticación y de la clave de API te permiten restringir quién puede llamar a los métodos de tu servicio, no proporcionan el mismo nivel de seguridad y envían información diferente al servicio llamado. Puedes encontrar más información sobre las diferencias entre claves de API y la autenticación y el momento apropiado para usar cada esquema en Cuándo y por qué usar claves de API.

Para ver un ejemplo funcional completo de uso de la autenticación, consulta Cómo autenticar con una cuenta de servicio, que agrega autenticación al servicio de librería de nuestros Instructivos.

Cómo configurar la autenticación para el ESP

Puedes configurar la autenticación para un servicio de Endpoints para gRPC en su archivo YAML de configuración de servicio de gRPC con la sección authentication. Debe especificar el método de autenticación y los detalles de la fuente de autenticación, como providers, en los que se muestra lo siguiente:

  • El valor id se usa para identificar el proveedor de autenticación cuando se usa en rules: por lo general, usa el nombre del método de autenticación, pero no es necesario.

  • El valor issuer es el emisor de los tokens necesarios y, por lo tanto, especifica el método de autenticación.

  • El valor jwks_uri es el URI de la clave pública del proveedor, que se usa para validar los tokens. Algunos métodos de autenticación no necesitan que especifiques esto, como los tokens de ID de Google, caso en el cual el ESP obtiene la información de forma automática.

Puedes definir varios proveedores de seguridad en el mismo archivo, pero cada uno debe tener un issuer diferente. Consulta AuthProvider para obtener más información.

Especifica los métodos de API que deseas que usen estos requisitos de autenticación mediante rules, como se describe en AuthenticationRule.

Los ejemplos siguientes muestran cómo configurar el ESP en un servicio de gRPC para algunos métodos de autenticación admitidos:

firebase

Para que admita la autenticación de Firebase:

authentication:
      providers:
      - id: firebase
        jwks_uri: https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com
        # Replace YOUR-PROJECT-ID with your project ID
        issuer: https://securetoken.google.com/YOUR-PROJECT-ID
        # Optional. Replace YOUR-CLIENT-ID with your client ID
        audiences: "YOUR-CLIENT-ID"
      rules:
      - selector: "*"
        requirements:
          - provider_id: firebase
    

Auth0

Para que admita la autenticación de Auth0:

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
    

Token de ID de Google

Para que admita la autenticación con un token de ID de Google:

authentication:
      providers:
      - id: google_id_token
        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
    

De forma personalizada

Para que admita la autenticación personalizada:

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
    

El campo audiences no es obligatorio. El ESP acepta todos los JWT con el nombre del servicio de back-end en forma de https://SERVICE_NAME en la reclamación de aud. Para incluir en la lista blanca los ID de cliente adicionales a fin de acceder al servicio de backend, puedes especificar los ID de cliente permitidos en el campo audiences mediante valores separados por comas. A continuación, el ESP acepta los JWT con los ID de cliente incluidos en la lista blanca en el reclamo aud.

Cómo llamar un método autenticado de gRPC

Si un método requiere autenticación, los clientes de gRPC deben pasar el token de autenticación como metadatos con su llamada de método, donde la clave es authorization y el valor es Bearer <JWT_TOKEN>. Ve un ejemplo sobre cómo hacer esto cuando se llama el ejemplo de librería en Python, Node.js o Java:

Python

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

        if use_tls:
            with open('../roots.pem', 'rb') as f:
                creds = grpc.ssl_channel_credentials(f.read())
            channel = grpc.secure_channel('{}:{}'.format(host, port), creds)
        else:
            channel = grpc.insecure_channel('{}:{}'.format(host, port))
    
        stub = bookstore_pb2_grpc.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;
      }
    }

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

La forma en que el cliente obtiene un JWT válido para enviar dependerá del método de autenticación.

Cómo recibir resultados de autenticación en tu API

Por lo general, el ESP reenvía todos los encabezados que recibe. Sin embargo, anula el encabezado Authorization original cuando x-google-backend especifica la dirección de backend en la especificación de OpenAPI o BackendRule en la configuración del servicio de gRPC.

El ESP enviará el resultado de la autenticación en X-Endpoint-API-UserInfo a la API de backend. Se recomienda usar este encabezado en lugar del encabezado original de Authorization. Este encabezado está codificado en base64url y contiene el objeto JSON siguiente:

    {
      "id": "from-sub",
      "issuer": "from-iss",
      "email": "from-email",
      "audiences": ["from-aud"],
      "claims": {
         original-jwt-payload
       }
    }
    

Si usas ESPv2 Beta, el formato del valor en el encabezado es diferente. Consulta Migrar al proxy de servicio extensible V2 Beta para obtener más información sobre el nuevo formato.

Qué sigue