カスタム トークンを作成

Firebase では、保護された JSON Web Token(JWT)を使用したユーザーまたは端末の認証が可能であるために、認証に対する完全な制御が得られます。サーバーでこうしたトークンを生成し、クライアント デバイスに返した後、signInWithCustomToken() メソッドで認証するためにこのトークンを使用します。

このために、ユーザー名やパスワードなどのログイン認証情報を受け入れ、認証情報が有効である場合にカスタム JWT を返すサーバー エンドポイントを作成する必要があります。これによって、サーバーから返されたカスタム JWT は、Firebase(iOSAndroidウェブ)での認証のためにクライアント端末で使用できます。認証後、Firebase Realtime Database や Firebase Storage など他の Firebase サービスにアクセスするときにこの ID が使用されます。さらに、Firebase Realtime Database Security Rulesauth オブジェクトと Firebase Storage Security Rulesrequest.auth オブジェクトで JWT のコンテンツが使用可能になります。

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

準備

Firebase Admin SDK でカスタム トークンを作成するには、サービス アカウントが必要です。サービス アカウントで Admin SDK を初期化する方法の詳細については、Admin SDK の設定手順をご覧ください。

Firebase Admin SDK を使用したカスタム トークンの作成

Firebase Admin SDK には、カスタム トークンを作成するための組み込みメソッドが用意されています。最低限 uid を指定する必要があり、任意の文字列を指定できますが、認証中のユーザーまたは端末を一意に識別するものである必要があります。これらのトークンは 24 時間後に失効します。

Node.js

var uid = "some-uid";

admin.auth().createCustomToken(uid)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(error) {
    console.log("Error creating custom token:", error);
  });

Java

String uid = "some-uid";

FirebaseAuth.getInstance().createCustomToken(uid)
    .addOnSuccessListener(new OnSuccessListener<String>() {
        @Override
        public void onSuccess(String customToken) {
            // Send token back to client
        }
    });

オプションで、カスタム トークンに追加のクレームを含めるよう指定することもできます。たとえば、セキュリティ ルールの auth / request.auth オブジェクトで使用可能なカスタム トークンに次の premiumAccount フィールドが追加されています。

Node.js

var uid = "some-uid";
var additionalClaims = {
  premiumAccount: true
};

admin.auth().createCustomToken(uid, additionalClaims)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(error) {
    console.log("Error creating custom token:", error);
  });

Java

String uid = "some-uid";
HashMap<String, Object> additionalClaims = new HashMap<String, Object>();
additionalClaims.put("premiumAccount", true);

FirebaseAuth.getInstance().createCustomToken(uid, additionalClaims)
    .addOnSuccessListener(new OnSuccessListener<String>() {
        @Override
        public void onSuccess(String customToken) {
            // Send token back to client
        }
    });

クライアントでのカスタム トークンを使用したログイン

カスタム トークンを作成した後、クライアント アプリに送信する必要があります。クライアント アプリは、signInWithCustomToken() を呼び出してカスタム トークンで認証します。

iOS

Objective-C

[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRUser *_Nullable user,
                                        NSError *_Nullable error) {
                             // ...
                           }];

Swift

FIRAuth.auth()?.signIn(withCustomToken: customToken ?? "") { (user, error) in
  // ...
}

Android

mAuth.signInWithCustomToken(mCustomToken)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                Log.d(TAG, "signInWithCustomToken:onComplete:" + task.isSuccessful());

                // If sign in fails, display a message to the user. If sign in succeeds
                // the auth state listener will be notified and logic to handle the
                // signed in user can be handled in the listener.
                if (!task.isSuccessful()) {
                    Log.w(TAG, "signInWithCustomToken", task.getException());
                    Toast.makeText(CustomAuthActivity.this, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                }
            }
        });

ウェブ

firebase.auth().signInWithCustomToken(token).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // ...
});

認証が成功した場合、ユーザーはカスタム トークンに含まれる uid によって指定されるアカウントで、クライアント アプリにログインします。そのアカウントがまだ存在しなければ、該当ユーザーのレコードが作成されます。

signInWithEmailAndPassword()signInWithCredential() など)他のログイン メソッドと同様に、Firebase Realtime Database Security Rulesauth オブジェクトと Firebase Storage Security Rulesrequest.auth オブジェクトにユーザーの uid が格納されます。この場合、カスタム トークンの生成時に指定した uid が使用されます。

Database ルール

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

Storage ルール

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /adminContent/{filename} {
      allow read, write: if request.auth.uid == "some-uid";
    }
  }
}

カスタム トークンに追加クレームが含まれる場合、ルール内の auth.token オブジェクト(Firebase Realtime Database)または request.auth.token オブジェクト(Firebase Storage)から参照できます。

Database ルール

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

Storage ルール

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 ログイン済みユーザーの一意の ID(1~36 文字の文字列にする必要があります)。
claims(省略可) セキュリティ ルールの auth / request.auth 変数に含めるオプションのカスタム クレーム

次に、公式の Firebase Admin SDK が存在しないさまざまな言語でカスタムトークンを作成する方法について、いくつか実装例を示します。

PHP

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

Python

python-jwtpycrypto のパッケージを使用した実装例:

import jwt  # Requires: pip install python-jwt
import Crypto.PublicKey.RSA as RSA  # Requires: pip install pycrypto

import datetime

# 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 = RSA.importKey("-----BEGIN PRIVATE KEY-----\n...")

def create_custom_token(uid, is_premium_account):
  try:
    payload = {
      "iss": service_account_email,
      "sub": service_account_email,
      "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
      "uid": uid,
      "claims": {
        "premium_account": is_premium_account
      }
    }
    exp = datetime.timedelta(minutes=60)
    return jwt.generate_jwt(payload, private_key, "RS256", exp)
  except Exception as e:
    print "Error creating custom token: " + e.message
    return None

Ruby

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

フィードバックを送信...