본문으로 건너뛰기

테넌트 관리 자동화

Logto Cloud 테넌트는 프로그래밍 방식으로 관리할 수 있습니다. 테넌트 생성 및 추가 설정을 Console로 전환하지 않고도 계속 진행할 수 있습니다.

이는 자체 온보딩 플로우, 내부 플랫폼, AI 에이전트, 또는 통합 자동화에서 테넌트를 프로비저닝해야 할 때 유용합니다.

자동화 흐름은 다음과 같습니다:

  1. **Logto Cloud 개인 액세스 토큰 (PAT)**을 사용하여 Logto Cloud API를 호출합니다.
  2. POST /api/tenants로 테넌트를 생성합니다.
  3. 생성 응답에서 기본 기계 간 (M2M) 애플리케이션 자격 증명을 읽어옵니다.
  4. 기본 M2M 애플리케이션을 사용하여 새 테넌트의 Management API 액세스 토큰을 얻습니다.
  5. 새 테넌트의 Management API를 호출하여 애플리케이션, 사용자, 역할, 리소스, 조직, 기타 설정을 계속 프로비저닝합니다.

시작하기 전에

다음 값을 준비하세요:

VariableDescription
CLOUD_API_ENDPOINTLogto Cloud API 엔드포인트. Logto Cloud의 경우 https://cloud.logto.io를 사용하세요.
LOGTO_CLOUD_PATLogto Cloud 계정의 PAT.
TENANT_NAME생성할 테넌트의 표시 이름.
TENANT_TAG테넌트 유형. development 또는 production을 사용하세요.
REGION_NAME테넌트의 지역 식별자.

환경 변수로 설정하세요:

export CLOUD_API_ENDPOINT="https://cloud.logto.io"
export LOGTO_CLOUD_PAT="<logto-cloud-pat>"
export TENANT_NAME="My automated tenant"
export TENANT_TAG="development"
export REGION_NAME="<region-name>"

사용 가능한 지역 가져오기

테넌트를 생성하기 전에, Logto Cloud 계정에서 사용 가능한 지역을 조회하세요:

curl "$CLOUD_API_ENDPOINT/api/me/regions" \
-H "Authorization: Bearer $LOGTO_CLOUD_PAT"

응답에는 사용 가능한 지역이 포함되어 있습니다. 테넌트 생성 시 REGION_NAME으로 name 값을 사용하세요.

예시 응답:

{
"regions": [
{
"name": "EU",
"displayName": "Europe"
},
{
"name": "US",
"displayName": "United States"
}
]
}

테넌트 생성하기

Logto Cloud PAT으로 POST /api/tenants를 호출하세요:

curl "$CLOUD_API_ENDPOINT/api/tenants" \
-X POST \
-H "Authorization: Bearer $LOGTO_CLOUD_PAT" \
-H "Content-Type: application/json" \
-d '{
"name": "'"$TENANT_NAME"'",
"tag": "'"$TENANT_TAG"'",
"regionName": "'"$REGION_NAME"'"
}'

응답에는 생성된 테넌트와 기본 M2M 애플리케이션이 포함됩니다. M2M 애플리케이션은 새 테넌트에 생성되며, 해당 테넌트의 Management API에 접근할 수 있습니다.

예시 응답:

{
"id": "new-tenant-id",
"name": "My automated tenant",
"tag": "development",
"indicator": "https://new-tenant-id.logto.app",
"regionName": "EU",
"defaultApplication": {
"id": "default-m2m-app-id",
"secret": "default-m2m-app-secret"
}
}

다음 단계에 필요한 값을 저장하세요:

export TENANT_ID="<response.id>"
export TENANT_ENDPOINT="<response.indicator>"
export DEFAULT_M2M_APP_ID="<response.defaultApplication.id>"
export DEFAULT_M2M_APP_SECRET="<response.defaultApplication.secret>"

새 테넌트의 Management API 액세스 토큰 받기

기본 M2M 애플리케이션 자격 증명을 사용하여 새 테넌트에서 액세스 토큰을 요청하세요:

curl "$TENANT_ENDPOINT/oidc/token" \
-X POST \
-u "$DEFAULT_M2M_APP_ID:$DEFAULT_M2M_APP_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "resource=$TENANT_ENDPOINT/api" \
-d "scope=all"

예시 응답:

{
"access_token": "eyJ...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "all"
}

액세스 토큰을 저장하세요:

export MANAGEMENT_API_ACCESS_TOKEN="<response.access_token>"

새 테넌트 프로비저닝 계속하기

Management API 액세스 토큰을 사용하여 새 테넌트의 Management API를 호출하세요.

예를 들어, 애플리케이션 목록 조회:

curl "$TENANT_ENDPOINT/api/applications" \
-H "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN"

또는 애플리케이션 생성:

curl "$TENANT_ENDPOINT/api/applications" \
-X POST \
-H "Authorization: Bearer $MANAGEMENT_API_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My web app",
"type": "SPA",
"oidcClientMetadata": {
"redirectUris": ["https://example.com/callback"],
"postLogoutRedirectUris": ["https://example.com"]
}
}'

이 시점에서, 자동화는 사용자, 애플리케이션, API 리소스, 역할, 조직, 커넥터, 로그인 경험 설정 등 모든 Management API 작업을 계속할 수 있습니다.

전체 자동화 예시

다음 Node.js 예시는 테넌트를 생성하고, 반환된 기본 M2M 자격 증명으로 Management API 액세스 토큰을 교환한 뒤, 새 테넌트의 애플리케이션 목록을 조회합니다:

const cloudApiEndpoint = 'https://cloud.logto.io';
const logtoCloudPat = process.env.LOGTO_CLOUD_PAT;

const createTenantResponse = await fetch(`${cloudApiEndpoint}/api/tenants`, {
method: 'POST',
headers: {
authorization: `Bearer ${logtoCloudPat}`,
'content-type': 'application/json',
},
body: JSON.stringify({
name: 'My automated tenant',
tag: 'development',
regionName: 'EU',
}),
});

if (!createTenantResponse.ok) {
throw new Error(`테넌트 생성 실패: ${await createTenantResponse.text()}`);
}

const tenant = await createTenantResponse.json();
const tenantEndpoint = tenant.indicator;
const { id: appId, secret: appSecret } = tenant.defaultApplication;

const tokenResponse = await fetch(`${tenantEndpoint}/oidc/token`, {
method: 'POST',
headers: {
authorization: `Basic ${Buffer.from(`${appId}:${appSecret}`).toString('base64')}`,
'content-type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'client_credentials',
resource: `${tenantEndpoint}/api`,
scope: 'all',
}),
});

if (!tokenResponse.ok) {
throw new Error(`Management API 토큰 획득 실패: ${await tokenResponse.text()}`);
}

const { access_token: managementApiAccessToken } = await tokenResponse.json();

const applicationsResponse = await fetch(`${tenantEndpoint}/api/applications`, {
headers: {
authorization: `Bearer ${managementApiAccessToken}`,
},
});

if (!applicationsResponse.ok) {
throw new Error(`애플리케이션 목록 조회 실패: ${await applicationsResponse.text()}`);
}

const applications = await applicationsResponse.json();

console.log({ tenantId: tenant.id, applications });