Özel Jetonlar Oluşturma

Firebase, güvenli JSON Web Token'ları (JWT) kullanarak kullanıcıların veya cihazların kimliğini doğrulamanıza olanak tanır ve kimlik doğrulama üzerinde tam kontrol sahibi olmanızı sağlar. Bu jetonları sunucunuzda oluşturur, istemci cihazına geri gönderir ve ardından signInWithCustomToken() yöntemiyle kimlik doğrulaması yapmak için kullanırsınız.

Bunu yapmak için kullanıcı adı ve şifre gibi oturum açma kimlik bilgilerini kabul eden ve kimlik bilgileri geçerliyse özel bir JWT döndüren bir sunucu uç noktası oluşturmanız gerekir. Sunucunuzdan döndürülen özel JWT, Firebase ile kimlik doğrulaması yapmak için bir istemci cihaz tarafından kullanılabilir (iOS+, Android, web). Kimlik doğrulandıktan sonra bu kimlik, Firebase Realtime Database ve Cloud Storage gibi diğer Firebase hizmetlerine erişirken kullanılır. Ayrıca JWT'nin içeriği, Realtime Database Security Rules uygulamanızdaki auth nesnesinde ve Cloud Storage Security Rules uygulamanızdaki request.auth nesnesinde kullanılabilir.

Firebase Admin SDK ile özel bir jeton oluşturabilir veya sunucunuz Firebase'in yerel olarak desteklemediği bir dilde yazılmışsa üçüncü taraf JWT kitaplığı kullanabilirsiniz.

Başlamadan önce

Özel jetonlar, imzalamak için kullanılan özel anahtarın bir Google hizmet hesabına ait olduğu imzalı JWT'lerdir. Firebase Admin SDK'nın özel jetonları imzalamak için kullanması gereken Google hizmet hesabını belirtmenin birkaç yolu vardır:

  • Yönetici SDK'sının bir hizmet hesabını keşfetmesine izin verme: Bu yöntem, Google Cloud Functions ve App Engine gibi Google tarafından yönetilen ortamlarda kullanılabilir. Google Cloud konsolu üzerinden bazı ek izinleri yapılandırmanız gerekebilir.
  • Hizmet hesabı kimliği kullanma: Bu yöntem, belirtilen hizmet hesabının Google tarafından yönetilen anahtarlarını kullanarak jetonları imzalar. Bu sayede, özel anahtarları kendiniz yönetmek zorunda kalmazsınız. Uzak bir web hizmeti kullanır ve Google Cloud konsolu üzerinden bu hizmet hesabı için ek izinler yapılandırmanız gerekebilir.
  • Hizmet hesabı anahtar dosyası kullanma: Bu yöntemi yalnızca kodunuz Google tarafından yönetilen bir ortamın dışında çalışıyorsa ve yukarıdaki yöntemlerden birini kullanamıyorsanız tercih edin. Bu yöntem, uzun ömürlü bir özel anahtar oluşturmanızı, depolamanızı ve korumanızı gerektirir.

Hizmet hesabı anahtarlarını indirmenizi veya yönetmenizi gerektirmediği için ilk iki yöntemi öneririz.

Admin SDK'nın bir hizmet hesabını keşfetmesine izin verme

Kodunuz Google tarafından yönetilen bir ortamda dağıtıldıysa Admin SDK, özel jetonları imzalamak için otomatik olarak bir yöntem bulmaya çalışabilir:

  • Kodunuz Java, Python veya Go için App Engine standart ortamında dağıtıldıysa Admin SDK, özel jetonları imzalamak için bu ortamda bulunan App Kimlik hizmetini kullanabilir. Uygulama Kimliği hizmeti, Google App Engine tarafından uygulamanız için sağlanan bir hizmet hesabını kullanarak verileri imzalar.

  • Kodunuz başka bir yönetilen ortamda (ör. Google Cloud Functions, Google Compute Engine) dağıtılmışsa Firebase Admin SDK'sı, yerel meta veri sunucusundan bir hizmet hesabı kimliği dizesini otomatik olarak bulabilir. Keşfedilen hizmet hesabı kimliği, daha sonra jetonları uzaktan imzalamak için IAM hizmetiyle birlikte kullanılır.

Bu imzalama yöntemlerini kullanmak için SDK'yı Google Uygulama Varsayılan Kimlik Bilgileriyle ilk kullanıma hazırlayın ve hizmet hesabı kimliği dizesi belirtmeyin:

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Go

app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create();

