Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.

Tạo mã thông báo tùy chỉnh

Firebase cung cấp cho bạn quyền kiểm soát hoàn toàn việc xác thực bằng cách cho phép bạn xác thực người dùng hoặc thiết bị bằng cách sử dụng Mã thông báo web JSON (JWT) an toàn. Bạn tạo những thẻ trên máy chủ của bạn, vượt qua chúng trở lại với một thiết bị client, và sau đó sử dụng chúng để xác thực thông qua signInWithCustomToken() phương pháp.

Để đạt được điều này, bạn phải tạo một điểm cuối máy chủ chấp nhận thông tin đăng nhập — chẳng hạn như tên người dùng và mật khẩu — và nếu thông tin xác thực hợp lệ, trả về JWT tùy chỉnh. JWT tùy chỉnh trở về từ máy chủ của bạn sau đó có thể được sử dụng bởi một thiết bị khách hàng để xác thực với căn cứ hỏa lực ( iOS , Android , web ). Sau khi được xác thực, danh tính này sẽ được sử dụng khi truy cập các dịch vụ Firebase khác, chẳng hạn như Cơ sở dữ liệu thời gian thực của Firebase và Lưu trữ đám mây. Bên cạnh đó, các nội dung của JWT sẽ có mặt tại các auth đối tượng trong bạn Realtime Nội quy Cơ sở dữ liệu và các request.auth đối tượng trong bạn Security Rules lưu trữ đám mây .

Bạn có thể tạo mã thông báo tùy chỉnh bằng SDK quản trị Firebase hoặc bạn có thể sử dụng thư viện JWT của bên thứ ba nếu máy chủ của bạn được viết bằng ngôn ngữ mà Firebase không hỗ trợ.

Trước khi bắt đầu

Mã thông báo tùy chỉnh là JWT đã ký trong đó khóa cá nhân được sử dụng để ký thuộc về tài khoản dịch vụ của Google. Có một số cách để chỉ định tài khoản dịch vụ của Google sẽ được SDK quản trị viên Firebase sử dụng để ký mã thông báo tùy chỉnh:

  • Sử dụng một tệp JSON tài khoản dịch vụ - Phương pháp này có thể được sử dụng trong bất kỳ môi trường, nhưng đòi hỏi bạn phải đóng gói một tệp JSON tài khoản dịch vụ cùng với mã của bạn. Cần phải đặc biệt chú ý để đảm bảo rằng tệp JSON của tài khoản dịch vụ không bị lộ ra bên ngoài.
  • Cho SDK quản trị phát hiện ra một tài khoản dịch vụ - Phương pháp này có thể được sử dụng trong các môi trường do Google quản lý chẳng hạn như Google Cloud Chức năng và App Engine. Bạn có thể phải định cấu hình một số quyền bổ sung thông qua Google Cloud Console.
  • Sử dụng một ID tài khoản dịch vụ - Khi được sử dụng trong một môi trường Google quản lý phương pháp này sẽ ký thẻ sử dụng khóa tài khoản dịch vụ của quy định. Tuy nhiên, nó sử dụng một dịch vụ web từ xa và bạn có thể phải định cấu hình các quyền bổ sung cho tài khoản dịch vụ này thông qua Google Cloud Console.

Sử dụng tệp JSON của tài khoản dịch vụ

Tệp JSON của tài khoản dịch vụ chứa tất cả thông tin tương ứng với tài khoản dịch vụ (bao gồm cả khóa riêng RSA). Bạn có thể tải chúng xuống từ bảng điều khiển Firebase. Thực hiện theo các SDK quản trị thiết lập các hướng dẫn để biết thêm thông tin về làm thế nào để khởi tạo SDK quản trị với một tệp JSON tài khoản dịch vụ.

Phương pháp khởi chạy này phù hợp với nhiều loại triển khai SDK dành cho quản trị viên. Ngoài ra, nó cho phép SDK quản trị viên tạo và ký mã thông báo tùy chỉnh cục bộ mà không cần thực hiện bất kỳ lệnh gọi API từ xa nào. Hạn chế chính của phương pháp này là nó yêu cầu bạn đóng gói tệp JSON tài khoản dịch vụ cùng với mã của bạn. Cũng lưu ý rằng khóa riêng tư trong tệp JSON của tài khoản dịch vụ là thông tin nhạy cảm và phải đặc biệt lưu ý để giữ bí mật. Cụ thể, không thêm tệp JSON của tài khoản dịch vụ vào quyền kiểm soát phiên bản công khai.

