Firebase memberi Anda kontrol penuh terhadap autentikasi melalui kemampuan mengautentikasi pengguna atau perangkat menggunakan Token Web JSON (JWT) yang aman. Anda dapat membuat token ini di server, meneruskannya kembali ke perangkat klien, lalu menggunakannya untuk melakukan autentikasi dengan metode signInWithCustomToken()
.
Untuk melakukannya, Anda harus membuat endpoint server yang menerima kredensial
login, seperti nama pengguna dan sandi. Jika kredensial
valid, endpoint server akan menampilkan JWT kustom. JWT kustom yang ditampilkan dari server Anda kemudian
dapat digunakan oleh perangkat klien untuk melakukan autentikasi menggunakan Firebase
(iOS+, Android,
web). Setelah diautentikasi, identitas ini akan
digunakan saat mengakses layanan Firebase lain, seperti Firebase Realtime Database
dan Cloud Storage. Selanjutnya, konten JWT akan
tersedia di objek auth
di bagian
Realtime Database Security Rules dan
request.auth
di bagian
Cloud Storage Security Rules
Anda dapat membuat token kustom dengan Firebase Admin SDK, atau Anda dapat menggunakan library JWT pihak ketiga jika server ditulis dalam bahasa yang tidak didukung oleh Firebase secara default.
Sebelum memulai
Token kustom adalah JWT bertanda tangan, dan kunci pribadi yang digunakan untuk menandatanganinya adalah milik akun layanan Google. Ada beberapa cara menentukan akun layanan Google yang harus digunakan oleh Firebase Admin SDK untuk menandatangani token kustom:
- Menggunakan file JSON akun layanan -- Metode ini dapat digunakan di lingkungan apa pun, tetapi mengharuskan Anda untuk mengemas file JSON akun layanan bersama dengan kode Anda. Anda harus sangat berhati-hati saat memastikan agar file JSON akun layanan tidak terungkap ke pihak eksternal.
- Mengizinkan Admin SDK menemukan akun layanan -- Metode ini dapat digunakan dalam lingkungan yang dikelola oleh Google, seperti Google Cloud Functions dan App Engine. Anda mungkin harus mengonfigurasi sejumlah izin tambahan melalui Google Cloud console.
- Menggunakan ID akun layanan -- Ketika digunakan di lingkungan yang dikelola Google, metode ini akan menandatangani token menggunakan kunci akun layanan yang ditentukan. Namun, metode ini menggunakan layanan web jarak jauh dan Anda mungkin harus mengonfigurasi sejumlah izin tambahan untuk akun layanan ini melalui Google Cloud console.
Menggunakan file JSON akun layanan
File JSON akun layanan berisi semua informasi yang terkait dengan akun layanan (termasuk kunci pribadi RSA). File JSON tersebut dapat didownload dari Firebase console. Ikuti petunjuk penyiapan Admin SDK untuk mengetahui informasi lebih lanjut terkait cara menginisialisasi Admin SDK dengan file JSON akun layanan.
Metode inisialisasi ini cocok untuk berbagai deployment Admin SDK. Selain itu, dengan metode ini, Admin SDK juga dapat membuat dan menandatangani token kustom secara lokal, tanpa melakukan panggilan API jarak jauh. Kekurangan utama dari pendekatan ini adalah Anda harus mengemas file JSON akun layanan bersama dengan kode Anda. Perlu diperhatikan juga bahwa kunci pribadi dalam file JSON akun layanan adalah informasi sensitif, dan Anda harus sangat berhati-hati dalam menjaga kerahasiaannya. Secara khusus, jangan tambahkan file JSON akun layanan ke kontrol versi publik.
Mengizinkan Admin SDK menemukan akun layanan
Jika kode Anda di-deploy di lingkungan yang dikelola oleh Google, Admin SDK dapat mencoba untuk secara otomatis menemukan cara menandatangani token kustom:
Jika kode Anda di-deploy di lingkungan standar App Engine untuk Java, Python, atau Go, Admin SDK dapat menggunakan layanan App Identity yang ada di lingkungan tersebut untuk menandatangani token kustom. Layanan App Identity menandatangani data menggunakan akun layanan yang disediakan untuk aplikasi Anda oleh Google App Engine.
Jika kode Anda di-deploy di suatu lingkungan terkelola lain (misalnya, Google Cloud Functions, Google Compute Engine), Firebase Admin SDK dapat menemukan string ID akun layanan secara otomatis dari server metadata lokal. ID akun layanan yang ditemukan tersebut kemudian digunakan bersama dengan layanan IAM untuk menandatangani token dari jarak jauh.
Agar dapat menggunakan metode penandatanganan ini, lakukan inisialisasi SDK dengan Kredensial Default Aplikasi Google dan jangan tentukan string ID akun layanan:
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();
Untuk menguji kode yang sama secara lokal, download file JSON akun layanan lalu tetapkan variabel lingkungan GOOGLE_APPLICATION_CREDENTIALS
untuk diarahkan kepadanya.
Jika Firebase Admin SDK harus menemukan string ID akun layanan, hal itu akan dilakukan saat kode Anda membuat token kustom untuk pertama kalinya. Hasilnya disimpan dalam cache dan digunakan kembali untuk operasi penandatanganan token berikutnya. ID akun layanan yang ditemukan secara otomatis biasanya adalah salah satu akun layanan default yang disediakan oleh Google Cloud:
Sama seperti ID akun layanan yang ditentukan secara eksplisit, ID akun layanan yang
ditemukan secara otomatis harus memiliki izin iam.serviceAccounts.signBlob
agar
pembuatan token kustom dapat berjalan dengan baik. Anda mungkin harus menggunakan bagian
IAM and admin di
Google Cloud console untuk memberikan izin yang diperlukan
akun layanan default. Baca bagian pemecahan masalah di bawah untuk mengetahui informasi lebih lanjut.
Menggunakan ID akun layanan
Untuk menjaga konsistensi antarbagian aplikasi, Anda dapat menentukan ID akun layanan yang kuncinya akan digunakan untuk menandatangani token ketika berjalan di lingkungan yang dikelola Google. Tindakan ini dapat membuat kebijakan IAM lebih simpel dan lebih aman. Selain itu, file JSON akun layanan tidak perlu disertakan dalam kode Anda.
ID akun layanan dapat ditemukan di
Google Cloud console,
atau dalam kolom client_email
dari file JSON akun layanan yang didownload.
ID akun layanan adalah alamat email yang memiliki format berikut:
<client-id>@<project-id>.iam.gserviceaccount.com
. ID tersebut secara unik mengidentifikasi
akun layanan dalam project Firebase dan Google Cloud.
Untuk membuat token kustom menggunakan ID akun layanan terpisah, lakukan inisialisasi SDK seperti yang ditunjukkan di bawah ini:
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",
});
ID akun layanan bukan merupakan informasi sensitif. Jadi, jika ID ini terungkap, tidak ada konsekuensinya. Namun, untuk menandatangani token kustom dengan akun layanan yang ditentukan, Firebase Admin SDK harus memanggil layanan jarak jauh.
Selain itu, Anda juga harus memastikan bahwa akun layanan yang digunakan oleh Admin SDK untuk melakukan panggilan ini,
biasanya {project-name}@appspot.gserviceaccount.com
, memiliki izin iam.serviceAccounts.signBlob
.
Baca bagian pemecahan masalah di bawah untuk mengetahui informasi lebih lanjut.
Membuat token kustom menggunakan Firebase Admin SDK
Firebase Admin SDK memiliki metode bawaan untuk membuat token kustom. Setidaknya, Anda perlu menyediakan uid
, yang dapat berupa string apa pun, tetapi harus
secara unik mengidentifikasi pengguna atau perangkat yang diautentikasi. Token ini tidak akan berlaku lagi setelah satu jam.
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
Jika diinginkan, Anda juga bisa menetapkan klaim tambahan untuk disertakan dalam token kustom. Pada contoh di bawah ini, kolom premiumAccount
telah ditambahkan ke token kustom, yang akan tersedia di objek auth
/request.auth
dalam Security Rules:
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
Nama token kustom yang dicadangkan
Login menggunakan token kustom pada klien
Setelah membuat token kustom, Anda harus mengirimkannya ke aplikasi klien.
Aplikasi klien akan melakukan autentikasi dengan token kustom dengan memanggil
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;
// ...
});
Jika autentikasi berhasil, pengguna akan login ke
aplikasi klien Anda, dan akun yang ditentukan oleh uid
disertakan dalam token
kustom. Jika akun tersebut sebelumnya tidak ada, data untuk pengguna tersebut akan
dibuat.
Dengan cara yang sama seperti metode login lainnya (seperti
signInWithEmailAndPassword()
dan signInWithCredential()
), objek auth
di bagian Realtime Database Security Rules dan
objek request.auth
di bagian
Cloud Storage Security Rules akan
diisi dengan uid
pengguna. Dalam hal ini, uid
adalah yang
Anda tentukan saat membuat token kustom.
Aturan Database
{
"rules": {
"adminContent": {
".read": "auth.uid === 'some-uid'"
}
}
}
Aturan Storage
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";
}
}
}
Jika token kustom berisi klaim tambahan, klaim tersebut dapat dirujuk dari
objek auth.token
(Firebase Realtime Database) atau request.auth.token
(Cloud Storage) dalam aturan Anda:
Aturan Database
{
"rules": {
"premiumContent": {
".read": "auth.token.premiumAccount === true"
}
}
}
Aturan Storage
service firebase.storage {
match /b/<your-firebase-storage-bucket>/o {
match /premiumContent/{filename} {
allow read, write: if request.auth.token.premiumAccount == true;
}
}
}
Membuat token kustom menggunakan library JWT pihak ketiga
Jika backend Anda menggunakan bahasa yang tidak memiliki Firebase Admin SDK resmi, Anda masih dapat membuat token kustom secara manual. Pertama, temukan library JWT pihak ketiga untuk bahasa Anda. Kemudian, gunakan library JWT tersebut untuk membuat JWT yang mencakup klaim berikut:
Klaim Token Kustom | ||
---|---|---|
alg |
Algoritme | "RS256" |
iss |
Penerbit | Alamat email akun layanan project Anda |
sub |
Subjek | Alamat email akun layanan project Anda |
aud |
Audiens | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Waktu penerbitan | Waktu saat ini, dalam satuan detik sejak epoch UNIX |
exp |
Waktu habis masa berlaku |
Waktu saat token sudah tidak berlaku lagi, dalam satuan detik sejak epoch UNIX. Waktu ini bisa mencapai maksimum 3600 detik lebih lama daripada iat .
Catatan: ini hanya mengontrol kapan token kustom berhenti berlaku. Namun, setelah Anda memproses login menggunakan signInWithCustomToken() , pengguna akan tetap login di perangkatnya hingga validitas sesi berakhir atau pengguna tersebut logout.
|
uid |
ID unik dari pengguna yang login harus berupa string dengan panjang 1-128 karakter. uid yang lebih singkat menawarkan performa yang lebih baik.
|
|
claims (opsional) |
Klaim kustom opsional yang akan disertakan di variabel auth /request.auth Security Rules
|
Berikut beberapa contoh implementasi mengenai cara membuat token kustom dalam berbagai bahasa yang tidak didukung Firebase Admin SDK:
PHP
Menggunakan 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
Menggunakan 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
Setelah token kustom dibuat, kirimkan token tersebut ke aplikasi klien Anda agar digunakan untuk melakukan autentikasi dengan Firebase. Lihat contoh kode di atas untuk mengetahui cara melakukannya.
Pemecahan masalah
Bagian ini berisi penjelasan mengenai beberapa masalah umum yang mungkin dihadapi developer saat membuat token kustom, serta cara menyelesaikannya.
IAM API tidak diaktifkan
Jika menentukan ID akun layanan untuk menandatangani token, Anda mungkin akan mendapatkan error yang serupa dengan yang berikut:
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 menggunakan IAM API untuk menandatangani token. Error ini menunjukkan bahwa IAM API saat ini tidak diaktifkan untuk project Firebase Anda. Buka link dalam pesan error di browser web, lalu klik tombol "Enable API" guna mengaktifkannya untuk project Anda.
Akun layanan tidak memiliki izin yang diperlukan
Jika akun layanan yang dijalankan Firebase Admin SDK tidak memiliki
izin iam.serviceAccounts.signBlob
, Anda mungkin akan mendapatkan pesan error seperti berikut:
Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/-/serviceAccounts/{your-service-account-id}.
Cara termudah untuk menyelesaikan masalah ini adalah dengan memberikan peran
IAM "Service Account Token Creator" ke akun layanan yang bersangkutan, biasanya
{project-name}@appspot.gserviceaccount.com
:
- Buka halaman IAM dan admin di Google Cloud console.
- Pilih project Anda lalu klik "Lanjutkan".
- Klik ikon edit yang sesuai dengan akun layanan yang ingin Anda perbarui.
- Klik "Add Another Role".
- Ketik "Service Account Token Creator" ke dalam filter penelusuran, lalu pilih dari hasilnya.
- Klik "Simpan" untuk mengonfirmasi pemberian peran.
Baca dokumentasi IAM untuk mengetahui informasi lebih lanjut tentang proses ini, atau pelajari cara mengubah peran menggunakan alat command line gcloud.
Gagal menentukan akun layanan
Jika Anda mendapatkan pesan error yang mirip dengan yang berikut ini, berarti Firebase Admin SDK belum diinisialisasi dengan benar.
Failed to determine service account ID. Initialize the SDK with service account credentials or specify a service account ID with iam.serviceAccounts.signBlob permission.
Jika Anda mengandalkan SDK untuk menemukan ID akun layanan secara otomatis, pastikan kode di-deploy di lingkungan yang dikelola Google dengan server metadata. Jika tidak, pastikan untuk menentukan file JSON akun layanan atau ID akun layanan saat melakukan inisialisasi SDK.