프로그래매틱 방식으로 Identity Platform 테넌트 관리

이 문서에서는 Identity Platform Admin SDK를 사용하여 테넌트와 해당 사용자를 프로그래매틱 방식으로 관리하는 방법을 설명합니다. 관리자가 수행할 수 있는 몇 가지 활동은 다음과 같습니다.

  • 사용자 관리: 특정 테넌트의 사용자를 생성, 업데이트, 삭제, 나열합니다.

  • ID 확인: 자체 서버의 리소스에 대한 액세스를 제한하기 위해 앱 사용자를 식별합니다.

  • 사용자 가져오기: 외부 인증 시스템이나 다른 Identity Platform 프로젝트 또는 테넌트에서 사용자를 마이그레이션합니다.

  • 커스텀 클레임으로 액세스 제어: 특정 테넌트의 사용자 계정에 커스텀 속성을 정의하고 역할 기반 액세스 제어와 같은 다양한 액세스 제어 전략을 구현합니다.

  • 사용자 세션 관리: 특정 테넌트의 사용자 갱신 토큰을 취소합니다.

  • 이메일 작업 링크: 특정 테넌트의 사용자 비밀번호 재설정, 이메일 링크 로그인, 이메일 확인을 위한 맞춤설정된 이메일 링크를 생성합니다.

  • 테넌트 관리: 특정 Identity Platform 프로젝트의 테넌트를 생성, 나열, 가져오기, 업데이트, 삭제할 수 있습니다.

  • 테넌트에서 OIDC 및 SAML 공급업체 관리: 지정된 테넌트에서 OIDC 및 SAML 구성을 프로그래매틱 방식으로 관리합니다.

시작하기 전에

지원되는 기능

다음 표에는 멀티 테넌트 환경의 각 SDK에서 지원하는 기능이 나와 있습니다.

기능 Node.js 자바 Python Go C#
커스텀 토큰 발급
ID 토큰 확인
사용자 관리
커스텀 클레임으로 액세스 제어
갱신 토큰 취소
사용자 가져오기
이메일 작업 링크 생성
다단계 인증
SAML/OIDC 제공업체 구성 관리
세션 쿠키 관리

다음 표에서는 테넌트별 컨텍스트에서 Admin SDK 및 Google Cloud Console을 사용하여 구성할 수 있는 로그인 방식을 보여줍니다.

기능 Google Cloud Console Admin SDK
이메일
OIDC
SAML
소셜
전화
다단계 인증
익명

테넌트 관리

Admin SDK를 사용하면 Google Cloud Console을 사용하는 대신 안전한 서버 환경에서 프로그래매틱 방식으로 테넌트를 관리할 수 있습니다. 여기에는 테넌트를 생성, 나열, 가져오기, 수정 또는 삭제할 수 있는 권한이 포함됩니다.

각 테넌트에는 자체 ID 공급업체, 설정, 사용자 집합이 포함되어 있습니다. 테넌트 구성 관리 작업(CRUD)은 admin.auth().tenantManager()를 사용하는 상위 프로젝트 인스턴스에서 사용할 수 있습니다.

테넌트 구성은 표시 이름, 테넌트 식별자, 이메일 인증 구성과 같은 테넌트에 대한 정보를 제공합니다.

테넌트의 다른 모든 설정(예: 허용된 도메인 및 인증된 리디렉션 URI)은 상위 프로젝트에서 상속됩니다. Google Cloud Console을 사용하여 이를 관리해야 합니다.

테넌트별 사용자 관리, 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)

자바

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)

자바

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

이 메서드는 tenantId에 해당하는 Tenant 객체를 반환합니다. 제공된 tenantId가 기존 테넌트에 속하지 않으면 반환된 프라미스가 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)

자바

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)

자바

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가 기존 테넌트에 속하지 않으면 반환된 프라미스가 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)

자바

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

삭제가 완료되면 메서드가 빈 결과를 반환합니다. 제공된 tenantId가 기존 테넌트에 속하지 않으면 반환된 프라미스가 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)

자바

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는 보안 서버 환경에서 프로그래매틱 방식으로 보안 보장 마크업 언어(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')

자바

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)

자바

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)

자바

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)

자바

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)

자바

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')

자바

tenantAuth.deleteSamlProviderConfig("saml.myProvider");

OIDC 공급업체는 Auth 프로젝트 수준 인스턴스가 아닌 해당 TenantAwareAuth 인스턴스에서 관리할 수 있다는 점을 제외하면 프로젝트 수준의 OIDC 공급업체와 유사하게 관리됩니다.

자세한 내용은 프로그래매틱 방식으로 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')

자바

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

사용자 가져오기

uid 식별자로 테넌트별 사용자를 검색할 수 있습니다.

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)

자바

// 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)

자바

// 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)

자바

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)

자바

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')

자바

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()

자바

// 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')

자바

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)

자바

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)

자바

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 토큰을 확인하고 디코딩하는 메서드가 내장되어 있습니다.

클라이언트에서 특정 테넌트에 사용자를 성공적으로 로그인한 후에 클라이언트 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')

자바

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

자바

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')

자바

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

자바

// 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

자바

// 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')

자바

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.

자바

// 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

자바

// 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'))

자바

// 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 클라이언트 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')

자바

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)

자바

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 제공된 식별자에 해당하는 테넌트가 없습니다.
auth/unsupported-tenant-operation 멀티 테넌트 컨텍스트에서는 이 작업이 지원되지 않습니다.
auth/invalid-testing-phone-number 잘못된 테스트 전화번호 또는 잘못된 테스트 코드가 제공되었습니다.
auth/test-phone-number-limit-exceeded 허용되는 테스트 전화번호 및 코드 쌍의 최대 개수를 초과했습니다.