Identity Platform テナントをプログラムで管理する

このドキュメントでは、Identity Platform Admin SDK を使用してテナントとそのユーザーをプログラムで管理する方法について説明します。管理者として実行できる業務には、次のようなものがあります。

  • ユーザー管理: 特定のテナントのユーザーの作成、更新、削除、一覧表示を行います。

  • ID の確認: アプリのユーザーを識別して、所有するサーバー上のリソースへのアクセスを制限します。

  • ユーザーのインポート: 外部認証システム、または他の Identity Platform プロジェクトやテナントからユーザーを移行します。

  • カスタム クレームによるアクセス制御: 特定のテナントのユーザー アカウントにカスタム属性を定義し、ロールベースのアクセス制御などのさまざまなアクセス制御方法を実装します。

  • ユーザー セッション管理: 特定のテナントのユーザーの更新トークンを取り消します。

  • メール アクション リンク: 特定のテナントのユーザーに対し、パスワードの再設定、メールリンク ログイン、メール確認のカスタムメール リンクを作成します。

  • テナント管理: 特定の Identity Platform プロジェクトのテナントを作成、一覧表示、取得、更新、削除します。

  • テナントの OIDC と SAML プロバイダの管理: 指定されたテナントの OIDC および SAML 構成をプログラムで管理します。

始める前に

サポートされている機能

マルチテナント環境で各 SDK がサポートしている機能を、次の表に示します。

機能 Node.js Java Python Go C#
カスタム トークン作成
ID トークンの確認
ユーザーの管理
カスタム クレームによるアクセスの制御
更新トークンの取り消し
ユーザーのインポート
メールのアクション リンクを生成する
多要素認証
SAML / OIDC プロバイダの構成を管理する
セッション Cookie を管理する

Admin SDK と Google Cloud コンソールを使用してテナント固有の環境で構成できるログイン方法を、次の表に示します。

機能 Google Cloud Console Admin SDK
メール
OIDC
SAML
ソーシャル
電話
多要素認証
匿名

テナント管理

Admin SDK を使用すると、Google Cloud コンソールを使用する代わりに、安全なサーバー環境からプログラムでテナントを管理できます。これには、テナントを作成、一覧表示、取得、変更、削除することが含まれます。

各テナントは、独自の ID プロバイダ、設定、一連のユーザーで構成されています。テナント構成管理オペレーション(CRUD)は、admin.auth().tenantManager() を使用して親プロジェクト インスタンスから利用できます。

テナント構成は、表示名、テナント ID、メール認証構成など、テナントに関する情報を提供します。

テナントの他のすべての設定(ホワイトリストに登録されたドメインや認証済みのリダイレクト URI など)は親プロジェクトから継承されます。これらは Google Cloud コンソールを使用して管理する必要があります。

テナント固有のユーザー管理、OIDC/SAML プロバイダの構成、メールリンク生成などのオペレーションでは、ターゲット テナント用の TenantAwareAuth インスタンス(一意の tenantId によって識別される)が必要になります。

Node.js

const tenantManager = admin.auth().tenantManager();
const tenantAuth = tenantManager.authForTenant(tenantId);

Python

from firebase_admin import tenant_mgt

tenant_client = tenant_mgt.auth_for_tenant(tenant_id)

Java

FirebaseAuth auth = FirebaseAuth.getInstance();
TenantManager tenantManager = auth.getTenantManager();
TenantAwareFirebaseAuth tenantAuth = tenantManager.getAuthForTenant(tenantId);

ユーザー管理 API、OIDC/SAML プロバイダ管理 API、メールリンク生成 API に対するすべての呼び出しは、このテナントの範囲内で行われます(TenantAwareAuth インスタンスを使用)。

既存のテナントの取得

Admin SDK には、tenantId(テナント固有の識別子)に基づいてテナントに関する情報を取得する getTenant() メソッドが用意されています。

Node.js

admin.auth().tenantManager().getTenant(tenantId)
  .then((tenant) => {
    console.log(tenant.toJSON());
  })
  .catch((error) => {
    // Handle error.
  });

Python

tenant = tenant_mgt.get_tenant(tenant_id)

print('Retreieved tenant:', tenant.tenant_id)

Java

Tenant tenant = FirebaseAuth.getInstance().getTenantManager().getTenant(tenantId);
System.out.println("Retrieved tenant: " + tenant.getTenantId());

このメソッドは、tenantId に対応する Tenant オブジェクトを返します。指定された tenantId が既存のテナントのものでない場合、Promise は auth/tenant-not-found エラーでリジェクトされます。

Tenant インスタンスを TenantAwareAuth オブジェクトと混同しないように注意してください。authInstance.tenantManager().authForTenant() は、BaseAuth を拡張する TenantAwareAuth インスタンスを返します。Auth クラスも BaseAuth を拡張します。BaseAuth は、ユーザーを管理し、さまざまなコンテキストで OIDC/SAML プロバイダを構成する API を提供します。Auth の場合、コンテキストは親プロジェクト レベルになります。TenantAwareAuth の場合、コンテキストはテナントレベルになります(テナントはテナント ID によって決まります)。getTenant() メソッドは基本的なテナント情報(テナント ID、表示名、メール プロバイダの設定など)で解決されますが、そのテナントで API を呼び出すには、authForTenant(tenantFromGetTenant.tenantId) を使用する必要があります。

テナントの作成

新しいテナント構成を作成するには、createTenant() メソッドを使用します。

Node.js

