snow · 2026.5.28 03:31 · 조회 2

Logto 멀티 테넌트 (Organizations)

LogtoIAMB2BOrganizations멀티테넌트

Logto의 Organizations 기능은 B2B SaaS 서비스에서 고객사(테넌트)별로 사용자와 권한을 독립적으로 관리할 수 있도록 지원합니다. 하나의 Logto 테넌트 안에서 여러 "조직(Organization)"을 운영하는 구조입니다.


6.1 조직(Organization) 개념

멀티 테넌트 구조 이해

Logto Tenant (your-tenant-id.logto.app)
├── Organization: 고객사 A (org-a)
│   ├── Member: user1@a.com (Admin)
│   ├── Member: user2@a.com (Member)
│   └── Member: user3@a.com (Viewer)
├── Organization: 고객사 B (org-b)
│   ├── Member: admin@b.com (Admin)
│   └── Member: dev@b.com (Member)
└── Organization: 고객사 C (org-c)
    └── Member: ceo@c.com (Admin)

각 Organization은 독립적인 멤버십과 역할 체계를 가집니다. 동일 사용자가 여러 Organization에 다른 역할로 소속될 수 있습니다.

주요 개념

개념설명
Organization고객사/팀/워크스페이스 단위
Organization RoleOrganization 내 역할 (admin, member 등)
Organization Scope조직 컨텍스트의 권한
Just-in-Time특정 이메일 도메인으로 가입 시 자동 Organization 배정

6.2 Organization 설정 및 생성

Organization 기능 활성화

Admin Console → OrganizationsSet up organization 클릭 후 기능을 활성화합니다.

Organization Role 템플릿 정의

Admin Console → OrganizationsOrganization template

Role 이름Organization Scope설명
adminread:data, write:data, manage:members, manage:settings조직 전체 관리
memberread:data, write:data일반 작업 가능
viewerread:data읽기 전용

Management API로 Organization 생성

const org = await fetch(
  'https://your-tenant-id.logto.app/api/organizations',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: '고객사 A',
      description: 'A사 전용 워크스페이스',
      customData: { plan: 'enterprise', contractEnd: '2026-12-31' },
    }),
  }
).then(r => r.json());

console.log('Organization ID:', org.id);

6.3 멤버 관리

Organization에 멤버 추가

await fetch(
  `https://your-tenant-id.logto.app/api/organizations/${orgId}/users`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ userIds: ['user-id-1', 'user-id-2'] }),
  }
);

멤버에게 Organization Role 할당

await fetch(
  `https://your-tenant-id.logto.app/api/organizations/${orgId}/users/${userId}/roles`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ organizationRoleIds: ['role-id-admin'] }),
  }
);

Just-in-Time (JIT) 프로비저닝

Admin Console → Organizations → 해당 Organization → Just-in-time provisioning

이메일 도메인: company-a.com
기본 역할:    member

user@company-a.com으로 가입하는 모든 사용자는 자동으로 해당 Organization의 member로 추가됩니다.


6.4 Organization 컨텍스트 토큰 획득

React SDK에서 Organization 토큰 요청

import { useLogto } from '@logto/react';

function useOrgAccess(organizationId: string) {
  const { getOrganizationToken } = useLogto();

  const callOrgApi = async (path: string) => {
    const token = await getOrganizationToken(organizationId);

    return fetch(`https://api.yourapp.com${path}`, {
      headers: {
        Authorization: `Bearer ${token}`,
        'X-Organization-Id': organizationId,
      },
    });
  };

  return { callOrgApi };
}

Organization 토큰 payload 예시

{
  "sub": "user-id",
  "organization_id": "org-a",
  "organization_roles": ["admin"],
  "scope": "read:data write:data manage:members manage:settings",
  "aud": "https://api.yourapp.com",
  "iss": "https://your-tenant-id.logto.app/oidc"
}

백엔드에서 Organization 권한 검증

function requireOrgRole(role: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const orgRoles = req.user?.organization_roles ?? [];
    if (!orgRoles.includes(role)) {
      return res.status(403).json({ error: 'Insufficient organization role' });
    }
    next();
  };
}

router.delete(
  '/orgs/:orgId/members/:userId',
  requireAuth,
  requireOrgRole('admin'),
  async (req, res) => {
    await OrgService.removeMember(req.params.orgId, req.params.userId);
    res.status(204).send();
  }
);

6.5 사용자의 Organization 목록 조회

import { LogtoConfig, UserScope } from '@logto/react';

const config: LogtoConfig = {
  endpoint: 'https://your-tenant-id.logto.app',
  appId: 'your-app-id',
  scopes: [UserScope.Organizations, UserScope.Profile, UserScope.Email],
};
function OrganizationSelector() {
  const { fetchUserInfo } = useLogto();
  const [orgs, setOrgs] = useState([]);

  useEffect(() => {
    fetchUserInfo().then(user => setOrgs(user.organizations ?? []));
  }, []);

  return (
    <div>
      <h3>워크스페이스 선택</h3>
      {orgs.map(org => (
        <button key={org.id} onClick={() => selectOrg(org.id)}>
          {org.name} ({org.role})
        </button>
      ))}
    </div>
  );
}

다음 단계

멀티 테넌트 설정이 완료되었습니다. 로그인 화면을 브랜드에 맞게 커스터마이징하는 방법을 알아보십시오.

다음: Logto 로그인 UI 커스터마이징 — 브랜딩, CSS, 커스텀 도메인


참고: Logto Organizations — https://docs.logto.io/organizations

댓글

아직 댓글이 없습니다.

댓글을 작성하려면 로그인이 필요합니다.