Firebase では、安全な JSON Web トークン (JWT) を使用してユーザーまたはデバイスを認証できるため、認証を完全に制御できます。これらのトークンをサーバーで生成し、クライアント デバイスに戻してから、それらを使用してsignInWithCustomToken()
メソッドを介して認証します。
これを実現するには、ユーザー名やパスワードなどのサインイン資格情報を受け入れるサーバー エンドポイントを作成し、資格情報が有効な場合はカスタム JWT を返す必要があります。サーバーから返されたカスタム JWT は、クライアント デバイスで Firebase ( iOS+ 、 Android 、 web ) で認証するために使用できます。認証されると、Firebase Realtime Database や Cloud Storage などの他の Firebase サービスにアクセスするときに、この ID が使用されます。さらに、JWT のコンテンツは、 Realtime Database ルールのauth
オブジェクトとCloud Storage セキュリティ ルールのrequest.auth
オブジェクトで利用できます。
Firebase Admin SDK を使用してカスタム トークンを作成するか、Firebase がネイティブでサポートしていない言語でサーバーが記述されている場合は、サードパーティの JWT ライブラリを使用できます。
あなたが始める前に
カスタム トークンは、署名に使用される秘密鍵が Google サービス アカウントに属する署名付き JWT です。カスタム トークンに署名するために Firebase Admin SDK で使用する Google サービス アカウントを指定するには、いくつかの方法があります。
- サービス アカウント JSON ファイルの使用-- この方法はどの環境でも使用できますが、サービス アカウント JSON ファイルをコードと一緒にパッケージ化する必要があります。サービス アカウントの JSON ファイルが外部に公開されないように、特別な注意を払う必要があります。
- Admin SDK にサービス アカウントを検出させる-- この方法は、Google Cloud Functions や App Engine など、Google が管理する環境で使用できます。 Google Cloud Console を使用して追加の権限を構成する必要がある場合があります。
- サービス アカウント ID の使用 -- Google が管理する環境で使用する場合、このメソッドは指定されたサービス アカウントのキーを使用してトークンに署名します。ただし、リモート Web サービスを使用するため、Google Cloud Console を介してこのサービス アカウントに追加の権限を構成する必要がある場合があります。
サービス アカウント JSON ファイルの使用
サービス アカウントの JSON ファイルには、サービス アカウントに対応するすべての情報 (RSA 秘密鍵を含む) が含まれます。 Firebase コンソールからダウンロードできます。サービス アカウントの JSON ファイルを使用して Admin SDK を初期化する方法の詳細については、 Admin SDK のセットアップ手順に従ってください。
この初期化方法は、幅広い Admin SDK 展開に適しています。また、リモート API 呼び出しを行わずに、Admin SDK がローカルでカスタム トークンを作成して署名できるようにします。このアプローチの主な欠点は、コードとともにサービス アカウントの JSON ファイルをパッケージ化する必要があることです。また、サービス アカウントの JSON ファイル内の秘密鍵は機密情報であるため、機密を保持するために特別な注意を払う必要があることに注意してください。具体的には、サービス アカウントの JSON ファイルをパブリック バージョン管理に追加することは控えてください。
Admin SDK がサービス アカウントを検出できるようにする
コードが Google によって管理されている環境にデプロイされている場合、Admin SDK はカスタム トークンに署名する手段を自動検出しようとすることができます。
コードが Java、Python、または Go の App Engine スタンダード環境にデプロイされている場合、Admin SDK はその環境にあるApp Identity サービスを使用してカスタム トークンに署名できます。 App Identity サービスは、Google App Engine によってアプリ用にプロビジョニングされたサービス アカウントを使用してデータに署名します。
コードが他のマネージド環境 (Google Cloud Functions、Google Compute Engine など) にデプロイされている場合、Firebase Admin SDK はローカルメタデータ サーバーからサービス アカウント ID 文字列を自動検出できます。検出されたサービス アカウント ID は、IAM サービスと組み合わせて使用され、リモートでトークンに署名します。
これらの署名方法を利用するには、SDK を Google アプリケーションのデフォルト認証情報で初期化し、サービス アカウント 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)
}
C#
FirebaseApp.Create();
同じコードをローカルでテストするには、サービス アカウントの JSON ファイルをダウンロードし、それを指すようにGOOGLE_APPLICATION_CREDENTIALS
環境変数を設定します。
Firebase Admin SDK がサービス アカウント ID 文字列を検出する必要がある場合は、コードが初めてカスタム トークンを作成するときに検出します。結果はキャッシュされ、後続のトークン署名操作で再利用されます。通常、自動検出されたサービス アカウント ID は、Google Cloud によって提供されるデフォルトのサービス アカウントの 1 つです。
明示的に指定されたサービス アカウント ID と同様に、自動検出されたサービス アカウント ID には、カスタム トークンの作成が機能するためにiam.serviceAccounts.signBlob
権限が必要です。 Google Cloud Console のIAM および管理セクションを使用して、デフォルトのサービス アカウントに必要な権限を付与する必要がある場合があります。詳細については、以下のトラブルシューティングのセクションを参照してください。
サービス アカウント ID の使用
アプリケーションのさまざまな部分間の一貫性を維持するために、Google が管理する環境での実行時にトークンの署名にキーが使用されるサービス アカウント ID を指定できます。これにより、IAM ポリシーがよりシンプルかつ安全になり、サービス アカウントの JSON ファイルをコードに含める必要がなくなります。
サービス アカウント ID は、 Google Cloud Consoleか、ダウンロードしたサービス アカウント JSON ファイルのclient_email
フィールドで確認できます。サービス アカウント ID は、 <client-id>@<project-id>.iam.gserviceaccount.com
という形式のメール アドレスです。 Firebase および Google Cloud プロジェクトでサービス アカウントを一意に識別します。
別のサービス アカウント ID を使用してカスタム トークンを作成するには、次のように 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)
}
C#
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.GetApplicationDefault(),
ServiceAccountId = "my-client-id@my-project-id.iam.gserviceaccount.com",
});
サービス アカウント ID は機密情報ではないため、公開されることは重要ではありません。ただし、指定されたサービス アカウントでカスタム トークンに署名するには、Firebase Admin SDK がリモート サービスを呼び出す必要があります。さらに、この呼び出しを行うために Admin SDK が使用しているサービス アカウント (通常は { {project-name}@appspot.gserviceaccount.com
) にiam.serviceAccounts.signBlob
権限があることも確認する必要があります。詳細については、以下のトラブルシューティングのセクションを参照してください。
Firebase Admin SDK を使用してカスタム トークンを作成する
Firebase Admin SDK には、カスタム トークンを作成するためのメソッドが組み込まれています。少なくともuid
を指定する必要があります。これは任意の文字列ですが、認証するユーザーまたはデバイスを一意に識別する必要があります。これらのトークンは 1 時間後に期限切れになります。
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)
C#
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)
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
予約済みのカスタム トークン名
クライアントでカスタム トークンを使用してサインインする
カスタム トークンを作成したら、それをクライアント アプリに送信する必要があります。クライアント アプリは、 signInWithCustomToken()
を呼び出して、カスタム トークンで認証します。
iOS+
Objective-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.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 version 8
firebase.auth().signInWithCustomToken(token)
.then((userCredential) => {
// Signed in
var user = userCredential.user;
// ...
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
Web version 9
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()
) など) と同様に、 Realtime Database ルールのauth
オブジェクトとCloud Storage セキュリティ ルールのrequest.auth
オブジェクトには、ユーザーの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 | 発行時 | UNIX エポックからの現在の時刻 (秒単位) |
exp | 有効期限 | トークンの有効期限が切れる UNIX エポックからの時間 (秒単位)。 iat より最大 3600 秒遅れる場合があります。注: これは、カスタム トークン自体の有効期限が切れる時間を制御するだけです。ただし、 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 が有効になっていません
トークンに署名するためのサービス アカウント 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.
Firebase Admin SDK は、 IAM APIを使用してトークンに署名します。このエラーは、現在 Firebase プロジェクトで IAM API が有効になっていないことを示しています。エラー メッセージのリンクを Web ブラウザーで開き、[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}.
これを解決する最も簡単な方法は、問題のサービス アカウント (通常は{project-name}@appspot.gserviceaccount.com
) に「サービス アカウント トークン作成者」IAM ロールを付与することです。
- Google Cloud Console で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.
SDK を使用してサービス アカウント ID を自動検出する場合は、コードがメタデータ サーバーを備えたマネージド Google 環境にデプロイされていることを確認してください。それ以外の場合は、SDK の初期化時にサービス アカウントの JSON ファイルまたはサービス アカウント ID を指定してください。