admin.auth().tenantManager().createTenant({
  displayName: 'myTenant1',
  emailSignInConfig: {
    enabled: true,
    passwordRequired: false, // Email link sign-in enabled.
  },
  // TODO: Remove if you don't want to enable multi-factor authentication.
  multiFactorConfig: {
    state: 'ENABLED',
    factorIds: ['phone']
  },
  // TODO: Remove if you don't want to register test phone numbers for use
  // with multi-factor authentication.
  testPhoneNumbers: {
    '+16505551234': '145678',
    '+16505550000': '123456'
  },
})
.then((createdTenant) => {
  console.log(createdTenant.toJSON());
})
.catch((error) => {
  // Handle error.
});

Python

tenant = tenant_mgt.create_tenant(
    display_name='myTenant1',
    enable_email_link_sign_in=True,
    allow_password_sign_up=True)

print('Created tenant:', tenant.tenant_id)

Java

Tenant.CreateRequest request = new Tenant.CreateRequest()
    .setDisplayName("myTenant1")
    .setEmailLinkSignInEnabled(true)
    .setPasswordSignInAllowed(true);
Tenant tenant = FirebaseAuth.getInstance().getTenantManager().createTenant(request);
System.out.println("Created tenant: " + tenant.getTenantId());

次のプロパティを任意に組み合わせて指定できます。

プロパティ 説明
displayName
string
    
テナントの表示名。英字、数字、ハイフンを使用し、英字で始まる 4~20 文字で構成される必要があります。
emailSignInConfig
{
  enable: boolean,
  passwordRequired: boolean
}
    
メール ログイン プロバイダの構成。これには、メール プロバイダが有効かどうかや、メールのログイン時にパスワードを要求するかどうかが含まれます。不要の場合、パスワードによるログインは、パスワードを使用するか、メールリンク ログインを使用して行えます。
multiFactorConfig
{
  state: 'DISABLED' | 'ENABLED',
  factorIds: string[]
}
    
テナントで多要素認証が有効になっているかどうかと、許可されている要素の種類。現在サポートされている要素 ID は phone のみです。
testPhoneNumbers
{
  string: string
}
  
テスト目的として登録する、電話番号とそれに関連する多要素認証コードのマップ。最大 10 個のエントリを指定できます。すべてのテスト電話番号を削除するには、このフィールドを null に設定します。

このメソッドは、新しく作成されたテナントの Tenant オブジェクトを返します。

テナントの更新

既存テナントのデータを変更するには、updateTenant() メソッドを使用します。tenantId と、変更するテナントのプロパティを指定する必要があります。

Node.js

admin.auth().tenantManager().updateTenant(tenantId, {
  displayName: 'updatedName',
  emailSignInConfig: {
    enabled: false, // Disable email provider.
  },
  // Enable multi-factor authentication.
  multiFactorConfig: {
    state: 'ENABLED',
    factorIds: ['phone']
  },
  // Register phone numbers for testing.
  testPhoneNumbers: {
    '+16505551234': '145678',
    '+16505550000': '123456'
  },
})
.then((updatedTenant) => {
  console.log(updatedTenant.toJSON());
})
.catch((error) => {
  // Handle error.
});

Python

tenant = tenant_mgt.update_tenant(
    tenant_id,
    display_name='updatedName',
    allow_password_sign_up=False) # Disable email provider

print('Updated tenant:', tenant.tenant_id)

Java

Tenant.UpdateRequest request = new Tenant.UpdateRequest(tenantId)
    .setDisplayName("updatedName")
    .setPasswordSignInAllowed(false);
Tenant tenant = FirebaseAuth.getInstance().getTenantManager().updateTenant(request);
System.out.println("Updated tenant: " + tenant.getTenantId());

updateTenant()createTenant() と同じプロパティを受理します。プロパティはすべて任意です。プロパティが指定されていない場合、既存の値は変更されません。

このメソッドが終了すると、更新された Tenant オブジェクトが返されます。指定された tenantId が既存のテナントのものでない場合、Promise は auth/tenant-not-found エラーでリジェクトされます。

テナントの削除

テナントは、tenantId を使用して削除できます。

Node.js

admin.auth().tenantManager().deleteTenant(tenantId)
  .then(() => {
    // Tenant deleted.
  })
  .catch((error) => {
    // Handle error.
  });

Python

tenant_mgt.delete_tenant(tenant_id)

Java

FirebaseAuth.getInstance().getTenantManager().deleteTenant(tenantId);

正常に削除されると、メソッドにより空の結果が返されます。指定された tenantId が既存のテナントのものでない場合、Promise は auth/tenant-not-found エラーでリジェクトされます。

テナントのリスト化

既存テナントをリスト化するには、listTenants() メソッドを使用します。

Node.js

function listAllTenants(nextPageToken) {
  return admin.auth().tenantManager().listTenants(100, nextPageToken)
    .then((result) => {
      result.tenants.forEach((tenant) => {
        console.log(tenant.toJSON());
      });
      if (result.pageToken) {
        return listAllTenants(result.pageToken);
      }
    });
}

listAllTenants();

Python

for tenant in tenant_mgt.list_tenants().iterate_all():
    print('Retrieved tenant:', tenant.tenant_id)

Java

ListTenantsPage page = FirebaseAuth.getInstance().getTenantManager().listTenants(null);
for (Tenant tenant : page.iterateAll()) {
  System.out.println("Retrieved tenant: " + tenant.getTenantId());
}

結果の各バッチには、テナントのリストに加え、テナントの次のバッチをリスト化するページトークンが含まれています。すべてのテナントがすでにリスト化されている場合、pageToken は返されません。

maxResults フィールドが指定されていない場合のデフォルトは、バッチあたり 1,000 テナントです。これは、同時にリスト化できるテナントの最大数でもあります。値が最大値を超えている場合は、引数エラーがスローされます。pageToken が指定されていない場合、このメソッドはテナントを先頭からリスト化します。

SAML プロバイダと OIDC プロバイダをプログラムで管理する

