توکن های سفارشی ایجاد کنید

Firebase با اجازه دادن به شما برای احراز هویت کاربران یا دستگاه‌ها با استفاده از JSON Web Tokens (JWT) کنترل کاملی بر احراز هویت به شما می‌دهد. شما این نشانه‌ها را روی سرور خود تولید می‌کنید، آن‌ها را به یک دستگاه کلاینت برمی‌گردانید، و سپس از آنها برای احراز هویت از طریق متد signInWithCustomToken() استفاده می‌کنید.

برای دستیابی به این هدف، باید یک نقطه پایانی سرور ایجاد کنید که اعتبار ورود به سیستم را بپذیرد - مانند نام کاربری و رمز عبور - و اگر اعتبارنامه معتبر باشد، یک JWT سفارشی برمی‌گرداند. سپس JWT سفارشی که از سرور شما بازگردانده می‌شود، می‌تواند توسط دستگاه مشتری برای احراز هویت با Firebase ( iOS ، Android ، وب ) استفاده شود. پس از احراز هویت، از این هویت هنگام دسترسی به سایر خدمات Firebase، مانند Firebase Realtime Database و Cloud Storage استفاده می‌شود. علاوه بر این، محتویات JWT در شی auth در Realtime Database Security Rules و شی request.auth در Cloud Storage Security Rules شما در دسترس خواهد بود.

می‌توانید با Firebase Admin SDK یک توکن سفارشی ایجاد کنید، یا اگر سرور شما به زبانی نوشته شده است که Firebase به طور بومی پشتیبانی نمی‌کند، می‌توانید از کتابخانه JWT شخص ثالث استفاده کنید.

قبل از شروع

توکن‌های سفارشی JWT‌های امضا شده‌ای هستند که کلید خصوصی مورد استفاده برای امضا به یک حساب سرویس Google تعلق دارد. چندین راه برای تعیین حساب سرویس Google که باید توسط Firebase Admin SDK برای امضای توکن های سفارشی استفاده شود، وجود دارد:

  • استفاده از فایل JSON حساب سرویس -- این روش را می توان در هر محیطی استفاده کرد، اما از شما می خواهد که فایل JSON حساب سرویس را همراه با کد خود بسته بندی کنید. باید مراقب باشید تا اطمینان حاصل شود که فایل JSON حساب سرویس در معرض اشخاص خارجی قرار نمی گیرد.
  • اجازه دادن به Admin SDK برای کشف یک حساب سرویس -- این روش را می توان در محیط های مدیریت شده توسط Google مانند Google Cloud Functions و App Engine استفاده کرد. ممکن است مجبور شوید برخی از مجوزهای اضافی را از طریق کنسول Google Cloud پیکربندی کنید.
  • استفاده از شناسه حساب سرویس -- هنگامی که در یک محیط مدیریت شده توسط Google استفاده می شود، این روش با استفاده از کلید حساب سرویس مشخص شده، نشانه ها را امضا می کند. با این حال، از یک سرویس وب راه دور استفاده می کند، و ممکن است مجبور شوید مجوزهای اضافی را برای این حساب سرویس از طریق کنسول Google Cloud پیکربندی کنید.

استفاده از فایل JSON حساب سرویس

فایل‌های JSON حساب سرویس حاوی تمام اطلاعات مربوط به حساب‌های سرویس (از جمله کلید خصوصی RSA) هستند. آنها را می توان از کنسول Firebase دانلود کرد. دستورالعمل‌های راه‌اندازی Admin SDK را برای اطلاعات بیشتر در مورد نحوه راه‌اندازی اولیه SDK مدیریت با یک فایل JSON حساب سرویس دنبال کنید.

این روش مقداردهی اولیه برای طیف وسیعی از استقرار Admin SDK مناسب است. همچنین Admin SDK را قادر می‌سازد تا توکن‌های سفارشی را به صورت محلی ایجاد و امضا کند، بدون اینکه تماس API از راه دور برقرار کند. اشکال اصلی این روش این است که شما را ملزم می کند یک فایل JSON حساب سرویس را به همراه کد خود بسته بندی کنید. همچنین توجه داشته باشید که کلید خصوصی در فایل JSON حساب سرویس، اطلاعات حساسی است و باید مراقب محرمانه ماندن آن بود. به طور خاص، از افزودن فایل‌های JSON حساب سرویس به کنترل نسخه عمومی خودداری کنید.

