Özel Jetonlar Oluşturma

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

Bunu yapmak için oturum açma kimlik bilgilerini (ör. kullanıcı adı ve şifre) kabul eden bir sunucu uç noktası oluşturmanız ve kimlik bilgileri geçerliyse özel bir JWT döndürmeniz gerekir. Sunucunuzdan döndürülen özel JWT, daha sonra bir istemci cihaz tarafından Firebase ile kimlik doğrulamak için kullanılabilir (iOS+, Android, web). Kimlik doğrulandıktan sonra bu kimlik, Firebase Realtime Database ve Cloud Storage gibi diğer Firebase hizmetlerine erişilirken kullanılır. Ayrıca, JWT'nin içeriği Realtime Database Security Rules içindeki auth nesnesinde ve Cloud Storage Security Rules içindeki 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, imzalamada kullanılan özel anahtarın bir Google hizmet hesabına ait olduğu imzalı JWT'lerdir. Firebase Admin SDK'sının özel jetonları imzalamak için kullanması gereken Google hizmet hesabını belirtmenin çeşitli yolları vardır:

  • Hizmet hesabı JSON dosyası kullanma: Bu yöntem herhangi bir ortamda kullanılabilir ancak kodunuzla birlikte bir hizmet hesabı JSON dosyası paketlemenizi gerektirir. Hizmet hesabı JSON dosyasının harici taraflara gösterilmediğinden emin olmak için özel dikkat gösterilmelidir.
  • Yönetici SDK'sının 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: Google tarafından yönetilen bir ortamda kullanıldığında bu yöntem, belirtilen hizmet hesabının anahtarıyla jetonları imzalar. Ancak bu hizmet, 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ı JSON dosyası kullanma

Hizmet hesabı JSON dosyaları, hizmet hesaplarıyla ilgili tüm bilgileri (RSA özel anahtarı dahil) içerir. Bu dosyalar Firebase konsolundan indirilebilir. Admin SDK'yı hizmet hesabı JSON dosyasıyla başlatma hakkında daha fazla bilgi edinmek için Admin SDK kurulumuyla ilgili talimatları uygulayın.

Bu başlatma yöntemi, çok çeşitli Admin SDK dağıtımları için uygundur. Ayrıca, Admin SDK'nın herhangi bir uzak API çağrısı yapmadan özel jetonları yerel olarak oluşturup imzalamasına olanak tanır. Bu yaklaşımın temel dezavantajı, kodunuzla birlikte bir hizmet hesabı JSON dosyası paketlemenizi gerektirmesidir. Ayrıca, hizmet hesabı JSON dosyasındaki özel anahtarın hassas bilgi olduğunu ve gizli tutulması için özel dikkat gösterilmesi gerektiğini unutmayın. Özellikle, hizmet hesabı JSON dosyalarını herkese açık sürüm kontrolüne eklemeyin.

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

Kodunuz Google tarafından yönetilen bir ortamda dağıtılıyorsa Admin SDK, özel jetonları imzalamanın bir yolunu otomatik olarak keşfetmeye çalışabilir:

  • Kodunuz Java, Python veya Go için App Engine standart ortamında dağıtılıyorsa Yönetici SDK'sı, özel jetonları imzalamak için bu ortamda bulunan Uygulama Kimliği 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ılıyorsa Firebase Admin SDK, yerel meta veri sunucusundan bir hizmet hesabı kimliği dizesini otomatik olarak bulabilir. Daha sonra, keşfedilen hizmet hesabı kimliği, jetonları uzaktan imzalamak için IAM hizmetiyle birlikte kullanılır.

Bu imzalama yöntemlerinden yararlanmak için SDK'yı Google Application Default kimlik bilgileriyle başlatı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 yerel olarak test etmek için bir hizmet hesabı JSON dosyası indirin ve GOOGLE_APPLICATION_CREDENTIALS ortam değişkenini bu dosyayı işaret edecek şekilde ayarlayın.

Firebase Admin SDK'sının bir hizmet hesabı kimliği dizesi bulması gerekiyorsa bunu kodunuz ilk kez özel bir jeton oluşturduğunda yapar. Sonuç, sonraki jeton imzalama işlemleri için önbelleğe alınır ve 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 bir ortamda çalışırken jetonları imzalamak üzere anahtarları kullanılacak bir hizmet hesabı kimliği belirtebilirsiniz. Bu sayede IAM politikaları daha basit ve güvenli hale gelebilir. Ayrıca, hizmet hesabı JSON dosyasını kodunuza eklemeniz gerekmez.

Hizmet hesabı kimliğini Google Cloud konsolunda veya indirilen hizmet hesabı JSON dosyasının client_email alanında bulabilirsiniz. Hizmet hesabı kimlikleri, aşağıdaki biçime sahip e-posta adresleridir: <client-id>@<project-id>.iam.gserviceaccount.com. Firebase'deki hizmet hesaplarını ve Google Cloud projelerini benzersiz şekilde tanımlar.

