Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

カスタムトークンを作成する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Firebaseを使用すると、安全なJSON Webトークン(JWT)を使用してユーザーまたはデバイスを認証できるため、認証を完全に制御できます。これらのトークンをサーバー上で生成し、クライアントデバイスに戻し、それらを使用してsignInWithCustomToken()メソッドを介して認証します。

これを実現するには、ユーザー名やパスワードなどのサインインクレデンシャルを受け入れるサーバーエンドポイントを作成する必要があります。クレデンシャルが有効な場合は、カスタムJWTを返します。サーバーから返されたカスタムJWTは、クライアントデバイスでFirebase( iOS +AndroidWeb )で認証するために使用できます。認証されると、このIDは、FirebaseRealtimeDatabaseやCloudStorageなどの他のFirebaseサービスにアクセスするときに使用されます。さらに、JWTのコンテンツは、リアルタイムデータベースルールauthオブジェクトと、クラウドストレージセキュリティルールrequest.authオブジェクトで利用できます。

Firebase Admin SDKを使用してカスタムトークンを作成できます。サーバーがFirebaseでネイティブにサポートされていない言語で記述されている場合は、サードパーティのJWTライブラリを使用できます。

あなたが始める前に

カスタムトークンは、署名に使用される秘密鍵がGoogleサービスアカウントに属する署名済みJWTです。カスタムトークンに署名するためにFirebaseAdminSDKで使用する必要があるGoogleサービスアカウントを指定する方法はいくつかあります。

  • サービスアカウントのJSONファイルの使用-このメソッドはどの環境でも使用できますが、コードと一緒にサービスアカウントのJSONファイルをパッケージ化する必要があります。サービスアカウントのJSONファイルが外部に公開されないように特別な注意を払う必要があります。
  • Admin SDKにサービスアカウントを検出させる-この方法は、GoogleCloudFunctionsやAppEngineなどのGoogleが管理する環境で使用できます。 GoogleCloudConsoleを介して追加の権限を設定する必要がある場合があります。
  • サービスアカウントIDの使用-Googleが管理する環境で使用する場合、このメソッドは指定されたサービスアカウントのキーを使用してトークンに署名します。ただし、リモートWebサービスを使用するため、GoogleCloudConsoleを介してこのサービスアカウントの追加の権限を構成する必要がある場合があります。

サービスアカウントのJSONファイルを使用する

サービスアカウントのJSONファイルには、サービスアカウントに対応するすべての情報(RSA秘密鍵を含む)が含まれています。 Firebaseコンソールからダウンロードできます。サービスアカウントのJSONファイルを使用してAdminSDKを初期化する方法の詳細については、 AdminSDKのセットアップ手順に従ってください。

この初期化方法は、さまざまなAdminSDKの展開に適しています。また、Admin SDKは、リモートAPI呼び出しを行わずに、ローカルでカスタムトークンを作成して署名することができます。このアプローチの主な欠点は、サービスアカウントのJSONファイルをコードと一緒にパッケージ化する必要があることです。また、サービスアカウントのJSONファイルの秘密鍵は機密情報であり、機密を保持するために特別な注意を払う必要があることに注意してください。具体的には、サービスアカウントのJSONファイルをパブリックバージョン管理に追加しないでください。

AdminSDKにサービスアカウントを検出させる

コードがGoogleによって管理されている環境にデプロイされている場合、AdminSDKはカスタムトークンに署名する手段を自動検出しようとする可能性があります。

  • コードがJava、Python、またはGoのApp Engine標準環境にデプロイされている場合、AdminSDKはその環境に存在するAppIdentityサービスを使用してカスタムトークンに署名できます。 App Identityサービスは、GoogleAppEngineによってアプリにプロビジョニングされたサービスアカウントを使用してデータに署名します。

  • コードが他の管理対象環境(Google Cloud Functions、Google Compute Engineなど)にデプロイされている場合、FirebaseAdminSDKはローカルメタデータサーバーからサービスアカウントID文字列を自動検出できます。検出されたサービスアカウントIDは、IAMサービスと組み合わせて使用​​され、トークンにリモートで署名します。