Cho phép SDK quản trị viên khám phá tài khoản dịch vụ

Nếu mã của bạn được triển khai trong môi trường do Google quản lý, SDK quản trị viên có thể cố gắng tự động phát hiện ra phương tiện để ký mã thông báo tùy chỉnh:

  • Nếu mã của bạn được triển khai trong môi trường tiêu chuẩn App Engine cho Java, Python hay Go, SDK quản trị có thể sử dụng các dịch vụ ứng dụng nhận dạng hiện nay trong môi trường đó để ký thẻ tùy chỉnh. Dịch vụ Nhận dạng ứng dụng ký dữ liệu bằng tài khoản dịch vụ được Google App Engine cấp cho ứng dụng của bạn.

  • Nếu mã của bạn được triển khai trong một số môi trường khác quản lý (ví dụ như chức năng Google Cloud, Google Compute Engine), các căn cứ hỏa lực SDK quản trị có thể tự động phát hiện ra một chuỗi ID tài khoản dịch vụ từ các địa phương máy chủ siêu dữ liệu . Sau đó, ID tài khoản dịch vụ được phát hiện sẽ được sử dụng cùng với dịch vụ IAM để ký mã thông báo từ xa.

Để sử dụng các phương pháp ký này, hãy khởi tạo SDK bằng thông tin đăng nhập Mặc định của Ứng dụng Google và không chỉ định chuỗi ID tài khoản dịch vụ:

Node.js

admin.initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Đi

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

NS#

FirebaseApp.Create();

Để kiểm tra các mã tương tự tại địa phương, tải về một tập tin JSON tài khoản dịch vụ và thiết lập GOOGLE_APPLICATION_CREDENTIALS biến môi trường để trỏ đến nó.

Nếu SDK quản trị Firebase phải phát hiện ra chuỗi ID tài khoản dịch vụ, thì nó sẽ làm như vậy khi mã của bạn tạo mã thông báo tùy chỉnh lần đầu tiên. Kết quả được lưu vào bộ nhớ đệm và sử dụng lại cho các hoạt động ký mã thông báo tiếp theo. ID tài khoản dịch vụ được tự động phát hiện thường là một trong những tài khoản dịch vụ mặc định do Google Cloud cung cấp:

Cũng giống như với ID tài khoản dịch vụ được quy định một cách rõ ràng, ID tài khoản dịch vụ tự động discoverd phải có iam.serviceAccounts.signBlob phép cho tùy chỉnh tạo thẻ để làm việc. Bạn có thể phải sử dụng IAM và quản trị phần của Google Cloud Console để cấp các tài khoản dịch vụ mặc định các quyền cần thiết. Xem phần khắc phục sự cố bên dưới để biết thêm chi tiết.

Sử dụng ID tài khoản dịch vụ

Để duy trì tính nhất quán giữa các phần khác nhau của ứng dụng, bạn có thể chỉ định ID tài khoản dịch vụ có các khóa sẽ được sử dụng để ký mã thông báo khi chạy trong môi trường do Google quản lý. Điều này có thể làm cho các chính sách IAM đơn giản hơn và an toàn hơn, đồng thời tránh phải bao gồm tệp JSON của tài khoản dịch vụ trong mã của bạn.

ID tài khoản dịch vụ có thể được tìm thấy trong Google Cloud Bảng điều khiển , hoặc trong client_email lĩnh vực dịch vụ tải tập tin tài khoản JSON. ID tài khoản dịch vụ là các địa chỉ email có định dạng sau: <client-id>@<project-id>.iam.gserviceaccount.com . Chúng xác định duy nhất các tài khoản dịch vụ trong các dự án Firebase và Google Cloud.

Để tạo mã thông báo tùy chỉnh bằng cách sử dụng ID tài khoản dịch vụ riêng biệt, hãy khởi tạo SDK như được hiển thị bên dưới:

Node.js

admin.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)

Đi

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

NS#

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