اجازه دادن به Admin SDK یک حساب سرویس را کشف کند

اگر کد شما در محیطی که توسط Google مدیریت می‌شود مستقر شده است، Admin SDK می‌تواند به طور خودکار ابزاری را برای امضای نشانه‌های سفارشی کشف کند:

  • اگر کد شما در محیط استاندارد App Engine برای Java، Python یا Go مستقر شده است، Admin SDK می‌تواند از سرویس App Identity موجود در آن محیط برای امضای توکن‌های سفارشی استفاده کند. سرویس App Identity داده ها را با استفاده از حساب سرویسی که توسط Google App Engine برای برنامه شما ارائه شده است امضا می کند.

  • اگر کد شما در محیط مدیریت شده دیگری (به عنوان مثال Google Cloud Functions، Google Compute Engine) مستقر شده باشد، Firebase Admin SDK می تواند به طور خودکار یک رشته شناسه حساب سرویس را از سرور ابرداده محلی پیدا کند. سپس شناسه حساب سرویس کشف شده همراه با سرویس IAM برای امضای توکن ها از راه دور استفاده می شود.

برای استفاده از این روش‌های امضا، SDK را با اطلاعات کاربری پیش‌فرض Google Application مقداردهی اولیه کنید و رشته ID حساب سرویس را مشخص نکنید:

Node.js

initializeApp();

جاوا

FirebaseApp.initializeApp();

پایتون

default_app = firebase_admin.initialize_app()

برو

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

سی شارپ

FirebaseApp.Create();

برای آزمایش همان کد به صورت محلی، یک فایل JSON حساب سرویس را دانلود کنید و متغیر محیطی GOOGLE_APPLICATION_CREDENTIALS را تنظیم کنید تا به آن اشاره کند.

اگر Firebase Admin SDK باید رشته ID حساب سرویس را پیدا کند، زمانی که کد شما برای اولین بار یک توکن سفارشی ایجاد می‌کند، این کار را انجام می‌دهد. نتیجه ذخیره می‌شود و برای عملیات امضای نشانه‌های بعدی مجدداً استفاده می‌شود. شناسه حساب سرویس کشف شده خودکار معمولاً یکی از حساب‌های خدمات پیش‌فرض ارائه شده توسط Google Cloud است:

درست مانند شناسه‌های حساب سرویس که به‌صراحت مشخص شده‌اند، شناسه‌های حساب سرویس با کشف خودکار باید مجوز iam.serviceAccounts.signBlob را داشته باشند تا ایجاد توکن سفارشی کار کند. برای اعطای مجوزهای لازم به حساب‌های سرویس پیش‌فرض، ممکن است مجبور شوید از IAM و بخش سرپرست کنسول Google Cloud استفاده کنید. برای جزئیات بیشتر به بخش عیب یابی زیر مراجعه کنید.

با استفاده از شناسه حساب سرویس

برای حفظ سازگاری بین بخش‌های مختلف برنامه‌تان، می‌توانید شناسه حساب سرویسی را مشخص کنید که کلیدهای آن برای امضای نشانه‌ها هنگام اجرا در یک محیط تحت مدیریت Google مورد استفاده قرار می‌گیرد. این می‌تواند خط‌مشی‌های IAM را ساده‌تر و ایمن‌تر کند و از گنجاندن فایل JSON حساب سرویس در کد خود اجتناب کند.

شناسه حساب سرویس را می توان در کنسول Google Cloud یا در قسمت client_email یک فایل JSON حساب سرویس دانلود شده پیدا کرد. شناسه‌های حساب سرویس، آدرس‌های ایمیلی هستند که قالب زیر را دارند: <client-id>@<project-id>.iam.gserviceaccount.com . آن‌ها حساب‌های سرویس را در پروژه‌های Firebase و Google Cloud به‌طور منحصربه‌فرد شناسایی می‌کنند.

برای ایجاد توکن های سفارشی با استفاده از شناسه حساب سرویس جداگانه، SDK را مطابق شکل زیر مقداردهی اولیه کنید:

Node.js

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

جاوا

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

پایتون

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

برو

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)
}

سی شارپ

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

شناسه‌های حساب سرویس اطلاعات حساسی نیستند و بنابراین قرار گرفتن در معرض آنها بی‌اهمیت است. با این حال، برای امضای توکن‌های سفارشی با حساب سرویس مشخص‌شده، Firebase Admin SDK باید یک سرویس راه دور را فراخوانی کند. علاوه بر این، باید مطمئن شوید که حساب سرویس Admin SDK که برای برقراری این تماس از آن استفاده می‌کند - معمولاً {project-name}@appspot.gserviceaccount.com - دارای مجوز iam.serviceAccounts.signBlob است. برای جزئیات بیشتر به بخش عیب یابی زیر مراجعه کنید.

با استفاده از Firebase Admin SDK توکن های سفارشی ایجاد کنید

Firebase Admin SDK دارای یک روش داخلی برای ایجاد توکن های سفارشی است. حداقل باید یک uid ارائه کنید که می تواند هر رشته ای باشد اما باید کاربر یا دستگاهی را که احراز هویت می کنید مشخص کند. این توکن ها پس از یک ساعت منقضی می شوند.

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);
  });

جاوا

String uid = "some-uid";

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

پایتون

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

برو

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)

سی شارپ

var uid = "some-uid";

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

همچنین می توانید به صورت اختیاری ادعاهای اضافی را برای گنجاندن در توکن سفارشی مشخص کنید. به عنوان مثال، در زیر، یک فیلد premiumAccount به توکن سفارشی اضافه شده است که در اشیاء auth / request.auth در قوانین امنیتی شما در دسترس خواهد بود:

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);
  });

جاوا

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

پایتون

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

custom_token = auth.create_custom_token(uid, additional_claims)

برو

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)

سی شارپ

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

نام‌های رمز سفارشی رزرو شده

با استفاده از توکن های سفارشی روی مشتریان وارد شوید

پس از ایجاد یک توکن سفارشی، باید آن را به برنامه مشتری خود ارسال کنید. برنامه مشتری با فراخوانی signInWithCustomToken() با توکن سفارشی احراز هویت می‌شود:

iOS+

هدف-C
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
سویفت
Auth.auth().signIn(withCustomToken: customToken ?? "") { user, error in
  // ...
}

اندروید

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);
                }
            }
        });

وحدت

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;
    // ...
  });

اگر احراز هویت با موفقیت انجام شود، کاربر شما اکنون با حساب مشخص شده توسط uid موجود در کد سفارشی وارد برنامه مشتری شما خواهد شد. اگر آن حساب قبلا وجود نداشت، یک رکورد برای آن کاربر ایجاد می شود.

همانند سایر روش‌های ورود به سیستم (مانند signInWithEmailAndPassword() و signInWithCredential() ) شی auth در Realtime Database Security Rules و شی request.auth در Cloud Storage Security Rules شما با uid کاربر پر می شود. . در این حالت، uid همانی خواهد بود که هنگام تولید توکن سفارشی مشخص کرده‌اید.

قوانین پایگاه داده

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

قوانین ذخیره سازی

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";
    }
  }
}

اگر توکن سفارشی حاوی ادعاهای اضافی باشد، می‌توان آنها را از auth.token ( Firebase Realtime Database ) یا شی request.auth.token ( Cloud Storage ) در قوانین شما ارجاع داد:

قوانین پایگاه داده

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

قوانین ذخیره سازی

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

با استفاده از یک کتابخانه JWT شخص ثالث، توکن های سفارشی ایجاد کنید

اگر باطن شما به زبانی است که Firebase Admin SDK رسمی ندارد، همچنان می توانید به صورت دستی توکن های سفارشی ایجاد کنید. ابتدا یک کتابخانه JWT شخص ثالث برای زبان خود پیدا کنید . سپس، از آن کتابخانه JWT برای ایجاد یک JWT که شامل ادعاهای زیر است استفاده کنید:

