blob: cf9ea7f111ab977540ce7eadea9340569ffd5911 [file]
// Copyright 2026 Google LLC
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
/// @docImport 'auth_client.dart';
library;
import 'dart:convert';
import 'package:google_cloud/general.dart';
import 'iam_signer.dart';
import 'impersonated_auth_client.dart';
import 'service_account_credentials.dart';
/// Extension providing smart signing capabilities for [AuthClient].
///
/// This extension adds a universal [sign] method that automatically selects
/// the appropriate signing strategy based on the authentication context:
///
/// 1. **ImpersonatedAuthClient**: Uses IAM signBlob with the target principal
/// 2. **Service account credentials**: Uses local RSA-SHA256 signing
/// 3. **Other auth clients** (ADC on GCE/Cloud Run): Uses IAM signBlob with
/// the default service account from metadata server
///
/// Example usage:
/// ```dart
/// // Works with service account credentials
/// final client = await clientViaServiceAccount(credentials, scopes);
/// final signature = await client.sign(utf8.encode('data to sign'));
///
/// // Works with ADC on GCE/Cloud Run
/// final client = await clientViaApplicationDefaultCredentials(scopes: scopes);
/// final signature = await client.sign(utf8.encode('data to sign'));
///
/// // Works with impersonated credentials
/// final client = await clientViaServiceAccountImpersonation(
/// sourceClient: sourceClient,
/// targetServiceAccount: 'target@project.iam.gserviceaccount.com',
/// targetScopes: scopes,
/// );
/// final signature = await client.sign(utf8.encode('data to sign'));
/// ```
extension AuthClientSigningExtension on AuthClient {
/// Queries the GCE metadata server to retrieve the default
/// service account email.
///
/// The result is cached for the lifetime of the Dart process.
///
/// If [refresh] is `true`, the cache is cleared and the value is re-computed.
Future<String> getServiceAccountEmail({bool refresh = false}) async =>
await serviceAccountEmailFromMetadataServer(
client: this,
refresh: refresh,
);
/// Signs the given [data] using the credentials from this auth client.
///
/// This method has three modes of operation depending on the client and
/// parameters:
///
/// 1. **Impersonated client:** If this client is an
/// [ImpersonatedAuthClient], signing is delegated to its `sign` method.
/// The `serviceAccountCredentials`, `serviceAccountEmail`, and
/// `endpoint` parameters of this extension method are ignored.
///
/// 2. **Local signing with private key:** If [serviceAccountCredentials] are
/// provided, they are used to sign the data locally using RSA-SHA256 if
/// the credentials have a private key. The `serviceAccountEmail`
/// and `endpoint` parameters are ignored.
///
/// 3. **IAM API signing:** Otherwise, this will call the IAM Credentials API
/// `signBlob` endpoint.
/// - The [serviceAccountEmail] can be provided to specify which service
/// account to use. If not provided, it will be inferred from the
/// environment (e.g., GCE metadata server).
/// - The [endpoint] is an optional custom IAM Credentials API endpoint.
/// This is useful when working with different universe domains. If not
/// provided, the endpoint is automatically determined from the
/// credential's universe domain (e.g.,
/// `https://iamcredentials.googleapis.com` for the default universe,
/// or a custom universe domain from the service account JSON).
///
/// Returns the signature as a String (base64-encoded).
///
/// Example:
/// ```dart
/// import 'dart:convert';
///
/// final client = await clientViaServiceAccount(credentials, scopes);
/// final data = utf8.encode('data to sign');
/// final signature = await client.sign(
/// data,
/// serviceAccountCredentials: credentials,
/// );
/// print('Signature (base64): ${signature.signedBlob}');
/// ```
Future<String> sign(
List<int> data, {
ServiceAccountCredentials? serviceAccountCredentials,
String? serviceAccountEmail,
String? endpoint,
}) async {
// Check if this is an impersonated client
if (this is ImpersonatedAuthClient) {
final impersonated = this as ImpersonatedAuthClient;
return (await impersonated.sign(data)).signedBlob;
}
if (serviceAccountCredentials != null) {
// Use local signing with service account credentials
return base64Encode(serviceAccountCredentials.sign(data));
}
return (await signBlob(
this,
data,
serviceAccountEmail: serviceAccountEmail,
endpoint: endpoint,
)).signedBlob;
}
}