ID tài khoản dịch vụ không phải là thông tin nhạy cảm và do đó việc hiển thị của chúng là không quan trọng. Tuy nhiên, để ký mã thông báo tùy chỉnh bằng tài khoản dịch vụ được chỉ định, SDK quản trị viên Firebase phải gọi một dịch vụ từ xa. Hơn nữa, bạn cũng phải đảm bảo rằng tài khoản dịch vụ SDK quản trị được sử dụng để thực hiện cuộc gọi này -usually {project-name}@appspot.gserviceaccount.com - có iam.serviceAccounts.signBlob phép . Xem phần khắc phục sự cố bên dưới để biết thêm chi tiết.

Tạo mã thông báo tùy chỉnh bằng SDK quản trị Firebase

SDK quản trị Firebase có một phương pháp tích hợp để tạo mã thông báo tùy chỉnh. Ở mức tối thiểu, bạn cần phải cung cấp một uid , mà có thể là bất kỳ chuỗi nhưng duy nhất nên xác định người dùng hoặc thiết bị bạn đang chứng thực. Các mã thông báo này sẽ hết hạn sau một giờ.

Node.js

const uid = 'some-uid';

admin
  .auth()
  .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)

Đi

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)

NS#

var uid = "some-uid";

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

Bạn cũng có thể tùy ý chỉ định các xác nhận quyền sở hữu bổ sung được đưa vào mã thông báo tùy chỉnh. Ví dụ, dưới đây, một premiumAccount lĩnh vực đã được thêm vào các thẻ tùy chỉnh, trong đó sẽ có sẵn trong các auth / request.auth đối tượng trong quy bảo mật của bạn:

Node.js

const userId = 'some-uid';
const additionalClaims = {
  premiumAccount: true,
};

admin
  .auth()
  .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)

Đi

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)

NS#

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

Đăng nhập bằng cách sử dụng mã thông báo tùy chỉnh trên máy khách

Sau khi tạo mã thông báo tùy chỉnh, bạn nên gửi mã đó đến ứng dụng khách của mình. Các xác thực ứng dụng khách hàng với các tùy chỉnh mã thông báo là gọi signInWithCustomToken() :

iOS

Objective-C
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Nhanh
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);
                }
            }
        });

Đoàn kết

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.FirebaseUser newUser = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      newUser.DisplayName, newUser.UserId);
});

C ++

firebase::Future<firebase::auth::User*> 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;
    // ...
  });

Nếu xác thực thành công, người dùng của bạn sẽ được hiện đã đăng nhập vào ứng dụng của khách hàng của bạn với tài khoản theo quy định của uid bao gồm trong thẻ tùy chỉnh. Nếu tài khoản đó không tồn tại trước đó, một bản ghi cho người dùng đó sẽ được tạo.

Trong cùng một cách như với khác đăng nhập phương pháp (chẳng hạn như signInWithEmailAndPassword()signInWithCredential() ) các auth đối tượng trong bạn Realtime Nội quy Cơ sở dữ liệu và các request.auth đối tượng trong bạn Security Rules Cloud Storage sẽ được áp dụng với người dùng uid . Trong trường hợp này, uid sẽ là một trong những mà bạn chỉ định khi tạo thẻ tùy chỉnh.

Quy tắc cơ sở dữ liệu

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

Quy tắc lưu trữ

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

Nếu mã thông báo tùy chỉnh chứa tuyên bố bổ sung, họ có thể được tham chiếu tắt của auth.token (căn cứ hỏa lực Realtime Cơ sở dữ liệu) hoặc request.auth.token (Cloud Storage) đối tượng trong quy tắc của bạn:

Quy tắc cơ sở dữ liệu

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

Quy tắc lưu trữ

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

Tạo mã thông báo tùy chỉnh bằng thư viện JWT của bên thứ ba

Nếu chương trình phụ trợ của bạn bằng ngôn ngữ không có SDK quản trị viên Firebase chính thức, bạn vẫn có thể tạo mã thông báo tùy chỉnh theo cách thủ công. Thứ nhất, tìm thấy một thư viện JWT của bên thứ ba đối với ngôn ngữ của bạn. Sau đó, sử dụng thư viện JWT đó để tạo ra một JWT bao gồm các yêu cầu sau:

