Autenticazione degli utenti

L'autenticazione consente a Extensible Service Proxy (ESP) di identificare gli utenti che chiamano i metodi del tuo servizio e, in base a questo, decidere se consentire loro di utilizzare quel metodo (autorizzazione). Questa pagina descrive come funziona l'autenticazione con Cloud Endpoints per i servizi gRPC, incluso come configurare ESP in un servizio gRPC per supportare le richieste autenticate e come chiamare i metodi autenticati da un client gRPC.

ESP supporta più metodi di autenticazione, tra cui Firebase, Auth0, e token ID Google, che possono essere configurati nell'ambito della configurazione dell'API gRPC. In ogni caso, il client deve fornire un token web JSON (JWT) identificativo nelle sue richieste. L'ESP convalida il token per conto della tua API, quindi non devi aggiungere alcun codice di autenticazione speciale.

Sebbene i requisiti di autenticazione e della chiave API ti consentano di limitare chi può chiamare i metodi del tuo servizio, non forniscono lo stesso livello di sicurezza e forniscono informazioni diverse al servizio chiamato. Puoi scoprire di più sulle differenze tra chiavi API e autenticazione e quando è opportuno utilizzare ciascun schema in Perché e quando utilizzare le chiavi API.

Per un esempio di funzionamento completo che utilizza l'autenticazione, vedi Autenticazione tramite un service account, che aggiunge l'autenticazione al servizio Bookstore dei nostri tutorial.

Configurazione dell'autenticazione per ESP

Configuri l'autenticazione per un servizio Endpoints for gRPC nel relativo file YAML di configurazione del servizio gRPC utilizzando la sezione authentication. Specifica il metodo di autenticazione e i dettagli dell'origine dell'autenticazione come providers, dove:

  • Il valore id viene utilizzato per identificare il fornitore di autenticazione quando viene utilizzato in rules: in genere viene utilizzato il nome del metodo di autenticazione, ma non è obbligatorio.

  • Il valore issuer è l'emittente dei token richiesti e specifica quindi il metodo di autenticazione.

  • Il valore jwks_uri è l'URI della chiave pubblica del fornitore, utilizzato per convalidare i token. Alcuni metodi di autenticazione non richiedono di specificare questo valore, ad esempio i token ID Google, in cui il fornitore di servizi email ottiene le informazioni automaticamente.

  • jwt_locations viene utilizzato per definire le posizioni da cui estrarre il JWT.

Puoi definire più fornitori di sicurezza nello stesso file, ma ognuno deve avere un issuer diverso. Per saperne di più, consulta AuthProvider.

Specifichi i metodi API per i quali vuoi utilizzare questi requisiti di autenticazione utilizzando rules, come descritto in AuthenticationRule.

Gli esempi seguenti mostrano come configurare ESP in un servizio gRPC per alcuni metodi di autenticazione supportati:

firebase

Per supportare Firebase Authentication:

authentication:
  providers:
  - id: firebase
    jwks_uri: https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@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

Per supportare l'autenticazione 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 ID Google

Per supportare l'autenticazione utilizzando un token ID Google:

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

personalizzati

Per supportare l'autenticazione personalizzata:

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

Per Firebase Authentication, il campo audiences è obbligatorio e deve contenere l'ID progetto Firebase. Per tutti gli altri metodi di autenticazione, è facoltativo. Se non specificato, ESP accetta tutti i JWT con il nome del servizio di backend nel formato https://SERVICE_NAME nella rivendicazione aud. Per consentire a ID client aggiuntivi di accedere al servizio di backend, puoi specificare gli ID client consentiti nel campo audiences utilizzando valori separati da virgole. ESP accetta quindi i JWT con gli ID client inclusi nella lista consentita nella rivendicazione aud.

Chiamare un metodo autenticato da gRPC

Se un metodo richiede l'autenticazione, i client gRPC devono trasmettere il token di autenticazione come metadati con la chiamata al metodo, dove la chiave è authorization e il valore è Bearer <JWT_TOKEN>. Vedi un esempio di come farlo quando chiami l'esempio Bookstore in Python, Node.js o 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);
    }
  });
};

Il modo in cui il client ottiene un JWT valido da inviare dipende dal metodo di autenticazione.

Ricevere i risultati dell'autenticazione nell'API

In genere, l'ESP inoltra tutte le intestazioni che riceve. Tuttavia, esegue l'override dell'intestazione Authorization originale quando l'indirizzo di backend è specificato da x-google-backend nella specifica OpenAPI o da BackendRule nella configurazione del servizio gRPC.

ESP invierà il risultato dell'autenticazione in X-Endpoint-API-UserInfo all'API di backend. Ti consigliamo di utilizzare questa intestazione anziché l'intestazione Authorization originale. Questa intestazione è una stringa che base64url codifica un oggetto JSON. Il formato dell'oggetto JSON varia tra ESPv2 ed ESP. Per ESPv2, l'oggetto JSON è esattamente il payload JWT originale. Per ESP, l'oggetto JSON utilizza nomi di campi diversi e inserisce il payload JWT originale nel campo claims. Per ulteriori informazioni sul formato, consulta la sezione Gestire i JWT nel servizio di backend.

Passaggi successivi