Aynı kodu hizmet hesabı anahtarı indirmeden yerel olarak test etmek için Google Cloud CLI'yı kullanarak kendi kullanıcı kimlik bilgilerinizle oturum açın:

gcloud auth application-default login

Yerel makinenizde meta veri sunucusu olmadığından Admin SDK, hizmet hesabı kimliğini otomatik olarak bulamaz. Bunun yerine, SDK'yı başlatırken bir hizmet hesabı kimliği belirtin ve kullanıcı hesabınızın bu hizmet hesabında Hizmet Hesabı Jetonu Oluşturucu rolüne sahip olduğundan emin olun.

Firebase Admin SDK'sının bir hizmet hesabı kimliği dizesini keşfetmesi gerekiyorsa kodunuz ilk kez özel bir jeton oluşturduğunda bunu yapar. Sonuç önbelleğe alınır ve sonraki jeton imzalama işlemleri için yeniden kullanılır. Otomatik olarak keşfedilen hizmet hesabı kimliği genellikle Google Cloud tarafından sağlanan varsayılan hizmet hesaplarından biridir:

Açıkça belirtilen hizmet hesabı kimliklerinde olduğu gibi, otomatik olarak keşfedilen hizmet hesabı kimliklerinin de özel jeton oluşturma işleminin çalışması için iam.serviceAccounts.signBlob iznine sahip olması gerekir. Varsayılan hizmet hesaplarına gerekli izinleri vermek için Google Cloud konsolunun IAM ve yönetici bölümünü kullanmanız gerekebilir. Daha fazla bilgi için aşağıdaki sorun giderme bölümüne bakın.

Hizmet hesabı kimliği kullanma

Uygulamanızın çeşitli bölümleri arasında tutarlılığı korumak için Google tarafından yönetilen anahtarları jetonları imzalamak için kullanılacak bir hizmet hesabı kimliği belirtebilirsiniz. Bu sayede IAM politikaları daha basit ve güvenli hale gelebilir. Ayrıca hizmet hesabı anahtarı indirme, depolama veya dağıtma ihtiyacı ortadan kalkar.

Hizmet hesabı kimliğini Google Cloud Console'da bulabilirsiniz. Hizmet hesabı kimlikleri, şu biçimdeki e-posta adresleridir: <client-id>@<project-id>.iam.gserviceaccount.com. Firebase ve Google Cloud projelerindeki hizmet hesaplarını benzersiz şekilde tanımlarlar.

Ayrı bir hizmet hesabı kimliği kullanarak özel jetonlar oluşturmak için SDK'yı aşağıdaki gibi ilk kullanıma hazırlayın:

Node.js

initializeApp({
  serviceAccountId: 'my-client-id@my-project-id.iam.gserviceaccount.com',
});

Java

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setServiceAccountId("my-client-id@my-project-id.iam.gserviceaccount.com")
    .build();
FirebaseApp.initializeApp(options);

Python

options = {
    'serviceAccountId': 'my-client-id@my-project-id.iam.gserviceaccount.com',
}
firebase_admin.initialize_app(options=options)

Go

conf := &firebase.Config{
	ServiceAccountID: "my-client-id@my-project-id.iam.gserviceaccount.com",
}
app, err := firebase.NewApp(context.Background(), conf)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.GetApplicationDefault(),
    ServiceAccountId = "my-client-id@my-project-id.iam.gserviceaccount.com",
});

Hizmet hesabı kimlikleri hassas bilgi değildir. Bu nedenle, açığa çıkmaları önemli değildir. Ancak özel jetonları belirtilen hizmet hesabıyla imzalamak için Firebase Admin SDK'nın uzak bir hizmeti çağırması gerekir. Ayrıca, Admin SDK'nın bu çağrıyı yapmak için kullandığı kimliğin (genellikle üretimde {project-name}@appspot.gserviceaccount.com veya yerel geliştirme sırasında kendi kullanıcı hesabınız) iam.serviceAccounts.signBlob iznine sahip olduğundan da emin olmanız gerekir. Daha fazla bilgi için aşağıdaki sorun giderme bölümüne bakın.

Hizmet hesabı anahtar dosyası kullanma

Kodunuz Google Cloud dışında, yukarıdaki yöntemlerin ikisinin de kullanılamadığı bir ortamda çalışıyorsa Admin SDK'yı bir hizmet hesabı anahtar dosyasıyla başlatabilirsiniz. Ayrıntılar için Admin SDK kurulum talimatlarını uygulayın. Bu yöntemde Admin SDK, uzaktan API çağrıları yapmadan dosyadaki özel anahtarı kullanarak özel jetonları yerel olarak imzalar.