Tuyên bố Mã thông báo Tùy chỉnh
alg Thuật toán "RS256"
iss Người phát hành Địa chỉ email tài khoản dịch vụ của dự án của bạn
sub Chủ thể Địa chỉ email tài khoản dịch vụ của dự án của bạn
aud Thính giả "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Được cấp-tại thời điểm Thời gian hiện tại, tính bằng giây kể từ kỷ nguyên UNIX
exp Thời gian hết hạn Thời gian, tính bằng giây kể từ kỷ nguyên UNIX, tại thời điểm mã thông báo hết hạn. Nó có thể tối đa là 3600 giây muộn hơn iat .
Lưu ý: đây chỉ kiểm soát thời điểm tùy chỉnh thẻ riêng của mình hết hạn. Nhưng một khi bạn đăng một người dùng trong việc sử dụng signInWithCustomToken() , họ sẽ vẫn đăng nhập vào thiết bị cho đến khi phiên của họ là vô hiệu hoặc dùng đăng xuất khỏi.
uid Số nhận dạng duy nhất của người dùng đã đăng nhập phải là một chuỗi, dài từ 1-36 ký tự
claims (không bắt buộc) Tuyên bố tùy chỉnh tùy chọn để đưa vào các quy tắc an auth / request.auth biến

Dưới đây là một số cách triển khai ví dụ về cách tạo mã thông báo tùy chỉnh bằng nhiều ngôn ngữ khác nhau mà SDK quản trị viên Firebase không hỗ trợ:

PHP

Sử dụng 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

Sử dụng 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

Sau khi bạn tạo mã thông báo tùy chỉnh, hãy gửi mã đó đến ứng dụng khách của bạn để sử dụng nhằm xác thực với Firebase. Xem các mẫu mã ở trên để biết cách thực hiện việc này.

Xử lý sự cố

Phần này nêu một số vấn đề phổ biến mà nhà phát triển có thể gặp phải khi tạo mã thông báo tùy chỉnh và cách giải quyết chúng.

API IAM không được bật

Nếu bạn đang chỉ định ID tài khoản dịch vụ để ký mã thông báo, bạn có thể gặp lỗi tương tự như sau:

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.

Các căn cứ hỏa lực SDK quản trị sử dụng các API IAM để tokens dấu. Lỗi này cho biết rằng API IAM hiện không được bật cho dự án Firebase của bạn. Mở liên kết trong thông báo lỗi trong trình duyệt web và nhấp vào nút "Bật API" để kích hoạt nó cho dự án của bạn.

Tài khoản dịch vụ không có quyền bắt buộc

Nếu tài khoản dịch vụ các căn cứ hỏa lực SDK quản trị đang chạy như không có iam.serviceAccounts.signBlob phép, bạn có thể nhận được một thông báo lỗi như sau:

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

Cách đơn giản nhất để giải quyết này là để cấp "Tài khoản Dịch vụ Mã Creator" vai trò IAM vào tài khoản dịch vụ liên quan, thường {project-name}@appspot.gserviceaccount.com :

  1. Mở IAM và quản trị trang trong Google Cloud Console.
  2. Chọn dự án của bạn và nhấp vào "Tiếp tục".
  3. Nhấp vào biểu tượng chỉnh sửa tương ứng với tài khoản dịch vụ bạn muốn cập nhật.
  4. Nhấp vào "Thêm một vai trò khác".
  5. Nhập "Trình tạo mã thông báo tài khoản dịch vụ" vào bộ lọc tìm kiếm và chọn từ kết quả.
  6. Nhấp vào "Lưu" để xác nhận việc cấp vai trò.

Tham khảo tài liệu hướng dẫn IAM để biết thêm chi tiết về quá trình này, hoặc tìm hiểu làm thế nào để làm vai trò cập nhật bằng cách sử dụng công cụ dòng lệnh gcloud.

Không xác định được tài khoản dịch vụ

Nếu bạn nhận được thông báo lỗi tương tự như sau, thì SDK quản trị viên Firebase chưa được khởi chạy đúng cách.

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

Nếu bạn đang dựa vào SDK để tự động phát hiện ID tài khoản dịch vụ, hãy đảm bảo mã được triển khai trong môi trường được Google quản lý với máy chủ siêu dữ liệu. Nếu không, hãy đảm bảo chỉ định tệp JSON tài khoản dịch vụ hoặc ID tài khoản dịch vụ khi khởi tạo SDK.