Admin SDK には、Security Assertion Markup Language(SAML)2.0 と OpenID Connect(OIDC)プロバイダ構成をセキュア サーバー環境からプログラムで管理する API が用意されています。

Admin SDK を使用すると、特定のテナントに対するこれらのプロバイダを管理できます。これは、プロジェクト レベルの OIDC プロバイダと SAML プロバイダの管理に似ています。

テナントのプロバイダを管理するには、始めに TenantAwareAuth インスタンスを作成します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

その後、テナントのプロバイダの作成、変更、削除などの一般的な操作を実行できます。

プロバイダの作成

テナントの SAML プロバイダを作成する方法を、次のコードに示します。

Node.js

const newConfig = {
  displayName: 'SAML provider name',
  enabled: true,
  providerId: 'saml.myProvider',
  idpEntityId: 'IDP_ENTITY_ID',
  ssoURL: 'https://example.com/saml/sso/1234/',
  x509Certificates: [
    '-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----',
    '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----'
  ],
  rpEntityId: 'RP_ENTITY_ID',
  // Using the default callback URL.
  callbackURL: 'https://project-id.firebaseapp.com/__/auth/handler'
};

tenantAuth.createProviderConfig(newConfig).then(() => {
  // Successful creation.
}).catch((error) => {
  // Handle error.
});

Python

saml = tenant_client.create_saml_provider_config(
    display_name='SAML provider name',
    enabled=True,
    provider_id='saml.myProvider',
    idp_entity_id='IDP_ENTITY_ID',
    sso_url='https://example.com/saml/sso/1234/',
    x509_certificates=[
        '-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----',
        '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
    ],
    rp_entity_id='P_ENTITY_ID',
    callback_url='https://project-id.firebaseapp.com/__/auth/handler')

print('Created new SAML provider:', saml.provider_id)

Java

SamlProviderConfig.CreateRequest request = new SamlProviderConfig.CreateRequest()
    .setDisplayName("SAML provider name")
    .setEnabled(true)
    .setProviderId("saml.myProvider")
    .setIdpEntityId("IDP_ENTITY_ID")
    .setSsoUrl("https://example.com/saml/sso/1234/")
    .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----")
    .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----")
    .setRpEntityId("RP_ENTITY_ID")
    .setCallbackUrl("https://project-id.firebaseapp.com/__/auth/handler");
SamlProviderConfig saml = tenantAuth.createSamlProviderConfig(request);
System.out.println("Created new SAML provider: " + saml.getProviderId());

プロバイダの変更

プロバイダを変更する方法を、次のコードに示します。

Node.js

const updatedConfig = {
  x509Certificates: [
    '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
    '-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----',
  ],
};
tenantAuth.updateProviderConfig('saml.myProvider', updatedConfig).then(() => {
  // Successful update.
}).catch((error) => {
  // Handle error.
});

Python

saml = tenant_client.update_saml_provider_config(
    'saml.myProvider',
    x509_certificates=[
        '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
        '-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----',
    ])

print('Updated SAML provider:', saml.provider_id)

Java

SamlProviderConfig.UpdateRequest request =
    new SamlProviderConfig.UpdateRequest("saml.myProvider")
      .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----")
      .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----");
SamlProviderConfig saml = tenantAuth.updateSamlProviderConfig(request);
System.out.println("Updated SAML provider: " + saml.getProviderId());

プロバイダの取得

プロバイダ ID を使用して特定のテナントのプロバイダ構成を取得する方法を、次のコードに示します。

Node.js

tenantAuth.getProviderConfig('saml.myProvider').then((config) => {
  // Get display name and whether it is enabled.
  console.log(config.displayName, config.enabled);
}).catch((error) => {
  // Handle error. Common error is that config is not found.
});

Python

saml = tennat_client.get_saml_provider_config('saml.myProvider')
print(saml.display_name, saml.enabled)

Java

SamlProviderConfig saml = tenantAuth.getSamlProviderConfig("saml.myProvider");

// Get display name and whether it is enabled.
System.out.println(saml.getDisplayName() + " " + saml.isEnabled());

プロバイダのリスト化

特定のテナントのプロバイダ構成をリスト化する方法を、次のコードに示します。

Node.js

// Returns 10 SAML provider configs starting from the specified nextPageToken offset.
tenantAuth.listProviderConfigs({type: 'saml', maxResults: 10, pageToken: 'nextPageToken'}).then((results) => {
  results.providerConfigs.forEach((config) => {
    console.log(config.providerId);
  });
  // To list the next 10:
  // return tenantAuth.listProviderConfigs(
  //     {type: 'saml', maxResults: 10, pageToken: results.pageToken});
}).catch((error) => {
  // Handle error.
});

Python

for saml in tenant_client.list_saml_provider_configs('nextPageToken').iterate_all():
    print(saml.provider_id)

Java

ListProviderConfigsPage<SamlProviderConfig> page = tenantAuth.listSamlProviderConfigs(
    "nextPageToken");
for (SamlProviderConfig saml : page.iterateAll()) {
  System.out.println(saml.getProviderId());
}

プロバイダの削除

プロバイダを削除する方法を、次のコードに示します。

Node.js

tenantAuth.deleteProviderConfig('saml.myProvider').then(() => {
  // Successful deletion.
}).catch((error) => {
  // Handle error.
});

Python

tenant_client.delete_saml_provider_config('saml.myProvider')

Java

tenantAuth.deleteSamlProviderConfig("saml.myProvider");

OIDC プロバイダは、プロジェクト レベルの OIDC プロバイダと同様に管理されます。ただし、Auth プロジェクト レベルのインスタンスではなく、対応する TenantAwareAuth インスタンスから管理できます。

