فایربیس با امکان احراز هویت کاربران یا دستگاهها با استفاده از توکنهای وب JSON (JWT) امن، کنترل کاملی بر احراز هویت به شما میدهد. شما این توکنها را روی سرور خود تولید میکنید، آنها را به دستگاه کلاینت ارسال میکنید و سپس از طریق متد signInWithCustomToken() از آنها برای احراز هویت استفاده میکنید.
برای دستیابی به این هدف، باید یک نقطه پایانی سرور ایجاد کنید که اعتبارنامههای ورود به سیستم - مانند نام کاربری و رمز عبور - را بپذیرد و در صورت معتبر بودن اعتبارنامهها، یک JWT سفارشی را برگرداند. JWT سفارشی برگردانده شده از سرور شما میتواند توسط یک دستگاه کلاینت برای احراز هویت با Firebase ( iOS+ ، Android ، web ) استفاده شود. پس از احراز هویت، این هویت هنگام دسترسی به سایر سرویسهای Firebase، مانند Firebase Realtime Database و Cloud Storage ، استفاده خواهد شد. علاوه بر این، محتویات JWT در شیء auth در Realtime Database Security Rules و شیء request.auth در Cloud Storage Security Rules شما در دسترس خواهد بود.
شما میتوانید با استفاده از Firebase Admin SDK یک توکن سفارشی ایجاد کنید، یا اگر سرور شما به زبانی نوشته شده است که Firebase به طور بومی از آن پشتیبانی نمیکند، میتوانید از یک کتابخانه JWT شخص ثالث استفاده کنید.
قبل از اینکه شروع کنی
توکنهای سفارشی، JWTهای امضا شدهای هستند که کلید خصوصی مورد استفاده برای امضا، متعلق به یک حساب سرویس گوگل است. روشهای مختلفی برای مشخص کردن حساب سرویس گوگل که باید توسط Firebase Admin SDK برای امضای توکنهای سفارشی استفاده شود، وجود دارد:
- استفاده از فایل JSON حساب کاربری سرویس -- این روش میتواند در هر محیطی استفاده شود، اما مستلزم آن است که شما یک فایل JSON حساب کاربری سرویس را همراه با کد خود بستهبندی کنید. باید دقت ویژهای شود تا اطمینان حاصل شود که فایل JSON حساب کاربری سرویس در معرض اشخاص ثالث قرار نمیگیرد.
- اجازه دادن به Admin SDK برای کشف یک حساب سرویس -- این روش میتواند در محیطهایی که توسط گوگل مدیریت میشوند مانند Google Cloud Functions و App Engine استفاده شود. ممکن است لازم باشد برخی مجوزهای اضافی را از طریق کنسول Google Cloud پیکربندی کنید.
- استفاده از شناسه حساب سرویس -- وقتی در یک محیط تحت مدیریت گوگل استفاده شود، این روش توکنها را با استفاده از کلید حساب سرویس مشخص شده امضا میکند. با این حال، از یک سرویس وب از راه دور استفاده میکند و ممکن است مجبور شوید مجوزهای اضافی را برای این حساب سرویس از طریق کنسول Google Cloud پیکربندی کنید.
استفاده از فایل JSON حساب کاربری سرویس
فایلهای JSON حساب کاربری سرویس شامل تمام اطلاعات مربوط به حسابهای کاربری سرویس (از جمله کلید خصوصی RSA) هستند. آنها را میتوان از کنسول Firebase دانلود کرد. برای اطلاعات بیشتر در مورد نحوه مقداردهی اولیه SDK مدیریت با یک فایل JSON حساب کاربری سرویس ، دستورالعملهای تنظیم SDK مدیریت را دنبال کنید.
این روش مقداردهی اولیه برای طیف گستردهای از استقرارهای SDK مدیریت مناسب است. همچنین، Admin SDK را قادر میسازد تا توکنهای سفارشی را به صورت محلی و بدون هیچ گونه فراخوانی API از راه دور ایجاد و امضا کند. اشکال اصلی این رویکرد این است که شما را ملزم میکند یک فایل JSON حساب سرویس را به همراه کد خود بستهبندی کنید. همچنین توجه داشته باشید که کلید خصوصی در یک فایل JSON حساب سرویس، اطلاعات حساسی است و باید در محرمانه نگه داشتن آن دقت ویژهای شود. به طور خاص، از اضافه کردن فایلهای JSON حساب سرویس به کنترل نسخه عمومی خودداری کنید.
اجازه دادن به Admin SDK برای کشف یک حساب سرویس
اگر کد شما در محیطی که توسط گوگل مدیریت میشود، مستقر شده باشد، Admin SDK میتواند به صورت خودکار روشی را برای امضای توکنهای سفارشی کشف کند:
اگر کد شما در محیط استاندارد App Engine برای جاوا، پایتون یا Go مستقر شده باشد، Admin SDK میتواند از سرویس App Identity موجود در آن محیط برای امضای توکنهای سفارشی استفاده کند. سرویس App Identity دادهها را با استفاده از یک حساب کاربری سرویس که توسط Google App Engine برای برنامه شما فراهم شده است، امضا میکند.
اگر کد شما در محیط مدیریتشدهی دیگری (مثلاً Google Cloud Functions، Google Compute Engine) پیادهسازی شده باشد، Firebase Admin SDK میتواند بهطور خودکار رشتهی شناسهی حساب سرویس را از سرور فرادادهی محلی کشف کند. سپس شناسهی حساب سرویس کشفشده همراه با سرویس IAM برای امضای توکنها از راه دور استفاده میشود.
برای استفاده از این روشهای امضا، SDK را با اعتبارنامههای پیشفرض برنامه Google مقداردهی اولیه کنید و رشته شناسه حساب سرویس را مشخص نکنید:
نود جی اس
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 برای اعطای مجوزهای لازم به حسابهای سرویس پیشفرض استفاده کنید. برای جزئیات بیشتر به بخش عیبیابی در زیر مراجعه کنید.
استفاده از شناسه حساب سرویس
برای حفظ هماهنگی بین بخشهای مختلف برنامهتان، میتوانید یک شناسه حساب سرویس مشخص کنید که کلیدهای آن برای امضای توکنها هنگام اجرا در یک محیط تحت مدیریت گوگل استفاده خواهد شد. این کار میتواند سیاستهای IAM را سادهتر و ایمنتر کند و از لزوم گنجاندن فایل JSON حساب سرویس در کد شما جلوگیری کند.
شناسه حساب سرویس را میتوان در کنسول Google Cloud یا در فیلد client_email فایل JSON حساب سرویس دانلود شده پیدا کرد. شناسههای حساب سرویس، آدرسهای ایمیلی هستند که قالب زیر را دارند: <client-id>@<project-id>.iam.gserviceaccount.com . آنها به طور منحصر به فرد حسابهای سرویس را در پروژههای Firebase و Google Cloud شناسایی میکنند.
برای ایجاد توکنهای سفارشی با استفاده از یک شناسه حساب سرویس جداگانه، SDK را مطابق شکل زیر مقداردهی اولیه کنید:
نود جی اس
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 ارائه دهید که میتواند هر رشتهای باشد، اما باید به طور منحصر به فرد کاربر یا دستگاهی را که احراز هویت میکنید، شناسایی کند. این توکنها پس از یک ساعت منقضی میشوند.
نود جی اس
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 در قوانین امنیتی شما در دسترس خواهد بود:
نود جی اس
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() با توکن سفارشی احراز هویت میکند:
آیاواس+
هدف-سی
[[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);
});
سی++
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 شخص ثالث
اگر بکاند شما به زبانی است که SDK رسمی مدیریت فایربیس ندارد، همچنان میتوانید توکنهای سفارشی را به صورت دستی ایجاد کنید. ابتدا، یک کتابخانه JWT شخص ثالث برای زبان خود پیدا کنید . سپس، از آن کتابخانه JWT برای ایجاد یک JWT که شامل ادعاهای زیر است استفاده کنید:
| ادعاهای توکن سفارشی | ||
|---|---|---|
alg | الگوریتم | "RS256" |
iss | صادرکننده | آدرس ایمیل حساب کاربری سرویس پروژه شما |
sub | موضوع | آدرس ایمیل حساب کاربری سرویس پروژه شما |
aud | مخاطب | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | صادر شده در زمان | زمان فعلی، بر حسب ثانیه از زمان آغاز یونیکس |
exp | زمان انقضا | مدت زمانی که توکن از زمان آغاز یونیکس (UNIX epoch) منقضی میشود (بر حسب ثانیه). این زمان میتواند حداکثر ۳۶۰۰ ثانیه دیرتر از iat باشد.توجه: این فقط زمانی را کنترل میکند که خود توکن سفارشی منقضی میشود. اما وقتی کاربری را با استفاده از signInWithCustomToken() وارد سیستم میکنید، او تا زمانی که جلسهاش نامعتبر شود یا کاربر از سیستم خارج شود، در دستگاه وارد سیستم باقی میماند. |
uid | شناسه منحصر به فرد کاربر وارد شده باید یک رشته باشد، بین ۱ تا ۱۲۸ کاراکتر، شامل همه. شناسههای uid کوتاهتر عملکرد بهتری ارائه میدهند. | |
claims (اختیاری) | ادعاهای سفارشی اختیاری که باید در قوانین امنیتی گنجانده شوند متغیرهای auth / request.auth | |
در اینجا چند نمونه پیادهسازی از نحوه ایجاد توکنهای سفارشی به زبانهای مختلفی که Firebase Admin SDK از آنها پشتیبانی نمیکند، آورده شده است:
پی اچ پی
استفاده از 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 فعال نیست
اگر برای امضای توکنها، شناسه حساب سرویس (service account ID) تعیین میکنید، ممکن است با خطایی مشابه خطای زیر مواجه شوید:
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.
کیت توسعه نرمافزار (SDK) مدیریت فایربیس از رابط برنامهنویسی کاربردی (API) IAM برای امضای توکنها استفاده میکند. این خطا نشان میدهد که رابط برنامهنویسی کاربردی IAM در حال حاضر برای پروژه فایربیس شما فعال نیست. لینک موجود در پیام خطا را در یک مرورگر وب باز کنید و روی دکمه «فعال کردن 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 "Service Account Token Creator" به حساب سرویس مورد نظر است که معمولاً {project-name}@appspot.gserviceaccount.com میباشد:
- صفحه IAM و ادمین را در کنسول Google Cloud باز کنید.
- پروژه خود را انتخاب کنید و روی «ادامه» کلیک کنید.
- روی نماد ویرایش مربوط به حساب سرویسی که میخواهید بهروزرسانی کنید، کلیک کنید.
- روی «افزودن نقش دیگر» کلیک کنید.
- عبارت «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.
اگر برای کشف خودکار شناسه حساب سرویس به SDK متکی هستید، مطمئن شوید که کد در یک محیط مدیریتشده گوگل با یک سرور فراداده مستقر شده است. در غیر این صورت، حتماً فایل JSON حساب سرویس یا شناسه حساب سرویس را در مقداردهی اولیه SDK مشخص کنید.