Ayrı bir hizmet hesabı kimliği kullanarak özel jetonlar oluşturmak için SDK'yı aşağıda gösterildiği gibi başlatı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 bilgiler değildir ve bu nedenle bunların açığa çıkması önemsizdir. 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ığı hizmet hesabının (genellikle {project-name}@appspot.gserviceaccount.com) 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.

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

Firebase Admin SDK'sında özel jeton 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 bir ş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 eklenecek 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() işlevini çağırarak özel jetonla kimlik doğrular:

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 yer alan uid ile belirtilen hesapla istemci uygulamanızda oturum açar. Bu hesap daha önce yoksa 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 ile doldurulur. Bu durumda, uid, özel jetonu oluştururken belirttiğiniz değer olur.

Veritabanı Kuralları

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

Depolama 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 referans verilebilir:

Veritabanı Kuralları

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

Depolama 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ığını kullanarak özel jetonlar oluşturma

Arka uçunuz, resmi Firebase Admin SDK'sının bulunmadığı bir dildeyse yine de özel jetonları manuel olarak oluşturabilirsiniz. Öncelikle, diliniz için üçüncü taraf JWT kitaplığı bulun. Ardından, aşağıdaki talepleri 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 Yayınlanma zamanı UNIX sıfır zamanından itibaren saniye cinsinden geçerli saat
exp Geçerlilik süresi Jetonun geçerliliğinin sona erdiği zaman (UNIX sıfır zamanından itibaren saniye cinsinden). iat tarihinden en fazla 3.600 saniye sonra olabilir.
Not: Bu ayar yalnızca özel jetonun kendisinin sona erme zamanını kontrol eder. Ancak signInWithCustomToken() kullanarak bir kullanıcının oturumunu açtığınızda, oturumu geçersiz kılınana veya kullanıcı oturumu kapatana kadar cihazda oturum açık kalır.
uid Oturum açmış kullanıcının benzersiz tanımlayıcısı, 1-128 karakter uzunluğunda bir dize olmalıdır. Daha kısa uid daha iyi performans sağlar.
claims (isteğe bağlı) Güvenlik kurallarına auth / request.auth değişkenlerine dahil edilecek isteğe bağlı özel talepler

Firebase Admin SDK'nın desteklemediği çeşitli dillerde özel jetonların nasıl oluşturulacağına dair bazı örnek uygulamaları aşağıda bulabilirsiniz:

PHP

php-jwt kullanma:

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

// Get your service account's email address and private key from the JSON key file
$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 kullanma:

require "jwt"

# Get your service account's email address and private key from the JSON key file
$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ğrulamak için kullanılmak üzere istemci uygulamanıza gönderin. Bunu nasıl yapacağınızı öğ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, jetonları imzalamak için IAM API'sini kullanır. Bu hata, IAM API'nin Firebase projeniz için şu anda etkin olmadığını gösterir. Hata mesajındaki bağlantıyı bir web tarayıcısında açın ve projenizde etkinleştirmek için "API'yi etkinleştir" düğmesini tıklayın.

Hizmet hesabında gerekli izinler yok

Firebase Admin SDK'nın çalıştığı hizmet hesabında iam.serviceAccounts.signBlob izni yoksa aşağıdakine benzer 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 çözmenin en kolay yolu, söz konusu hizmet hesabına "Hizmet Hesabı Jetonu Oluşturucu" IAM rolünü vermektir. Bu rol genellikle {project-name}@appspot.gserviceaccount.com olur:

  1. Google Cloud konsolunda IAM ve yönetici sayfasını açın.
  2. Projenizi seçin ve "Devam"ı tıklayın.
  3. Güncellemek istediğiniz hizmet hesabına karşılık gelen düzenleme simgesini tıklayın.
  4. "Başka bir rol ekle"yi tıklayın.
  5. Arama filtresine "Service Account Token Creator" (Hizmet Hesabı Jeton Oluşturucu) yazın ve sonuçlardan bu seçeneği belirleyin.
  6. Rol verme işlemini onaylamak için "Kaydet"i tıklayın.

Bu süreçle ilgili daha fazla bilgi için IAM belgelerine bakın 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 doğru şekilde başlatılmamıştır.

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

Bir hizmet hesabı kimliğini otomatik olarak keşfetmek için SDK'yı kullanıyorsanız kodun, meta veri sunucusu olan yönetilen bir Google ortamında dağıtıldığından emin olun. Aksi takdirde, SDK başlatılırken hizmet hesabı JSON dosyasını veya hizmet hesabı kimliğini belirttiğinizden emin olun.