詳細については、プログラムによる SAML プロバイダと OIDC プロバイダの管理をご覧ください。

テナント固有のユーザーの管理

Admin SDK を使用すると、特定のテナントのすべてのユーザーの作成、取得、更新、削除、リスト化を行えます。

はじめに、対応するテナントの TenantAwareAuth インスタンスを用意します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

ユーザーの取得

テナント固有のユーザーは、uid ID を使用して取得できます。

Node.js

tenantAuth.getUser(uid)
  .then((userRecord) => {
    // See the UserRecord reference documentation to learn more.
    console.log('Successfully fetched user data:', userRecord.toJSON());
    // Tenant ID will be reflected in userRecord.tenantId.
  })
  .catch((error) => {
    console.log('Error fetching user data:', error);
  });

Python

	# Get an auth.Client from tenant_mgt.auth_for_tenant()
    user = tenant_client.get_user(uid)
    print('Successfully fetched user data:', user.uid)

Java

// Get an auth client from the firebase.App
UserRecord user = tenantAuth.getUser(uid);
System.out.println("Successfully fetched user data: " + user.getDisplayName());

メールアドレスでユーザーを識別することもできます。

Node.js

tenantAuth.getUserByEmail(email)
  .then((userRecord) => {
    // See the UserRecord reference documentation to learn more.
    console.log('Successfully fetched user data:', userRecord.toJSON());
    // Tenant ID will be reflected in userRecord.tenantId.
  })
  .catch((error) => {
    console.log('Error fetching user data:', error);
  });

Python

user = tenant_client.get_user_by_email(email)
print('Successfully fetched user data:', user.uid)

Java

// Get an auth client from the firebase.App
UserRecord user = tenantAuth.getUserByEmail(email);
System.out.println("Successfully fetched user data: " + user.getDisplayName());

ユーザーを作成する

特定のテナントに対する新しいユーザーを作成するには、createUser() メソッドを使用します。新しいユーザーを作成する際、uid は省略可能です。指定しない場合、Identity Platform により一意の ID がプロビジョニングされます。

Node.js

tenantAuth.createUser({
  email: 'user@example.com',
  emailVerified: false,
  phoneNumber: '+11234567890',
  password: 'secretPassword',
  displayName: 'John Doe',
  photoURL: 'http://www.example.com/12345678/photo.png',
  disabled: false
})
.then((userRecord) => {
  // See the UserRecord reference documentation to learn more.
  console.log('Successfully created new user:', userRecord.uid);
  // Tenant ID will be reflected in userRecord.tenantId.
})
.catch((error) => {
  console.log('Error creating new user:', error);
});

Python

user = tenant_client.create_user(
    email='user@example.com',
    email_verified=False,
    phone_number='+15555550100',
    password='secretPassword',
    display_name='John Doe',
    photo_url='http://www.example.com/12345678/photo.png',
    disabled=False)
print('Sucessfully created new user:', user.uid)

Java

UserRecord.CreateRequest request = new UserRecord.CreateRequest()
    .setEmail("user@example.com")
    .setEmailVerified(false)
    .setPhoneNumber("+15555550100")
    .setPassword("secretPassword")
    .setDisplayName("John Doe")
    .setPhotoUrl("http://www.example.com/12345678/photo.png")
    .setDisabled(false);
UserRecord user = tenantAuth.createUser(request);
System.out.println("Successfully created user: " + user.getDisplayName());

ユーザーの変更

uidupdateUser() メソッドに指定することで、既存のユーザーを変更できます。

Node.js

tenantAuth.updateUser(uid, {
  email: 'modifiedUser@example.com',
  phoneNumber: '+11234567890',
  emailVerified: true,
  password: 'newPassword',
  displayName: 'Jane Doe',
  photoURL: 'http://www.example.com/12345678/photo.png',
  disabled: true
})
.then((userRecord) => {
  // See the UserRecord reference documentation to learn more.
  console.log('Successfully updated user', userRecord.toJSON());
})
.catch((error) => {
  console.log('Error updating user:', error);
});

Python

user = tenant_client.update_user(
    uid,
    email='user@example.com',
    phone_number='+15555550100',
    email_verified=True,
    password='newPassword',
    display_name='John Doe',
    photo_url='http://www.example.com/12345678/photo.png',
    disabled=True)
print('Sucessfully updated user:', user.uid)

Java

UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid)
    .setEmail("user@example.com")
    .setEmailVerified(true)
    .setPhoneNumber("+15555550100")
    .setPassword("newPassword")
    .setDisplayName("John Doe")
    .setPhotoUrl("http://www.example.com/12345678/photo.png")
    .setDisabled(true);
UserRecord user = tenantAuth.updateUser(request);
System.out.println("Successfully updated user: " + user.getDisplayName());

ユーザーの削除

uid に基づいてユーザーを削除する方法を、次の例に示します。

Node.js

tenantAuth.deleteUser(uid)
  .then(() => {
    console.log('Successfully deleted user');
  })
  .catch((error) => {
    console.log('Error deleting user:', error);
  });

Python

tenant_client.delete_user(uid)
print('Successfully deleted user')

Java

tenantAuth.deleteUser(uid);

System.out.println("Successfully deleted user: " + uid);

ユーザーの一覧を表示する

特定のテナントのユーザーのリストすべてを一括で取得するには、listUsers() メソッドを使用します。各バッチには、ユーザー レコードのリストに加え、他にユーザーが残っている場合は、ネクストページ トークンが含まれます。

Node.js