ادعاهای توکن سفارشی
alg الگوریتم "RS256"
iss صادر کننده آدرس ایمیل حساب سرویس پروژه شما
sub موضوع آدرس ایمیل حساب سرویس پروژه شما
aud مخاطب "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat صادر شده در زمان زمان فعلی، در ثانیه از دوران یونیکس
exp زمان انقضا زمان، بر حسب ثانیه از دوره یونیکس، که در آن توکن منقضی می‌شود. می تواند حداکثر 3600 ثانیه دیرتر از iat باشد.
توجه: این فقط زمانی را کنترل می کند که خود توکن سفارشی منقضی شود. اما هنگامی که کاربر را با استفاده از signInWithCustomToken() وارد سیستم کردید، تا زمانی که جلسه آنها باطل شود یا کاربر از سیستم خارج نشود، وارد دستگاه می‌شوند.
uid شناسه منحصربه‌فرد کاربری که وارد سیستم شده است باید یک رشته، بین 1 تا 128 کاراکتر و شامل باشد. uid های کوتاهتر عملکرد بهتری را ارائه می دهند.
claims (اختیاری) ادعاهای سفارشی اختیاری برای گنجاندن متغیرهای auth / request.auth در قوانین امنیتی

در اینجا چند نمونه پیاده سازی از نحوه ایجاد توکن های سفارشی در زبان های مختلف که Firebase Admin SDK پشتیبانی نمی کند آورده شده است:

PHP

استفاده از php-jwt :

// 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-jwt :

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

پس از ایجاد توکن سفارشی، آن را به برنامه مشتری خود ارسال کنید تا از آن برای احراز هویت با Firebase استفاده کنید. برای نحوه انجام این کار به نمونه کدهای بالا مراجعه کنید.

عیب یابی

این بخش برخی از مشکلات رایجی که توسعه دهندگان ممکن است در هنگام ایجاد توکن های سفارشی با آن مواجه شوند و نحوه حل آنها را تشریح می کند.

IAM API فعال نیست

اگر شناسه حساب سرویس را برای امضای توکن ها مشخص می کنید، ممکن است با خطای زیر مواجه شوید:

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 از IAM API برای امضای توکن ها استفاده می کند. این خطا نشان می دهد که IAM API در حال حاضر برای پروژه Firebase شما فعال نیست. پیوند موجود در پیام خطا را در یک مرورگر وب باز کنید و روی دکمه "فعال کردن API" کلیک کنید تا آن را برای پروژه خود فعال کنید.

حساب سرویس مجوزهای لازم را ندارد

اگر حساب سرویس Firebase Admin SDK بدون مجوز iam.serviceAccounts.signBlob اجرا می‌شود، ممکن است پیام خطایی مانند زیر دریافت کنید:

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

ساده ترین راه برای حل این مشکل، اعطای نقش IAM «خلق رمز حساب سرویس» به حساب سرویس مورد نظر، معمولاً {project-name}@appspot.gserviceaccount.com است:

  1. صفحه IAM و admin را در کنسول Google Cloud باز کنید.
  2. پروژه خود را انتخاب کنید و روی "ادامه" کلیک کنید.
  3. روی نماد ویرایش مربوط به حساب سرویسی که می خواهید به روز کنید کلیک کنید.
  4. روی «افزودن نقش دیگر» کلیک کنید.
  5. "Service Account Token Creator" را در فیلتر جستجو تایپ کنید و آن را از نتایج انتخاب کنید.
  6. برای تایید اعطای نقش روی "ذخیره" کلیک کنید.

برای جزئیات بیشتر در مورد این فرآیند به مستندات IAM مراجعه کنید، یا یاد بگیرید که چگونه نقش‌های به‌روزرسانی را با استفاده از ابزارهای خط فرمان gcloud انجام دهید.

حساب سرویس تعیین نشد

اگر پیام خطایی مشابه زیر دریافت کردید، Firebase Admin SDK به درستی مقداردهی اولیه نشده است.

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

اگر برای کشف خودکار شناسه حساب سرویس به SDK متکی هستید، مطمئن شوید که کد در یک محیط مدیریت شده Google با سرور ابرداده مستقر شده است. در غیر این صورت، حتماً فایل JSON حساب سرویس یا شناسه حساب سرویس را در مقداردهی اولیه SDK مشخص کنید.