これらの署名方法を利用するには、SDKをGoogleアプリケーションのデフォルトのクレデンシャルで初期化し、サービスアカウントID文字列を指定しないでください。

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

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は通常、GoogleCloudが提供するデフォルトのサービスアカウントの1つです。

明示的に指定されたサービスアカウントIDの場合と同様に、自動検出されたサービスアカウントIDには、カスタムトークンの作成が機能するためのiam.serviceAccounts.signBlob権限が必要です。デフォルトのサービスアカウントに必要な権限を付与するには、GoogleCloudConsoleのIAMおよび管理セクションを使用する必要がある場合があります。詳細については、以下のトラブルシューティングのセクションを参照してください。

サービスアカウントIDの使用

アプリケーションのさまざまな部分間の一貫性を維持するために、Googleが管理する環境で実行するときにトークンに署名するためにキーが使用されるサービスアカウントIDを指定できます。これにより、IAMポリシーがよりシンプルで安全になり、コードにサービスアカウントのJSONファイルを含める必要がなくなります。

サービスアカウントIDは、 Google Cloud Console 、またはダウンロードしたサービスアカウントのJSONファイルのclient_emailフィールドにあります。サービスアカウントIDは、次の形式の電子メールアドレスです: <client-id>@<project-id>.iam.gserviceaccount.com 。 FirebaseおよびGoogleCloudプロジェクトのサービスアカウントを一意に識別します。

別のサービスアカウントIDを使用してカスタムトークンを作成するには、以下に示すようにSDKを初期化します。

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)

行け

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は機密情報ではないため、それらの公開は重要ではありません。ただし、指定したサービスアカウントでカスタムトークンに署名するには、FirebaseAdminSDKがリモートサービスを呼び出す必要があります。さらに、管理SDKがこの呼び出しを行うために使用しているサービスアカウント(通常は{project-name {project-name}@appspot.gserviceaccount.com )にiam.serviceAccounts.signBlob権限があることも確認する必要があります。詳細については、以下のトラブルシューティングのセクションを参照してください。

FirebaseAdminSDKを使用してカスタムトークンを作成する

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

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)

行け

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

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)

行け

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() )など)と同じように、リアルタイムデータベースルールauthオブジェクトとクラウドストレージセキュリティルール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ライブラリを使用してカスタムトークンを作成する

バックエンドが公式のFirebaseAdminSDKを備えていない言語である場合でも、カスタムトークンを手動で作成できます。まず、ご使用の言語に対応するサードパーティの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〜36文字の長さの文字列である必要があります
claims (オプション)セキュリティルールに含めるオプションのカスタムクレームauth / request.auth変数

FirebaseAdminSDKがサポートしていないさまざまな言語でカスタムトークンを作成する方法の実装例を次に示します。

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での認証に使用します。これを行う方法については、上記のコードサンプルを参照してください。

トラブルシューティング

このセクションでは、開発者がカスタムトークンを作成するときに発生する可能性のあるいくつかの一般的な問題と、それらを解決する方法について概説します。

IAMAPIが有効になっていません

トークンに署名するためにサービスアカウント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は、 IAMAPIを使用してトークンに署名します。このエラーは、FirebaseプロジェクトでIAMAPIが現在有効になっていないことを示しています。 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ロールを付与することです。

  1. GoogleCloudConsoleでIAMと管理ページを開きます。
  2. プロジェクトを選択し、[続行]をクリックします。
  3. 更新するサービスアカウントに対応する編集アイコンをクリックします。
  4. 「別の役割を追加」をクリックします。
  5. 検索フィルターに「ServiceAccountTokenCreator」と入力し、結果から選択します。
  6. [保存]をクリックして、役割の付与を確認します。

このプロセスの詳細については、 IAMのドキュメントを参照するか、gcloudコマンドラインツールを使用してロールを更新する方法を学習してください。

サービスアカウントを特定できませんでした

次のようなエラーメッセージが表示される場合は、FirebaseAdminSDKが正しく初期化されていません。

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を指定してください。