function listAllUsers(nextPageToken) {
  // List batch of users, 1000 at a time.
  tenantAuth.listUsers(1000, nextPageToken)
    .then((listUsersResult) => {
      listUsersResult.users.forEach((userRecord) => {
        console.log('user', userRecord.toJSON());
        // Tenant ID will be reflected in userRecord.tenantId.
      });
      if (listUsersResult.pageToken) {
        // List next batch of users.
        listAllUsers(listUsersResult.pageToken);
      }
    })
    .catch((error) => {
      console.log('Error listing users:', error);
    });
}
// Start listing users from the beginning, 1000 at a time.
listAllUsers();

Python

	# Note, behind the scenes, the iterator will retrive 1000 users at a time through the API
    for user in tenant_client.list_users().iterate_all():
        print('User: ' + user.uid)

	# Iterating by pages of 1000 users at a time.
    page = tenant_client.list_users()
    while page:
        for user in page.users:
            print('User: ' + user.uid)
        # Get next batch of users.
        page = page.get_next_page()

Java

// Note, behind the scenes, the ListUsersPage retrieves 1000 Users at a time
// through the API
ListUsersPage  page = tenantAuth.listUsers(null);
for (ExportedUserRecord user : page.iterateAll()) {
  System.out.println("User: " + user.getUid());
}

// Iterating by pages 100 users at a time.
page = tenantAuth.listUsers(null, 100);
while (page != null) {
  for (ExportedUserRecord user : page.getValues()) {
    System.out.println("User: " + user.getUid());
  }

  page = page.getNextPage();
}

詳細については、ユーザーの管理に関する Admin SDK のドキュメントをご覧ください。

ユーザーのインポート

Admin SDK を使用すると、昇格した管理者権限でを持つ特定のテナントにユーザーを一括してインポートできます。これにより、別の Identity Platform プロダクト、別のテナント、異なるハッシング アルゴリズムを使用する外部認証システムからユーザーを移行する機能など、多くの利点が得られます。また、フェデレーション プロバイダ(SAML、OIDC など)とカスタム クレームを使用してユーザーを一括インポートすることもできます。

はじめに、対応するテナントの TenantAwareAuth インスタンスを取得します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

特定のハッシュ アルゴリズムを使用して、一度に最大 1,000 ユーザーをインポートできます。

Node.js

tenantAuth.importUsers([{
  uid: 'uid1',
  email: 'user1@example.com',
  // Must be provided in a byte buffer.
  passwordHash: Buffer.from('password-hash-1'),
  // Must be provided in a byte buffer.
  passwordSalt: Buffer.from('salt1')
},
{
  uid: 'uid2',
  email: 'user2@example.com',
  // Must be provided in a byte buffer.
  passwordHash: Buffer.from('password-hash-2'),
  // Must be provided in a byte buffer.
  passwordSalt: Buffer.from('salt2')

}], {
  hash: {
    algorithm: 'HMAC_SHA256',
    // Must be provided in a byte buffer.
    key: Buffer.from('secret')
  }
})
.then((results) => {
  results.errors.forEach(function(indexedError) {
  console.log('Error importing user ' + indexedError.index);
  });
})
.catch((error) => {
  console.log('Error importing users:', error);
});

Python

users = [
    auth.ImportUserRecord(
        uid='uid1',
        email='user1@example.com',
        password_hash=b'password_hash_1',
        password_salt=b'salt1'
    ),
    auth.ImportUserRecord(
        uid='uid2',
        email='user2@example.com',
        password_hash=b'password_hash_2',
        password_salt=b'salt2'
    ),
]

hash_alg = auth.UserImportHash.hmac_sha256(key=b'secret')
try:
    result = tenant_client.import_users(users, hash_alg=hash_alg)
    for err in result.errors:
        print('Failed to import user:', err.reason)
except exceptions.FirebaseError as error:
    print('Error importing users:', error)

Java

List<ImportUserRecord> users = new ArrayList<>();
users.add(ImportUserRecord.builder()
    .setUid("uid1")
    .setEmail("user1@example.com")
    .setPasswordHash("password-hash-1".getBytes())
    .setPasswordSalt("salt1".getBytes())
    .build());
users.add(ImportUserRecord.builder()
    .setUid("uid2")
    .setEmail("user2@example.com")
    .setPasswordHash("password-hash-2".getBytes())
    .setPasswordSalt("salt2".getBytes())
    .build());
UserImportHash hmacSha256 = HmacSha256.builder()
    .setKey("secret".getBytes())
    .build();
UserImportResult result = tenantAuth.importUsers(users, UserImportOptions.withHash(hmacSha256));

for (ErrorInfo error : result.getErrors()) {
  System.out.println("Failed to import user: " + error.getReason());
}

インポートしたすべてのユーザーの tenantIdtenantAuth.tenantId に設定されます。

パスワードのないユーザーも、特定のテナントにインポートできます。こうしたユーザーは、フェデレーション プロバイダとカスタム クレームを使用してインポートされます。

Node,js

tenantAuth.importUsers([{
  uid: 'some-uid',
  displayName: 'John Doe',
  email: 'johndoe@acme.com',
  photoURL: 'http://www.example.com/12345678/photo.png',
  emailVerified: true,
  phoneNumber: '+11234567890',
  // Set this user as admin.
  customClaims: {admin: true},
  // User with SAML provider.
  providerData: [{
    uid: 'saml-uid',
    email: 'johndoe@acme.com',
    displayName: 'John Doe',
    photoURL: 'http://www.example.com/12345678/photo.png',
    providerId: 'saml.acme'
  }]
}])
.then(function(results) {
  results.errors.forEach(function(indexedError) {
  console.log('Error importing user ' + indexedError.index);
  });
})
.catch(function(error) {
  console.log('Error importing users:', error);
});

Python

