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 Role | Organization 내 역할 (admin, member 등) |
| Organization Scope | 조직 컨텍스트의 권한 |
| Just-in-Time | 특정 이메일 도메인으로 가입 시 자동 Organization 배정 |
6.2 Organization 설정 및 생성
Organization 기능 활성화
Admin Console → Organizations → Set up organization 클릭 후 기능을 활성화합니다.
Organization Role 템플릿 정의
Admin Console → Organizations → Organization template
| Role 이름 | Organization Scope | 설명 |
|---|---|---|
admin | read:data, write:data, manage:members, manage:settings | 조직 전체 관리 |
member | read:data, write:data | 일반 작업 가능 |
viewer | read: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
댓글
아직 댓글이 없습니다.
댓글을 작성하려면 로그인이 필요합니다.