snow · 2026.5.27 13:45 · 조회 1
Logto 인가(Authorization) 설정
인증(Authentication)이 "누구인가?"를 확인한다면, 인가(Authorization)는 "무엇을 할 수 있는가?"를 결정합니다. Logto는 OAuth 2.0 기반 API 리소스 보호와 RBAC(Role-Based Access Control)를 지원합니다.
5.1 API 리소스 등록
API 리소스는 보호할 백엔드 API의 식별자(Resource Indicator)입니다. 클라이언트가 Access Token을 요청할 때 어떤 API에 접근할지 명시하는 데 사용됩니다.
API 리소스 생성
Admin Console → API resources → Create API resource
이름: My Backend API
API Identifier (Resource): https://api.yourapp.com
Token expiration: 3600 (초)
API Identifier는 실제 호출 URL일 필요는 없지만, 관례적으로 API 기본 URL을 사용합니다.
API 퍼미션(Scope) 정의
API 리소스를 생성한 후, 해당 API에서 지원하는 권한(Scope)을 정의합니다.
예시 — 블로그 API:
| Scope 이름 | 설명 |
|---|---|
read:posts | 게시글 조회 |
write:posts | 게시글 생성/수정 |
delete:posts | 게시글 삭제 |
admin:all | 모든 관리 권한 |
클라이언트에서 Access Token 요청
1import { useLogto } from '@logto/react';2 3function useApiAccess() {4 const { getAccessToken } = useLogto();5 6 const callApi = async (path: string) => {7 const token = await getAccessToken('https://api.yourapp.com');8 9 return fetch(`https://api.yourapp.com${path}`, {10 headers: { Authorization: `Bearer ${token}` },11 });12 };13 14 return { callApi };15}5.2 RBAC 설정
RBAC(역할 기반 접근 제어)는 권한(Scope)을 역할(Role)에 묶고, 역할을 사용자에게 할당하는 방식입니다.
역할(Role) 생성
Admin Console → Roles → Create role
예시 역할 구성:
| 역할 이름 | 포함 Scope | 설명 |
|---|---|---|
viewer | read:posts | 읽기 전용 |
editor | read:posts, write:posts | 작성 가능 |
admin | read:posts, write:posts, delete:posts, admin:all | 전체 권한 |
사용자에게 역할 할당
Admin Console에서 할당:
Users → 사용자 선택 → Roles → Assign roles → viewer / editor / admin 선택
Management API로 할당:
1await fetch(2 `https://your-tenant-id.logto.app/api/users/${userId}/roles`,3 {4 method: 'POST',5 headers: {6 Authorization: `Bearer ${managementToken}`,7 'Content-Type': 'application/json',8 },9 body: JSON.stringify({ roleIds: ['role-id-editor'] }),10 }11);Access Token payload 예시
1{2 "sub": "user-id",3 "iss": "https://your-tenant-id.logto.app/oidc",4 "aud": "https://api.yourapp.com",5 "scope": "read:posts write:posts",6 "exp": 1234567890,7 "iat": 12345642908}5.3 백엔드 API에서 토큰 검증
Node.js/Express 예시 (jose 라이브러리)
1npm install jose1import { createRemoteJWKSet, jwtVerify } from 'jose';2import { Request, Response, NextFunction } from 'express';3 4const JWKS_URI = 'https://your-tenant-id.logto.app/oidc/jwks';5const ISSUER = 'https://your-tenant-id.logto.app/oidc';6const AUDIENCE = 'https://api.yourapp.com';7 8const jwks = createRemoteJWKSet(new URL(JWKS_URI));9 10export async function requireAuth(req: Request, res: Response, next: NextFunction) {11 const authHeader = req.headers.authorization;12 13 if (!authHeader?.startsWith('Bearer ')) {14 return res.status(401).json({ error: 'Missing token' });15 }16 17 try {18 const { payload } = await jwtVerify(authHeader.slice(7), jwks, {19 issuer: ISSUER,20 audience: AUDIENCE,21 });22 23 req.user = {24 id: payload.sub!,25 scopes: (payload.scope as string ?? '').split(' '),26 };27 28 next();29 } catch (err) {30 res.status(401).json({ error: 'Invalid token' });31 }32}33 34export function requireScope(scope: string) {35 return (req: Request, res: Response, next: NextFunction) => {36 if (!req.user?.scopes.includes(scope)) {37 return res.status(403).json({ error: 'Insufficient permissions' });38 }39 next();40 };41}라우터에 미들웨어 적용
1router.get('/posts', requireAuth, requireScope('read:posts'), async (req, res) => {2 res.json(await PostService.findAll());3});4 5router.post('/posts', requireAuth, requireScope('write:posts'), async (req, res) => {6 res.status(201).json(await PostService.create(req.body, req.user!.id));7});8 9router.delete('/posts/:id', requireAuth, requireScope('delete:posts'), async (req, res) => {10 await PostService.delete(req.params.id);11 res.status(204).send();12});Python/FastAPI 예시
1from fastapi import Depends, HTTPException, status2from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials3from jose import jwt4import httpx5 6JWKS_URI = "https://your-tenant-id.logto.app/oidc/jwks"7ISSUER = "https://your-tenant-id.logto.app/oidc"8AUDIENCE = "https://api.yourapp.com"9 10security = HTTPBearer()11 12async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):13 async with httpx.AsyncClient() as client:14 jwks = (await client.get(JWKS_URI)).json()15 16 try:17 return jwt.decode(credentials.credentials, jwks, algorithms=["RS256"],18 audience=AUDIENCE, issuer=ISSUER)19 except Exception:20 raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")21 22def require_scope(required_scope: str):23 async def checker(user = Depends(get_current_user)):24 if required_scope not in user.get("scope", "").split():25 raise HTTPException(status_code=403, detail="Insufficient scope")26 return user27 return checker다음 단계
API 보호와 RBAC 설정이 완료되었습니다. 여러 조직을 지원하는 멀티 테넌트 구조를 알아보십시오.
다음: Logto 멀티 테넌트 (Organizations) — 조직 개념, 멤버 관리, 조직별 RBAC
참고: Logto Authorization — https://docs.logto.io/authorization
댓글
아직 댓글이 없습니다.
댓글을 작성하려면 로그인이 필요합니다.