users = [
    auth.ImportUserRecord(
        uid='some-uid',
        display_name='John Doe',
        email='johndoe@gmail.com',
        photo_url='http://www.example.com/12345678/photo.png',
        email_verified=True,
        phone_number='+11234567890',
        custom_claims={'admin': True}, # set this user as admin
        provider_data=[ # user with SAML provider
            auth.UserProvider(
                uid='saml-uid',
                email='johndoe@gmail.com',
                display_name='John Doe',
                photo_url='http://www.example.com/12345678/photo.png',
                provider_id='saml.acme'
            )
        ],
    ),
]
try:
    result = tenant_client.import_users(users)
    for err in result.errors:
        print('Failed to import user:', err.reason)
except exceptions.FirebaseError as error:
    print('Error importing users:', error)

Java

List<ImportUserRecord> users = new ArrayList<>();
users.add(ImportUserRecord.builder()
    .setUid("some-uid")
    .setDisplayName("John Doe")
    .setEmail("johndoe@acme.com")
    .setPhotoUrl("https://www.example.com/12345678/photo.png")
    .setEmailVerified(true)
    .setPhoneNumber("+11234567890")
    // Set this user as admin.
    .putCustomClaim("admin", true)
    // User with SAML provider.
    .addUserProvider(UserProvider.builder()
        .setUid("saml-uid")
        .setEmail("johndoe@acme.com")
        .setDisplayName("John Doe")
        .setPhotoUrl("https://www.example.com/12345678/photo.png")
        .setProviderId("saml.acme")
        .build())
    .build());

UserImportResult result = tenantAuth.importUsers(users);

for (ErrorInfo error : result.getErrors()) {
  System.out.println("Failed to import user: " + error.getReason());
}

詳細については、Admin SDK ドキュメントのユーザーをインポートするをご覧ください。

身元確認

Identity Platform クライアント アプリがカスタム バックエンド サーバーと通信する場合、現在ログインしているユーザーをそのサーバーで識別する必要があります。これを安全に行うには、サーバーへの安全な接続を使用してログイン後にユーザーの ID トークンを送信します。これにより、サーバーは ID トークンの整合性と信頼性を検証できます。

Admin SDK には、特定のテナントの ID トークンを確認してデコードする組み込みのメソッドが用意されています。

クライアントから特定のテナントにユーザーが正常にログインすると、Client SDK を使用してユーザーの ID トークンを取得します。

auth.tenantId = 'TENANT-ID';
auth.signInWithEmailAndPassword('user@example.com', 'password')
  .then((userCredential) => {
    return userCredential.user.getIdToken();
  })
  .then((idToken) => {
    // Send the ID token to server for verification. ID token should be scoped to TENANT-ID.
  });

サーバーに TenantAwareAuth インスタンスを作成します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

次に、そのテナントの ID トークンを確認します。

Node.js

// idToken comes from the client app
tenantAuth.verifyIdToken(idToken)
  .then((decodedToken) => {
    let uid = decodedToken.uid;
    // This should be set to TENANT-ID. Otherwise auth/mismatching-tenant-id error thrown.
    console.log(decodedToken.firebase.tenant);
    // ...
  }).catch((error) => {
    // Handle error
  });

サーバー側のリソースは、異なるアクセスレベルを持つ複数のテナントからアクセスできます。この場合、テナント ID が事前にわからない可能性があるため、最初にプロジェクト レベルで ID トークンが確認されることがあります。

admin.auth().verifyIdToken(idToken)
  .then((decodedToken) => {
    if (decodedToken.firebase.tenant === 'TENANT-ID1') {
      // Allow appropriate level of access for TENANT-ID1.
    } else if (decodedToken.firebase.tenant === 'TENANT-ID2') {
      // Allow appropriate level of access for TENANT-ID2.
    } else {
      // Block access for all other tenants.
      throw new Error('Access not allowed.');
    }
  }).catch((error) => {
    // Handle error
  });

Python

	# id_token comes from the client app
    try:
        decoded_token = tenant_client.verify_id_token(id_token)

        # This should be set to TENANT-ID. Otherwise TenantIdMismatchError error raised.
        print('Verified ID token from tenant:', decoded_token['firebase']['tenant'])
    except tenant_mgt.TenantIdMismatchError:
        # Token revoked, inform the user to reauthenticate or signOut().
        pass

Java

try {
  // idToken comes from the client app
  FirebaseToken token = tenantAuth.verifyIdToken(idToken);
  // TenantId on the FirebaseToken should be set to TENANT-ID.
  // Otherwise "tenant-id-mismatch" error thrown.
  System.out.println("Verified ID token from tenant: " + token.getTenantId());
} catch (FirebaseAuthException e) {
  System.out.println("error verifying ID token: " + e.getMessage());
}

詳細については、ID トークンの確認に関する Admin SDK のドキュメントをご覧ください。

ユーザー セッションの管理

Identity Platform のセッションは長期間有効です。ユーザーがログインするたびに、ユーザーの認証情報が Identity Platform サーバーで確認され、有効期間の短い ID トークンが有効期間の長い更新トークンと交換されます。ID トークンは 1 時間有効です。更新トークンは、ユーザーが無効化、削除、アカウントの大幅な変更(メールやパスワードの更新など)が行われた場合を除いて、期限切れになりません。

デバイスの紛失や盗難の報告、アプリ内での一般的な脆弱性の発見、有効なトークンの大規模な漏洩などのケースでは、セキュリティ上の理由によりトークンを取り消す必要があります。Admin SDK には、特定のテナントの指定されたユーザーの更新トークンをすべて取り消す API が用意されています。

はじめに、TenantAwareAuth インスタンスを用意します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

次に、ユーザーの uid を指定することで、更新トークンを取り消すことができます。

Node.js

// Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
tenantAuth.revokeRefreshTokens(uid)
  .then(() => {
    return tenantAuth.getUser(uid);
  })
  .then((userRecord) => {
    return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
  })
  .then((timestamp) => {
    console.log('Tokens revoked at: ', timestamp);
  });

Python

	# Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
	# Retrieve the timestamp of the revocation, in seconds since the epoch.
    tenant_client.revoke_refresh_tokens(uid)

    user = tenant_client.get_user(uid)
    # Convert to seconds as the auth_time in the token claims is in seconds.
    revocation_second = user.tokens_valid_after_timestamp / 1000
    print('Tokens revoked at: {0}'.format(revocation_second))

Java

// Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
tenantAuth.revokeRefreshTokens(uid);

// accessing the user's TokenValidAfter
UserRecord user = tenantAuth.getUser(uid);


long timestamp = user.getTokensValidAfterTimestamp() / 1000;
System.out.println("the refresh tokens were revoked at: " + timestamp + " (UTC seconds)");

更新トークンが取り消された後は、再認証が行われるまで新しい ID トークンは発行されません。ただし、既存の ID トークンは、本来の有効期限(1 時間)まで有効なままです。

オプションの checkRevoked パラメータを指定することで、有効期限が切れていない有効な ID トークンが取り消されていないことを確認できます。これにより、トークンの整合性と信頼性検証が行われた後にトークンが取り消されたかどうかがチェックされます。

Node.js

// Verify the ID token for a specific tenant while checking if the token is revoked by passing
// checkRevoked true.
let checkRevoked = true;
tenantAuth.verifyIdToken(idToken, checkRevoked)
  .then(payload => {
    // Token is valid.
  })
  .catch(error => {
    if (error.code == 'auth/id-token-revoked') {
      // Token has been revoked. Inform the user to re-authenticate or
      // signOut() the user.
    } else {
      // Token is invalid.
    }
  });

Python

	# Verify the ID token for a specific tenant while checking if the token is revoked.
    try:
        # Verify the ID token while checking if the token is revoked by
        # passing check_revoked=True.
        decoded_token = tenant_client.verify_id_token(id_token, check_revoked=True)
        # Token is valid and not revoked.
        uid = decoded_token['uid']
    except tenant_mgt.TenantIdMismatchError:
        # Token belongs to a different tenant.
        pass
    except auth.RevokedIdTokenError:
        # Token revoked, inform the user to reauthenticate or signOut().
        pass
    except auth.UserDisabledError:
        # Token belongs to a disabled user record.
        pass
    except auth.InvalidIdTokenError:
        # Token is invalid
        pass

Java

// Verify the ID token for a specific tenant while checking if the token is revoked.
boolean checkRevoked = true;
try {
  FirebaseToken token = tenantAuth.verifyIdToken(idToken, checkRevoked);
  System.out.println("Verified ID token for: " + token.getUid());
} catch (FirebaseAuthException e) {
  if ("id-token-revoked".equals(e.getErrorCode())) {
    // Token is revoked. Inform the user to re-authenticate or signOut() the user.
  } else {
    // Token is invalid
  }
}

詳細については、セッションの管理に関する Admin SDK のドキュメントをご覧ください。

カスタム クレームによるアクセスの制御

Admin SDK では、特定のテナントに対するユーザー アカウントのカスタム属性を定義できます。こうした属性を使用すると、ロールベースのアクセス制御など、さまざまなアクセス制御の仕組みを実装できます。この属性により、アプリケーションのセキュリティ ルールによって適用されるアクセスレベルを変えることができます。

はじめに、対応するテナントの TenantAwareAuth インスタンスを取得します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

カスタム クレームには機密データが含まれている可能性があるため、Admin SDK によって特権サーバー環境からのみ設定される必要があります。

Node.js

// Set admin privilege on the user corresponding to uid for a specific tenant.
tenantAuth.setCustomUserClaims(uid, {admin: true}).then(() => {
  // The new custom claims will propagate to the user's ID token the
  // next time a new one is issued.
});

Python

# Set admin privilege on the user corresponding to uid.
tenant_client.set_custom_user_claims(uid, {'admin': True})
# The new custom claims will propagate to the user's ID token the
# next time a new one is issued.

Java

// Set admin privilege on the user corresponding to uid in a specific tenant.
Map<String, Object> claims = new HashMap<>();
claims.put("admin", true);
tenantAuth.setCustomUserClaims(uid, claims);
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.

ユーザーが次回ログインしたときか、既存のセッションで ID トークンを更新したときに、新たに設定されたカスタム属性がトークン ペイロードの最上位属性に表示されます。前記の例では、ID トークンには追加クレーム({admin: true})が含まれています。

ID トークンを確認してペイロードをデコードした後は、追加のカスタム クレームをチェックしてアクセス制御を適用できます。

Node.js

// Verify the ID token first.
tenantAuth.verifyIdToken(idToken).then((claims) => {
  if (claims.admin === true) {
    // Allow access to requested admin resource.
  }
});

Python

# Verify the ID token first.
claims = tenant_client.verify_id_token(id_token)
if claims['admin'] is True:
    # Allow access to requested admin resource.
    pass

Java

// Verify the ID token first.
FirebaseToken token = tenantAuth.verifyIdToken(idToken);
if (Boolean.TRUE.equals(token.getClaims().get("admin"))) {
  //Allow access to requested admin resource.
}
// Verify the ID token first.
FirebaseToken decoded = tenantAuth.verifyIdToken(idToken);
if (Boolean.TRUE.equals(decoded.getClaims().get("admin"))) {
  // Allow access to requested admin resource.
}

特定のテナントの既存のユーザーのカスタム クレームを、ユーザー レコードのプロパティとしても使用できます。

Node.js