Hizmet hesabı anahtarları, uzun ömürlü kimlik bilgileridir. Bir anahtar sızdırılırsa anahtara sahip olan herkes, anahtar silinene kadar hizmet hesabı olarak hareket edebilir. Anahtar dosyalarını hiçbir zaman sürüm denetimine kaydetmeyin ve ortamınızın desteklediği durumlarda anahtarlar yerine Workload Identity Federasyonu'nu kullanmayı tercih edin. Bazı kuruluşların, hizmet hesabı anahtarı oluşturulmasını engellemek için kuruluş politikası kısıtlamaları kullandığını da unutmayın. Bu durumda diğer yöntemlerden birini kullanmanız gerekir.

Firebase Admin SDK'yı kullanarak özel jetonlar oluşturma

Firebase Admin SDK'sında özel jetonlar oluşturmak için yerleşik bir yöntem bulunur. En azından bir uid sağlamanız gerekir. Bu, herhangi bir dize olabilir ancak kimliğini doğruladığınız kullanıcıyı veya cihazı benzersiz şekilde tanımlamalıdır. Bu jetonların süresi bir saat sonra dolar.

Node.js

const uid = 'some-uid';

getAuth()
  .createCustomToken(uid)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Java

String uid = "some-uid";

String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
// Send token back to client

Python

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Go

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

token, err := client.CustomToken(ctx, "some-uid")
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";

string customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(uid);
// Send token back to client

İsteğe bağlı olarak, özel jetona dahil edilecek ek talepler de belirtebilirsiniz. Örneğin, aşağıda özel jetona bir premiumAccount alanı eklenmiştir. Bu alan, güvenlik kurallarınızdaki auth / request.auth nesnelerinde kullanılabilir:

Node.js

const userId = 'some-uid';
const additionalClaims = {
  premiumAccount: true,
};

getAuth()
  .createCustomToken(userId, additionalClaims)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Java

String uid = "some-uid";
Map<String, Object> additionalClaims = new HashMap<String, Object>();
additionalClaims.put("premiumAccount", true);

String customToken = FirebaseAuth.getInstance()
    .createCustomToken(uid, additionalClaims);
// Send token back to client

Python

uid = 'some-uid'
additional_claims = {
    'premiumAccount': True
}

custom_token = auth.create_custom_token(uid, additional_claims)

Go

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

claims := map[string]interface{}{
	"premiumAccount": true,
}

token, err := client.CustomTokenWithClaims(ctx, "some-uid", claims)
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";
var additionalClaims = new Dictionary<string, object>()
{
    { "premiumAccount", true },
};

string customToken = await FirebaseAuth.DefaultInstance
    .CreateCustomTokenAsync(uid, additionalClaims);
// Send token back to client

Ayrılmış özel jeton adları

İstemcilerde özel jetonlar kullanarak oturum açma

Özel jeton oluşturduktan sonra bunu istemci uygulamanıza göndermeniz gerekir. İstemci uygulaması, signInWithCustomToken() yöntemini çağırarak özel jetonla kimlik doğrulaması yapar:

iOS+

Objective-C
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Swift
Auth.auth().signIn(withCustomToken: customToken ?? "") { user, error in
  // ...
}

Android

mAuth.signInWithCustomToken(mCustomToken)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithCustomToken:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    updateUI(user);
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "signInWithCustomToken:failure", task.getException());
                    Toast.makeText(CustomAuthActivity.this, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                    updateUI(null);
                }
            }
        });

Unity

auth.SignInWithCustomTokenAsync(custom_token).ContinueWith(task => {
  if (task.IsCanceled) {
    Debug.LogError("SignInWithCustomTokenAsync was canceled.");
    return;
  }
  if (task.IsFaulted) {
    Debug.LogError("SignInWithCustomTokenAsync encountered an error: " + task.Exception);
    return;
  }

  Firebase.Auth.AuthResult result = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      result.User.DisplayName, result.User.UserId);
});

C++

firebase::Future<firebase::auth::AuthResult> result =
    auth->SignInWithCustomToken(custom_token);

Web

firebase.auth().signInWithCustomToken(token)
  .then((userCredential) => {
    // Signed in
    var user = userCredential.user;
    // ...
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

Web

import { getAuth, signInWithCustomToken } from "firebase/auth";

const auth = getAuth();
signInWithCustomToken(auth, token)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ...
  });

