Firebase با اجازه دادن به شما برای اصالتسنجی کاربران یا دستگاهها بااستفاده از «نشانهای وب JSON» (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» یک کد سفارشی ایجاد کنید، یا اگر سرورتان به زبانی نوشته شده است که 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 با فایل JSON حساب سرویس، دستورالعملهای راهاندازی Admin SDK را دنبال کنید.
این روش مقداردهی اولیه برای طیف گستردهای از پیادهسازیهای Admin SDK مناسب است. همچنین به Admin SDK امکان میدهد تا بهصورت محلی و بدون برقراری تماسهای API ازراهدور، توکنهای سفارشی ایجاد و امضا کند. اشکال اصلی این رویکرد این است که شما را ملزم میکند فایل JSON حساب سرویس را همراه با کدتان بستهبندی کنید. همچنین توجه داشته باشید که کلید خصوصی در فایل JSON حساب سرویس اطلاعات حساسی است و باید برای محرمانه نگه داشتن آن دقت ویژهای شود. بهویژه، از افزودن فایلهای JSON حساب سرویس به کنترل نسخه عمومی خودداری کنید.
اجازه دادن به «سرپرست SDK» برای پیدا کردن حساب سرویس
اگر کدتان در محیطی که Google مدیریت میکند مستقر شده است، «سرپرست SDK» میتواند برای کشف خودکار روشی برای امضای کدهای سفارشی تلاش کند:
اگر کدتان در محیط استاندارد App Engine برای Java، Python، یا Go مستقر شده باشد، «کیت توسعه نرمافزار سرپرست» میتواند از سرویس هویت برنامه موجود در آن محیط برای امضای نشانهای سفارشی استفاده کند. سرویس «هویت برنامه» دادهها را بااستفاده از حساب سرویس ارائهشده برای برنامهتان توسط Google App Engine امضا میکند.
اگر کد شما در محیط مدیریتشده دیگری (مثلاً Google Cloud Functions، Google Compute Engine) مستقر شده باشد، Firebase Admin SDK میتواند بهطور خودکار رشته شناسه حساب سرویس را از سرور فراداده محلی پیدا کند. سپس از شناسه حساب سرویس کشفشده در ارتباط با سرویس IAM برای امضای ازراهدور توکنها استفاده میشود.
برای استفاده از این روشهای امضا، کیت توسعه نرمافزار را با اعتبارنامه پیشفرض برنامه Google مقداردهی اولیه کنید و رشته شناسه حساب سرویس را مشخص نکنید:
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 مجبور باشد رشته شناسه حساب سرویس را پیدا کند، این کار را زمانی انجام میدهد که کد شما برای اولینبار یک کد سفارشی ایجاد میکند. نتیجه در حافظه نهان ذخیره میشود و برای عملیات امضای توکن بعدی مجدداً استفاده میشود. شناسه حساب سرویس بهطور خودکار کشفشده معمولاً یکی از حسابهای سرویس پیشفرض است که توسط 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» باید سرویس ازراهدوری را فراخوانی کند.
علاوهبراین، باید مطمئن شوید که حساب سرویسی که «کیت توسعه نرمافزار سرپرست»
برای برقراری این تماس استفاده میکند
—معمولاً {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+
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;
// ...
});
اگر اصالتسنجی موفقیتآمیز باشد، کاربر شما اکنون با حسابی که توسط 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 |
زمان انقضا |
زمان، برحسب ثانیه از تاریخ یونیکس، که در آن توکن منقضی میشود. این زمان میتواند حداکثر ۳۶۰۰ ثانیه بعد از iat باشد.
توجه: این فقط زمانی را کنترل میکند که کد سفارشی خودش منقضی میشود. اما وقتی کاربر را بااستفاده از signInWithCustomToken() به سیستم وارد میکنید، تا زمانی که جلسه او نامعتبر شود یا کاربر از سیستم خارج شود،
در دستگاه واردشده به سیستم باقی میماند.
|
uid |
شناسه متمایز کاربر واردشده به سیستم باید رشتهای بین ۱ تا ۱۲۸ نویسه باشد. 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
بااستفاده از 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» از IAM API برای امضا کردن کدها استفاده میکند. این خطا نشان میدهد که IAM API درحالحاضر برای پروژه Firebase شما فعال نیست. پیوند موجود در پیام خطا را در مرورگر وب باز کنید و روی دکمه «فعال کردن میانای برنامهسازی کاربردی» کلیک کنید تا برای پروژهتان فعال شود.
حساب سرویس اجازههای لازم را ندارد
اگر حساب سرویسی که 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 ایجادکننده توکن حساب سرویس را به حساب سرویس مناسب اعطا کنید. حساب سرویس پیشفرض مورداستفاده به محیط و نسخه Cloud Functions بستگی دارد:
- Cloud Functions (نسل اول): از حساب سرویس پیشفرض App Engine استفاده میکند.
- Cloud Functions (نسل دوم): از حساب سرویس Compute Engine پیشفرض استفاده میکند.
در کنسول Google Cloud، به IAM و سرپرست بروید.
روی نماد ویرایش مربوط به حساب سرویسی که میخواهید بهروزرسانی کنید کلیک کنید.
روی افزودن نقشی دیگر کلیک کنید.
"Service Account Token Creator"را در فیلتر جستجو تایپ کنید و آن را از نتایج انتخاب کنید.برای تأیید اعطای نقش، روی ذخیره کلیک کنید.
برای جزئیات بیشتر درباره این فرایند، به اسناد 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.
اگر برای شناسایی خودکار شناسه حساب سرویس به کیت توسعه نرمافزار متکی هستید، مطمئن شوید کد در محیط Google مدیریتشده با سرور فراداده مستقر شده باشد. درغیراینصورت، حتماً فایل JSON حساب سرویس یا شناسه حساب سرویس را در مقداردهی اولیه کیت توسعه نرمافزار مشخص کنید.