// Lookup the user associated with the specified uid.
tenantAuth.getUser(uid).then((userRecord) => {
  // The claims can be accessed on the user record.
  console.log(userRecord.customClaims.admin);
});

Python

	# Lookup the user associated with the specified uid.
    user = tenant_client.get_user(uid)

	# The claims can be accessed on the user record.
    print(user.custom_claims.get('admin'))

Java

// Lookup the user associated with the specified uid in a specific tenant.
UserRecord user = tenantAuth.getUser(uid);
System.out.println(user.getCustomClaims().get("admin"));

詳細については、カスタム クレームに関する Admin SDK のドキュメントをご覧ください。

Identity Platform Client SDK を使用すると、パスワードの再設定、メールアドレスの確認、メールベースのログインに使用できるリンクが含まれた特定のテナントメールをユーザーに送信できます。これらのメールは Google によって送信され、カスタマイズには制限があります。

Admin SDK を使用すると、プログラムによって特定のテナントの範囲内でこうしたリンクを生成できます。

はじめに、対応するテナントの TenantAwareAuth インスタンスを取得します。

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

指定したテナントのユーザーのメールアドレスを検証するリンクを生成する方法を、次の例に示します。

Node.js

const actionCodeSettings = {
  // URL you want to redirect back to. The domain (www.example.com) for
  // this URL must be whitelisted in the Cloud console.
  url: 'https://www.example.com/checkout?cartId=1234',
  // This must be true for email link sign-in.
  handleCodeInApp: true,
  iOS: {
    bundleId: 'com.example.ios'
  },
  android: {
    packageName: 'com.example.android',
    installApp: true,
    minimumVersion: '12'
  },
  // FDL custom domain.
  dynamicLinkDomain: 'coolapp.page.link'
};

// Admin SDK API to generate the email verification link.
const userEmail = 'user@example.com';
tenantAuth.generateEmailVerificationLink(userEmail, actionCodeSettings)
  .then((link) => {
    // Construct email verification template, embed the link and send
    // using custom SMTP server.
    return sendCustomVerificationEmail(userEmail, displayName, link);
  })
  .catch((error) => {
    // Some error occurred.
  });

Python

action_code_settings = auth.ActionCodeSettings(
    url='https://www.example.com/checkout?cartId=1234',
    handle_code_in_app=True,
    ios_bundle_id='com.example.ios',
    android_package_name='com.example.android',
    android_install_app=True,
    android_minimum_version='12',
    # FDL custom domain.
    dynamic_link_domain='coolapp.page.link',
)

email = 'user@example.com'
link = tenant_client.generate_email_verification_link(email, action_code_settings)
# Construct email from a template embedding the link, and send
# using a custom SMTP server.
send_custom_email(email, link)

Java

ActionCodeSettings actionCodeSettings = ActionCodeSettings.builder()
    // URL you want to redirect back to. The domain (www.example.com) for
    // this URL must be whitelisted in the GCP Console.
    .setUrl("https://www.example.com/checkout?cartId=1234")
    // This must be true for email link sign-in.
    .setHandleCodeInApp(true)
    .setIosBundleId("com.example.ios")
    .setAndroidPackageName("com.example.android")
    .setAndroidInstallApp(true)
    .setAndroidMinimumVersion("12")
    // FDL custom domain.
    .setDynamicLinkDomain("coolapp.page.link")
    .build();

String link = tenantAuth.generateEmailVerificationLink(email, actionCodeSettings);

// Construct email verification template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link);

パスワードの再設定やメールベースのログインリンクを生成する同様のメカニズムを使用できます。テナント環境でメール アクション リンクを生成する場合、リンクからテナント ID を抽出して、クライアント Auth インスタンスに設定した後、コードを適用する必要があります。

const actionCodeUrl = firebase.auth.ActionCodeURL.parseLink(window.location.href);
// A one-time code, used to identify and verify a request.
const code = actionCodeUrl.code;
// The tenant ID being used to trigger the email action.
const tenantId = actionCodeUrl.tenantId;
auth.tenantId = tenantId;

// Apply the action code.
auth.applyActionCode(actionCode)
  .then(() => {
    // User's email is now verified.
  })
  .catch((error) => {
    // Handle error.
  });

詳細については、Admin SDK ドキュメントのメール アクション リンクをご覧ください。

エラー メッセージ

よく発生するエラー メッセージの一覧を、次の表に示します。

エラーコード 説明と解決手順
auth/billing-not-enabled この機能を使用するには、課金を有効にする必要があります。
auth/invalid-display-name displayName フィールドは、有効な文字列にする必要があります。
auth/invalid-name 指定されたリソース名が無効です。
auth/invalid-page-token ページトークンは、空でない有効な文字列にする必要があります。
auth/invalid-project-id 親プロジェクトが無効です。親プロジェクトで、またはマルチテナンシーが有効になっていないか、以前に無効の状態でした。
auth/invalid-tenant-id テナント ID は、空ではない有効な文字列にする必要があります。
auth/mismatching-tenant-id ユーザー テナント ID が現在の TenantAwareAuth テナント ID と一致しません。
auth/missing-display-name 作成または編集しているリソースに有効な表示名がありません。
auth/insufficient-permission ユーザーには、リクエストされたリソースにアクセスする権限や、特定のテナント オペレーションを実行する権限がありません。
auth/quota-exceeded 指定されたオペレーションのプロジェクト割り当てが超過しています。
auth/tenant-not-found 指定された ID に対応するテナントはありません。
auth/unsupported-tenant-operation マルチテナント環境では、この操作はサポートされていません。
auth/invalid-testing-phone-number 無効なテスト用の電話番号または無効なテストコードが指定されました。
auth/test-phone-number-limit-exceeded テスト用の電話番号とコードのペアが最大数を超えました。