Kimlik doğrulama başarılı olursa kullanıcınız, özel jetonda bulunan uid tarafından belirtilen hesapla istemci uygulamanızda oturum açar. Bu hesap daha önce mevcut değilse kullanıcı için bir kayıt oluşturulur.

Diğer oturum açma yöntemlerinde (ör. signInWithEmailAndPassword() ve signInWithCredential()) olduğu gibi, Realtime Database Security Rules içindeki auth nesnesi ve Cloud Storage Security Rules içindeki request.auth nesnesi, kullanıcının uid bilgisiyle doldurulur. Bu durumda uid, özel jetonu oluştururken belirttiğiniz değer olur.

Veritabanı Kuralları

{
  "rules": {
    "adminContent": {
      ".read": "auth.uid === 'some-uid'"
    }
  }
}

Depolama Alanı Kuralları

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /adminContent/{filename} {
      allow read, write: if request.auth != null && request.auth.uid == "some-uid";
    }
  }
}

Özel jeton ek talepler içeriyorsa kurallarınızdaki auth.token (Firebase Realtime Database) veya request.auth.token (Cloud Storage) nesnesinden bunlara referans verilebilir:

Veritabanı Kuralları

{
  "rules": {
    "premiumContent": {
      ".read": "auth.token.premiumAccount === true"
    }
  }
}

Depolama Alanı Kuralları

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /premiumContent/{filename} {
      allow read, write: if request.auth.token.premiumAccount == true;
    }
  }
}

Üçüncü taraf JWT kitaplığı kullanarak özel jetonlar oluşturma

Arka ucunuz resmi Firebase Admin SDK'sı olmayan bir dildeyse özel jetonları manuel olarak oluşturabilirsiniz. Öncelikle diliniz için üçüncü taraf JWT kitaplığı bulun. Ardından, aşağıdaki hak taleplerini içeren bir JWT oluşturmak için bu JWT kitaplığını kullanın:

Özel Jeton Talepleri
alg Algoritma "RS256"
iss Düzenleyen Projenizin hizmet hesabı e-posta adresi
sub Konu Projenizin hizmet hesabı e-posta adresi
aud Kitle "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Oluşturulma zamanı UNIX epoch'tan bu yana geçen geçerli zaman (saniye cinsinden)
exp Geçerlilik süresi Jetonun süresinin dolduğu zaman (UNIX epoch'tan bu yana geçen saniye cinsinden). iat değerinden en fazla 3.600 saniye sonra olabilir.
Not: Bu, yalnızca özel jetonun süresinin dolacağı zamanı kontrol eder. Ancak bir kullanıcının oturumunu signInWithCustomToken() kullanarak açtığınızda, oturumu geçersiz kılınana veya kullanıcı oturumu kapatana kadar cihazda oturumu açık kalır.
uid Oturum açmış kullanıcının benzersiz tanımlayıcısı, 1-128 karakter uzunluğunda (bu değerler dahil) bir dize olmalıdır. Daha kısa uids daha iyi performans sunar.
claims (isteğe bağlı) Güvenlik Kuralları auth / request.auth değişkenlerine dahil edilecek isteğe bağlı özel talepler

Aşağıda, Firebase Admin SDK'nın desteklemediği çeşitli dillerde özel jetonların nasıl oluşturulacağına dair bazı örnek uygulamalar verilmiştir. Bu örneklerde, jetonlar bir hizmet hesabı özel anahtarıyla yerel olarak imzalanır. Bu nedenle, hizmet hesabı anahtar dosyalarıyla ilgili güvenlik kılavuzu geçerlidir. Özel anahtar yönetmekten kaçınmak için Google'ın, IAM Service Account Credentials API'nin signJwt yöntemini çağırarak jeton yükünü uzaktan imzalamasını sağlayabilirsiniz. Arayanın, hizmet hesabında Hizmet Hesabı Jetonu Oluşturucu rolüne sahip olması gerekir.

PHP

php-jwt kullanımı:

// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;

// Placeholder values. In production, load your service account's email
// address and private key from a secure secret store. Never hard-code
// private keys in source code.
$service_account_email = "abc-123@a-b-c-123.iam.gserviceaccount.com";
$private_key = "-----BEGIN PRIVATE KEY-----...";

function create_custom_token($uid, $is_premium_account) {
  global $service_account_email, $private_key;

  $now_seconds = time();
  $payload = array(
    "iss" => $service_account_email,
    "sub" => $service_account_email,
    "aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    "iat" => $now_seconds,
    "exp" => $now_seconds+(60*60),  // Maximum expiration time is one hour
    "uid" => $uid,
    "claims" => array(
      "premium_account" => $is_premium_account
    )
  );
  return JWT::encode($payload, $private_key, "RS256");
}

Ruby

ruby-jwt kullanımı:

require "jwt"

# Placeholder values. In production, load your service account's email
# address and private key from a secure secret store. Never hard-code
# private keys in source code.
$service_account_email = "service-account@my-project-abc123.iam.gserviceaccount.com"
$private_key = OpenSSL::PKey::RSA.new "-----BEGIN PRIVATE KEY-----\n..."

def create_custom_token(uid, is_premium_account)
  now_seconds = Time.now.to_i
  payload = {:iss => $service_account_email,
             :sub => $service_account_email,
             :aud => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
             :iat => now_seconds,
             :exp => now_seconds+(60*60), # Maximum expiration time is one hour
             :uid => uid,
             :claims => {:premium_account => is_premium_account}}
  JWT.encode payload, $private_key, "RS256"
end

Özel jetonu oluşturduktan sonra Firebase ile kimlik doğrulaması yapmak için kullanmak üzere istemci uygulamanıza gönderin. Bunun nasıl yapılacağını öğrenmek için yukarıdaki kod örneklerine bakın.

Sorun giderme

Bu bölümde, geliştiricilerin özel jeton oluştururken karşılaşabileceği bazı yaygın sorunlar ve bunların nasıl çözüleceği açıklanmaktadır.

IAM API etkin değil

Jetonları imzalamak için bir hizmet hesabı kimliği belirtiyorsanız aşağıdakine benzer bir hata alabilirsiniz:

Identity and Access Management (IAM) API has not been used in project
1234567890 before or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project=1234567890
then retry. If you enabled this API recently, wait a few minutes for the action
to propagate to our systems and retry.

Firebase Admin SDK'sı, jetonları imzalamak için IAM API'yi kullanır. Bu hata, IAM API'nin Firebase projeniz için etkinleştirilmediğini gösterir. Hata mesajındaki bağlantıyı bir web tarayıcısında açın ve projeniz için etkinleştirmek üzere "API'yi etkinleştir" düğmesini tıklayın.

Hizmet hesabı gerekli izinlere sahip değil

Firebase Admin SDK'nın çalıştığı hizmet hesabında iam.serviceAccounts.signBlob izni yoksa aşağıdaki gibi bir hata mesajı alabilirsiniz:

Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.

Bu sorunu çözmek için uygun hizmet hesabına Hizmet Hesabı Jeton Oluşturucu IAM rolünü verin. Kullanılan varsayılan hizmet hesabı, ortama ve Cloud Functions sürümüne bağlıdır:

  • Cloud Functions (1. nesil): Varsayılan App Engine hizmet hesabını kullanır.
  • Cloud Functions (2. nesil): Varsayılan Compute Engine hizmet hesabını kullanır.
  1. Google Cloud konsolunda IAM ve Yönetici'ye gidin.

  2. Güncellemek istediğiniz hizmet hesabına karşılık gelen düzenleme simgesini tıklayın.

  3. Başka bir rol ekle'yi tıklayın.

  4. Arama filtresine "Service Account Token Creator" yazın ve sonuçlardan seçin.

  5. Rol atamasını onaylamak için Kaydet'i tıklayın.

Bu işlemle ilgili daha fazla bilgi için IAM belgelerini inceleyin veya gcloud komut satırı araçlarını kullanarak rolleri nasıl güncelleyeceğinizi öğrenin.

Hizmet hesabı belirlenemedi

Aşağıdakine benzer bir hata mesajı alırsanız Firebase Admin SDK'sı düzgün şekilde başlatılmamış demektir.

Failed to determine service account ID. Initialize the SDK with service account
credentials or specify a service account ID with iam.serviceAccounts.signBlob
permission.

Hizmet hesabı kimliğini otomatik olarak bulmak için SDK'yı kullanıyorsanız kodun, meta veri sunucusu olan yönetilen bir Google ortamına dağıtıldığından emin olun. Aksi takdirde, SDK başlatma sırasında bir hizmet hesabı kimliği belirttiğinizden veya Google dışı ortamlarda SDK'yı bir hizmet hesabı anahtar dosyası ile başlattığınızdan emin olun.