Özel Jetonlar Oluşturma

Firebase, güvenli JSON Web Jetonları (JWT'ler) kullanarak kullanıcıların veya cihazların kimliğini doğrulamanıza olanak tanıyarak kimlik doğrulama üzerinde tam kontrol sağlar. Bu jetonları sunucunuzda oluşturur, istemci cihaza geri gönderir ve ardından signInWithCustomToken() yöntemi aracılığıyla kimlik doğrulaması yapmak için bunları 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, 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şirken kullanılır. Ayrıca JWT'nin içeriği, Gerçek Zamanlı Veritabanı Güvenlik Kurallarınızdaki auth nesnesinde ve Cloud Storage Güvenlik Kurallarınızdaki request.auth nesnesinde kullanılabilir.

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

Başlamadan önce

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

  • Hizmet hesabı JSON dosyası kullanma -- Bu yöntem herhangi bir ortamda kullanılabilir ancak kodunuzla birlikte bir hizmet hesabı JSON dosyasını paketlemenizi gerektirir. Hizmet hesabı JSON dosyasının harici taraflara açık olmadığından emin olmak için özel dikkat gösterilmelidir.
  • Yönetici SDK'sının bir hizmet hesabı bulmasına izin verme: Bu yöntem, Google Cloud Functions ve App Engine gibi Google tarafından yönetilen ortamlarda kullanılabilir. Bazı ek izinleri Google Cloud konsolunu kullanarak 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ını kullanarak jetonları imzalar. Ancak uzak web hizmeti kullanıldığından bu hizmet hesabı için Google Cloud konsolunu kullanarak ek izinler yapılandırmanız gerekebilir.

Hizmet hesabı JSON dosyası kullanma

Hizmet hesabı JSON dosyaları, hizmet hesaplarına karşılık gelen tüm bilgileri içerir (RSA özel anahtarı dahil). Firebase konsolundan indirilebilir. Yönetici SDK'sını bir hizmet hesabı JSON dosyasıyla başlatma hakkında daha fazla bilgi için Yönetici SDK'sı kurulum talimatlarını uygulayın.

Bu başlatma yöntemi, çok çeşitli Admin SDK dağıtımları için uygundur. Ayrıca, Yönetici SDK'sinin herhangi bir uzak API çağrısı yapmadan özel jetonlar yerel olarak oluşturmasını ve imzalamasını sağlar. Bu yaklaşımın ana dezavantajı, kodunuzla birlikte bir hizmet hesabı JSON dosyasını paketlemenizi gerektirmesidir. Ayrıca, bir hizmet hesabı JSON dosyasındaki özel anahtarın hassas bilgiler olduğunu ve bu bilgilerin gizli tutulmasına özellikle dikkat edilmesi gerektiğini unutmayın. Özellikle, herkese açık sürüm kontrolüne hizmet hesabı JSON dosyaları eklemekten kaçının.

Yönetici SDK'sinin bir hizmet hesabını bulmasına izin verme

Kodunuz Google tarafından yönetilen bir ortamda dağıtılmışsa Admin SDK'sı özel jetonları imzalamanın yollarını otomatik olarak bulmaya çalışabilir:

  • Kodunuz Java, Python veya Go için App Engine standart ortamında dağıtıldıysa Yönetici SDK'sı, özel jetonları imzalamak için ilgili ortamda bulunan App Identity hizmetini kullanabilir. App Identity 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ıldıysa 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öntemlerinden yararlanmak için SDK'yı Google Uygulama Varsayılan 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 bunu işaret edecek şekilde ayarlayın.

Firebase Admin SDK'nın bir hizmet hesabı kimliği dizesi keşfetmesi gerekiyorsa kodunuz ilk kez özel jeton oluşturduğunda bunu bulur. 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, özel jeton oluşturma işleminin çalışması için otomatik olarak keşfedilen hizmet hesabı kimliklerinin de 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 ayrıntılı 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ığı sağlamak amacıyla, Google tarafından yönetilen bir ortamda çalışırken anahtarları jetonları imzalamak için kullanılacak bir hizmet hesabı kimliği belirtebilirsiniz. Bu şekilde IAM politikalarını daha basit ve güvenli hale getirebilir, ayrıca kodunuza hizmet hesabı JSON dosyasını eklemek zorunda kalmazsınız.

Hizmet hesabı kimliği, Google Cloud Console'da veya indirilen bir hizmet hesabı JSON dosyasının client_email alanında bulunabilir. Hizmet hesabı kimlikleri, şu biçimde olan e-posta adresleridir: <client-id>@<project-id>.iam.gserviceaccount.com. Firebase ve Google Cloud projelerinde hizmet hesaplarını 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 gösterilmesi alakasızdır. Ancak belirtilen hizmet hesabıyla özel jetonlar imzalamak için Firebase Admin SDK'nın uzak bir hizmeti çağırması gerekir. Ayrıca, Yönetici SDK'sı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 ayrıntılı 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'da, özel jetonlar oluşturmak için yerleşik bir yöntem bulunur. En azından, herhangi bir dize olabilecek ancak kimliğini doğruladığınız kullanıcıyı veya cihazı benzersiz bir şekilde tanımlaması gereken bir uid girmeniz gerekir. 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

İsterseniz özel jetona eklenecek başka talepler de belirtebilirsiniz. Örneğin, aşağıda, Güvenlik Kurallarınızdaki auth / request.auth nesnelerinde bulunacak özel jetona bir premiumAccount alanı eklendi:

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 jeton kullanarak oturum açın

Özel jeton oluşturduktan sonra bu jetonu istemci uygulamanıza göndermeniz gerekir. İstemci uygulaması signInWithCustomToken() çağrısı yaparak özel jetonla kimlik doğrulaması yapar:

iOS ve üzeri

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 artık istemci uygulamanızda özel jetonda bulunan uid ile belirtilen hesapla oturum açar. Bu hesap daha önce oluşturulmamışsa bu kullanıcı için bir kayıt oluşturulur.

Diğer oturum açma yöntemlerinde (signInWithEmailAndPassword() ve signInWithCredential() gibi) olduğu gibi Gerçek Zamanlı Veritabanı Güvenlik Kurallarınızdaki auth nesnesi ve Cloud Storage Güvenlik Kurallarınızdaki request.auth nesnesi kullanıcının uid ile doldurulur. Bu durumda, uid, özel jetonu oluştururken belirttiğiniz veri olacaktır.

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 hak talepleri 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 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 bir Firebase Admin SDK'sına sahip olmayan bir dilde olsa bile manuel olarak özel jetonlar oluşturabilirsiniz. Öncelikle dilinize uygun bir üçüncü taraf JWT kitaplığı bulun. Daha sonra bu JWT kitaplığını kullanarak aşağıdaki iddiaları içeren bir JWT'yi kırın:

Özel Jeton Hak 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 döneminden bu yana geçen saniye cinsinden geçerli zaman
exp Geçerlilik süresi Jetonun süresinin dolacağı UNIX döneminden beri geçen süre (saniye cinsinden). Bu, iat değerinden en fazla 3.600 saniye sonra olabilir.
Not: Bu ayar yalnızca özel jetonun süresinin dolacağı zamanı kontrol eder. Ancak, signInWithCustomToken() kullanarak oturum açan bir kullanıcının 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 (1-128 karakter dahil) bir dize olmalıdır. Daha kısa uid değerleri 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 hak talepleri

Firebase Admin SDK'nın desteklemediği çeşitli dillerde özel jeton oluşturmayla ilgili bazı örnek uygulamaları aşağıda bulabilirsiniz:

PHP

php-jwt kullanıldığında:

// 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 kullanıldığında:

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 bu jetonu Firebase ile kimlik doğrulamak için kullanmak üzere istemci uygulamanıza gönderin. Bunu nasıl yapacağınızı öğrenmek için yukarıdaki kod örneklerini inceleyin.

Sorun giderme

Bu bölümde, geliştiricilerin özel jetonlar oluştururken karşılaşabilecekleri 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 şu anda Firebase projeniz için etkinleştirilmediğini 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ı gerekli izinlere sahip değil

Firebase Admin SDK'nın çalıştırdığı 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 çözmenin en kolay yolu, söz konusu hizmet hesabına "Hizmet Hesabı Jetonu Oluşturucu" IAM rolünü vermektir. Genellikle {project-name}@appspot.gserviceaccount.com:

  1. Google Cloud Console'daki IAM ve yönetici sayfasını açın.
  2. Projenizi seçin ve "Continue" (Devam) düğmesini tıklayın.
  3. Güncellemek istediğiniz hizmet hesabına karşılık gelen düzenle simgesini tıklayın.
  4. "Başka bir rol ekle"yi tıklayın.
  5. Arama filtresine "Service Account Token Creator" (Hizmet Hesabı Jetonu Oluşturucu) yazın ve sonuçlar arasından bunu seçin.
  6. Rol atamasını onaylamak için "Kaydet"i tıklayın.

Bu süreçle ilgili daha fazla bilgi için IAM belgelerini inceleyin veya gcloud komut satırı araçlarını kullanarak rolleri güncelleme hakkında bilgi edinin.

Hizmet hesabı belirlenemedi

Aşağıdakine benzer bir hata mesajı alırsanız Firebase Admin SDK'sı düzgün bir ş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.

Bir hizmet hesabı kimliğini otomatik olarak keşfetmesi için SDK'dan yararlanıyorsanız kodun, yönetilen bir Google ortamında bir meta veri sunucusuyla 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.