Mengautentikasi pengguna

Autentikasi memungkinkan Extensible Service Proxy (ESP) mengidentifikasi pengguna yang memanggil metode layanan Anda, lalu, berdasarkan hal ini, memutuskan apakah akan mengizinkan mereka menggunakan metode tersebut (otorisasi). Halaman ini menjelaskan cara kerja autentikasi dengan Cloud Endpoints untuk layanan gRPC, termasuk cara mengonfigurasi ESP di layanan gRPC untuk mendukung permintaan yang diautentikasi, dan cara memanggil metode yang diautentikasi dari klien gRPC.

ESP mendukung beberapa metode autentikasi, termasuk Firebase, Auth0, dan token ID Google, yang semuanya dapat disiapkan sebagai bagian dari Konfigurasi gRPC API Anda. Dalam setiap kasus, klien harus memberikan token web JSON (JWT) identifikasi dalam permintaannya. ESP memvalidasi token atas nama API Anda, sehingga Anda tidak perlu menambahkan kode autentikasi khusus sendiri.

Meskipun persyaratan autentikasi dan kunci API memungkinkan Anda membatasi siapa yang dapat memanggil metode layanan, keduanya tidak memberikan tingkat keamanan yang sama, dan memberikan informasi yang berbeda ke layanan yang dipanggil. Anda dapat mengetahui lebih lanjut perbedaan antara kunci API dan autentikasi, serta waktu yang tepat untuk menggunakan setiap skema, di Kapan dan mengapa menggunakan kunci API.

Untuk contoh kerja lengkap menggunakan autentikasi, lihat Mengautentikasi menggunakan akun layanan, yang menambahkan autentikasi ke layanan Toko Buku dari Tutorial kami.

Mengonfigurasi autentikasi untuk ESP

Anda mengonfigurasi autentikasi untuk layanan Endpoints for gRPC dalam file YAML konfigurasi layanan gRPC, dengan menggunakan bagian authentication. Anda menentukan metode autentikasi dan detail sumber autentikasi sebagai providers, dengan:

  • Nilai id digunakan untuk mengidentifikasi penyedia autentikasi saat digunakan di rules: nilai ini biasanya menggunakan nama metode autentikasi, tetapi tidak harus begitu.

  • Nilai issuer adalah penerbit token yang diperlukan, sehingga menentukan metode autentikasi.

  • Nilai jwks_uri adalah URI kunci publik penyedia, yang digunakan untuk memvalidasi token. Beberapa metode autentikasi tidak mengharuskan Anda menentukannya, seperti token ID Google yang memungkinkan ESP mendapatkan informasi secara otomatis.

  • jwt_locations digunakan untuk menentukan lokasi untuk mengekstrak JWT.

Anda dapat menentukan beberapa penyedia keamanan dalam file yang sama, tetapi setiap penyedia harus memiliki issuer yang berbeda. Lihat AuthProvider untuk mengetahui informasi selengkapnya.

Anda menentukan metode API yang ingin menggunakan persyaratan autentikasi ini menggunakan rules, seperti yang dijelaskan dalam AuthenticationRule.

Contoh berikut menunjukkan cara menyiapkan ESP di layanan gRPC untuk beberapa metode autentikasi yang didukung:

firebase

Untuk mendukung autentikasi Firebase:

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

Untuk mendukung autentikasi 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

Untuk mendukung autentikasi menggunakan 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

khusus

Untuk mendukung autentikasi kustom:

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

Untuk autentikasi Firebase, kolom audiences diperlukan dan harus berupa project ID Firebase Anda. Untuk semua metode autentikasi lainnya, atribut ini bersifat opsional. Jika tidak ditentukan, ESP akan menerima semua JWT dengan nama layanan backend dalam bentuk https://SERVICE_NAME dalam klaim aud. Untuk mengizinkan client ID tambahan mengakses layanan backend, Anda dapat menentukan client ID yang diizinkan di kolom audiences menggunakan nilai yang dipisahkan koma. ESP kemudian menerima JWT dengan client ID yang diizinkan dalam klaim aud.

Memanggil metode yang diautentikasi dari gRPC

Jika metode memerlukan autentikasi, klien gRPC harus meneruskan token autentikasi sebagai metadata dengan panggilan metode mereka, dengan kunci authorization dan nilainya Bearer <JWT_TOKEN>. Lihat contoh cara melakukannya saat memanggil contoh Toko Buku di Python, Node.js, atau 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');
  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);
    }
  });
};

Cara klien mendapatkan JWT yang valid untuk dikirim bergantung pada metode autentikasi.

Menerima hasil autentikasi di API Anda

ESP biasanya meneruskan semua header yang diterimanya. Namun, header ini akan mengganti header Authorization asli saat alamat backend ditentukan oleh x-google-backend dalam spesifikasi OpenAPI atau BackendRule dalam konfigurasi layanan gRPC.

ESP akan mengirimkan hasil autentikasi di X-Endpoint-API-UserInfo ke API backend. Sebaiknya gunakan header ini, bukan header Authorization asli. Header ini adalah string yang dienkode oleh base64url objek JSON. Format objek JSON berbeda antara ESPv2 dan ESP. Untuk ESPv2, objek JSON sama persis dengan payload JWT asli. Untuk ESP, objek JSON menggunakan nama kolom yang berbeda dan menempatkan payload JWT asli di kolom claims. Lihat Menangani JWT di layanan backend untuk mengetahui informasi selengkapnya tentang formatnya.

Langkah selanjutnya