Auth0 커넥터
Tajo를 통해 Auth0를 Brevo에 연결하여 인증된 사용자 프로필을 마케팅 연락처로 동기화하고, 인증 이벤트 기반의 자동화를 트리거하며, 신원 및 액세스 관리 인사이트로 고객 데이터를 풍부하게 만드세요.
개요
| 속성 | 값 |
|---|---|
| 플랫폼 | Auth0 (by Okta) |
| 카테고리 | Identity & Access (Custom) |
| 설정 난이도 | 보통 |
| 공식 통합 | 아니오 |
| 동기화되는 데이터 | 사용자, 이벤트, 역할, 신원 |
| 인증 방식 | Machine-to-Machine OAuth 2.0 |
주요 기능
- 사용자 프로필 동기화 - Auth0 사용자 프로필을 Brevo 연락처에 동기화합니다
- 인증 이벤트 - 로그인, 가입, 비밀번호 재설정 시 자동화를 트리거합니다
- 역할 기반 세분화 - Auth0 역할 및 권한을 기반으로 연락처를 세분화합니다
- 소셜 신원 데이터 - 소셜 로그인 프로필 정보로 연락처를 풍부하게 만듭니다
- 로그인 활동 추적 - 마지막 로그인, 로그인 횟수, 디바이스 데이터를 추적합니다
- 멀티 테넌트 지원 - 여러 Auth0 테넌트의 사용자를 동기화합니다
사전 준비 사항
시작하기 전에 다음 사항이 준비되어 있어야 합니다.
- API 액세스가 있는 Auth0 계정
- Auth0에 등록된 Machine-to-Machine 애플리케이션
- M2M 애플리케이션에 부여된 Management API 권한
- API 액세스가 활성화된 Brevo 계정
- 커넥터 권한이 있는 Tajo 계정
인증
Machine-to-Machine OAuth 2.0
# Create an M2M application in Auth0 Dashboardexport AUTH0_DOMAIN=your-tenant.auth0.comexport AUTH0_CLIENT_ID=your_client_idexport AUTH0_CLIENT_SECRET=your_client_secretexport AUTH0_AUDIENCE=https://your-tenant.auth0.com/api/v2/// Get Management API access tokenconst tokenResponse = await fetch( `https://${process.env.AUTH0_DOMAIN}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_id: process.env.AUTH0_CLIENT_ID, client_secret: process.env.AUTH0_CLIENT_SECRET, audience: process.env.AUTH0_AUDIENCE, grant_type: 'client_credentials' }) });
const { access_token } = await tokenResponse.json();// Token is valid for 24 hours by defaultAPI 권한
M2M 애플리케이션에는 필요한 스코프만 부여하세요: read:users, read:user_idp_tokens, read:roles, read:logs. 필요하지 않은 경우 write 권한 부여는 피하세요.
설정
기본 설정
connectors: auth0: enabled: true domain: "${AUTH0_DOMAIN}" client_id: "${AUTH0_CLIENT_ID}" client_secret: "${AUTH0_CLIENT_SECRET}" audience: "https://${AUTH0_DOMAIN}/api/v2/"
sync: users: true events: true roles: true schedule: "0 */4 * * *" # Every 4 hours
lists: all_users: 20 verified_users: 21 social_login: 22필드 매핑
field_mapping: email: email given_name: FIRSTNAME family_name: LASTNAME nickname: NICKNAME picture: AVATAR_URL email_verified: EMAIL_VERIFIED logins_count: LOGIN_COUNT last_login: LAST_LOGIN_DATE created_at: SIGNUP_DATE user_metadata.phone: SMS user_metadata.company: COMPANY app_metadata.plan: SUBSCRIPTION_PLAN app_metadata.role: USER_ROLEAPI 엔드포인트
| 엔드포인트 | Method | 설명 |
|---|---|---|
https://{domain}/api/v2/users | GET | 사용자 목록 또는 검색 |
https://{domain}/api/v2/users/{id} | GET | 사용자 조회 |
https://{domain}/api/v2/users/{id} | PATCH | 사용자 메타데이터 업데이트 |
https://{domain}/api/v2/users/{id}/roles | GET | 사용자 역할 조회 |
https://{domain}/api/v2/roles | GET | 모든 역할 목록 |
https://{domain}/api/v2/logs | GET | 로그 이벤트 조회 |
https://{domain}/api/v2/stats/active-users | GET | 활성 사용자 수 조회 |
https://{domain}/api/v2/stats/daily | GET | 일일 통계 조회 |
https://{domain}/oauth/token | POST | 액세스 토큰 조회 |
코드 예제
커넥터 초기화
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('auth0', { domain: process.env.AUTH0_DOMAIN, clientId: process.env.AUTH0_CLIENT_ID, clientSecret: process.env.AUTH0_CLIENT_SECRET});사용자를 Brevo로 동기화
// Paginate through Auth0 userslet page = 0;const perPage = 50;let hasMore = true;
while (hasMore) { const response = await fetch( `https://${domain}/api/v2/users?` + new URLSearchParams({ page: page.toString(), per_page: perPage.toString(), include_totals: 'true', search_engine: 'v3', q: 'email_verified:true' }), { headers: { 'Authorization': `Bearer ${accessToken}` } } );
const { users, total } = await response.json();
for (const user of users) { await tajo.contacts.sync({ email: user.email, attributes: { FIRSTNAME: user.given_name, LASTNAME: user.family_name, LOGIN_COUNT: user.logins_count, LAST_LOGIN_DATE: user.last_login, SIGNUP_DATE: user.created_at, EMAIL_VERIFIED: user.email_verified }, listIds: [20] }); }
page++; hasMore = (page * perPage) < total;}Log Streams를 통한 인증 이벤트 추적
// Set up Auth0 Log Stream webhook// Configure in Auth0 Dashboard > Monitoring > Streams
app.post('/webhooks/auth0', async (req, res) => { // Verify authorization header const authHeader = req.headers.authorization; if (authHeader !== `Bearer ${process.env.AUTH0_WEBHOOK_TOKEN}`) { return res.status(401).send('Unauthorized'); }
const logs = req.body;
for (const log of logs) { switch (log.data.type) { case 's': // Successful login await tajo.events.track({ email: log.data.details.email, event: 'user_login', properties: { ip: log.data.ip, user_agent: log.data.user_agent, connection: log.data.connection } }); break; case 'ss': // Successful signup await tajo.contacts.sync({ email: log.data.details.email, attributes: { SIGNUP_DATE: log.data.date }, listIds: [20] }); break; case 'sp': // Successful password change await tajo.events.track({ email: log.data.details.email, event: 'password_changed' }); break; } }
res.status(200).send('OK');});역할 기반 세분화
// Sync user roles for segmentationconst rolesResponse = await fetch( `https://${domain}/api/v2/users/${userId}/roles`, { headers: { 'Authorization': `Bearer ${accessToken}` } });
const roles = await rolesResponse.json();const roleNames = roles.map(r => r.name).join(', ');
await tajo.contacts.update(userEmail, { attributes: { USER_ROLE: roleNames, IS_ADMIN: roles.some(r => r.name === 'admin') }});요청 제한
| 엔드포인트 카테고리 | 제한 | 비고 |
|---|---|---|
| Management API | 초당 50회 (Free) | 테넌트당 |
| Management API | 초당 100회 (Paid) | 테넌트당 |
| Authentication API | 다양 | 플랜에 따라 다름 |
| Log Streams | 실시간 | 전송에 요청 제한 없음 |
| 페이지네이션 | 페이지당 최대 50개 항목 | page와 per_page 파라미터 사용 |
페이지네이션 필수
Auth0 Management API는 페이지당 최대 50개의 결과를 반환합니다. 항상 page와 per_page 파라미터를 사용해 페이지네이션을 구현하세요. 전체 개수를 얻으려면 include_totals=true를 포함하세요.
문제 해결
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| 401 Unauthorized | 토큰 만료 | 새 M2M 토큰 요청 (24시간 만료) |
| 403 Forbidden | 스코프 누락 | M2M 앱에 필요한 권한을 부여하세요 |
| 빈 사용자 목록 | 검색 쿼리 오류 | v3 엔진용 Lucene 쿼리 구문을 사용하세요 |
| 메타데이터 누락 | 메타데이터가 설정되지 않음 | user_metadata와 app_metadata를 확인하세요 |
| Rate limit 429 | 요청이 너무 많음 | retry 헤더를 사용해 백오프를 구현하세요 |
디버그 모드
connectors: auth0: debug: true log_level: verbose log_sync: true모범 사례
- Log Streams 사용 - Logs API를 폴링하는 대신 실시간 이벤트 스트리밍을 사용하세요
- 페이지네이션 구현 - 대규모 테넌트의 경우 항상 사용자 목록 쿼리를 페이지네이션하세요
- M2M 토큰 캐싱 - 만료 직전까지 토큰을 재사용하세요 (기본 수명 24시간)
- 검색 엔진 v3 사용 - 효율적인 사용자 검색을 위해 Lucene 쿼리 구문을 사용하세요
- 검증된 사용자만 동기화 - 미검증 연락처를 피하기 위해
email_verified:true로 필터링하세요 - 사용자 메타데이터 활용 - 동기화를 위해 Auth0 user_metadata에 커스텀 속성을 저장하세요
보안
- Machine-to-Machine OAuth - 서버 간 인증을 위한 client credentials grant
- 스코프 기반 권한 - 최소한의 필요한 Management API 스코프를 부여하세요
- 토큰 순환 - M2M 토큰은 기본적으로 24시간 후 만료됩니다
- Log Stream 인증 - 웹훅 엔드포인트에 대해 bearer 토큰 검증을 사용하세요
- 테넌트 격리 - Auth0 테넌트별로 구성을 분리하세요
- 암호화된 전송 - 모든 API 통신에 TLS 1.2+ 사용