UNPKG

@gftdcojp/gftd-orm

Version:

Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture

1,331 lines 50.6 kB
/** * Auth0統合 - Auth0 JWTトークンとAuthorization Extension APIとの連携 */ import jwt from 'jsonwebtoken'; // @ts-ignore import jwksClient from 'jwks-rsa'; import { AuditLogManager, AuditEventType, AuditLogLevel } from './types'; import { log } from './utils/logger'; /** * Auth0統合マネージャー */ export class Auth0Integration { constructor() { this.extensionAccessToken = null; this.extensionTokenExpiry = 0; // 🔐 統一認証ドメイン設定: auth.gftd.ai をデフォルトに const authDomain = process.env.GFTD_AUTH0_DOMAIN || process.env.AUTH0_DOMAIN || 'auth.gftd.ai'; this.config = { domain: authDomain, audience: process.env.GFTD_AUTH0_AUDIENCE || process.env.AUTH0_AUDIENCE || `https://${authDomain}/api/v2/`, clientId: process.env.GFTD_AUTH0_CLIENT_ID || process.env.AUTH0_CLIENT_ID || 'k0ziPQ6IkDxE1AUSvzx5PwXtnf4y81x0', jwksUri: process.env.GFTD_AUTH0_JWKS_URI || process.env.AUTH0_JWKS_URI, // 🔐 NEW: Authorization Extension設定 authorizationExtension: { url: this.buildExtensionUrl(authDomain, process.env.GFTD_AUTH0_REGION || 'us-west'), clientId: process.env.GFTD_AUTH0_EXT_CLIENT_ID || '', clientSecret: process.env.GFTD_AUTH0_EXT_CLIENT_SECRET || '', audience: process.env.GFTD_AUTH0_EXT_AUDIENCE || 'auth0-authorization-extension-api', region: process.env.GFTD_AUTH0_REGION || 'us-west', }, }; // JWKS URIが指定されていない場合はドメインから自動生成 if (!this.config.jwksUri) { this.config.jwksUri = `https://${this.config.domain}/.well-known/jwks.json`; } // JWKSクライアントを初期化 const jwksUri = this.config.jwksUri || `https://${this.config.domain}/.well-known/jwks.json`; this.jwksClient = jwksClient({ jwksUri, requestHeaders: {}, // 必要に応じてヘッダーを追加 timeout: 30000, // 30秒 cache: true, rateLimit: true, jwksRequestsPerMinute: 5, cacheMaxAge: 24 * 60 * 60 * 1000, // 24時間 }); log.info(`Auth0 integration initialized for domain: ${this.config.domain}`); if (this.config.authorizationExtension?.clientId) { log.info(`Auth0 Authorization Extension enabled: ${this.config.authorizationExtension.url}`); } } /** * 🔐 NEW: Extension URLを構築 */ buildExtensionUrl(domain, region) { const tenant = domain.replace('.auth0.com', ''); const extensionId = 'adf6e2f2b84784b57522e3b19dfc9201'; // Auth0 Extension ID switch (region) { case 'europe': return `https://${tenant}.eu.webtask.io/${extensionId}/api`; case 'australia': return `https://${tenant}.au.webtask.io/${extensionId}/api`; default: // us-west return `https://${tenant}.us.webtask.io/${extensionId}/api`; } } /** * シングルトンインスタンスを取得 */ static getInstance(customConfig) { if (!Auth0Integration.instance) { Auth0Integration.instance = new Auth0Integration(); } // カスタム設定が渡された場合は設定を更新 if (customConfig) { Auth0Integration.instance.updateConfig(customConfig); } return Auth0Integration.instance; } /** * 設定を更新 */ updateConfig(customConfig) { this.config = { ...this.config, ...customConfig, }; // JWKS URIを更新 if (customConfig.domain && !customConfig.jwksUri) { this.config.jwksUri = `https://${this.config.domain}/.well-known/jwks.json`; } // Extension URLを更新 if (customConfig.domain && this.config.authorizationExtension) { this.config.authorizationExtension.url = this.buildExtensionUrl(this.config.domain, this.config.authorizationExtension.region); } // JWKSクライアントを再初期化 this.jwksClient = jwksClient({ jwksUri: this.config.jwksUri, requestHeaders: {}, timeout: 30000, cache: true, rateLimit: true, jwksRequestsPerMinute: 5, cacheMaxAge: 24 * 60 * 60 * 1000, }); log.info(`Auth0 configuration updated for domain: ${this.config.domain}`); } /** * 🔐 NEW: Machine-to-Machine認証でExtension Access Tokenを取得 */ async getExtensionAccessToken() { const now = Date.now(); // トークンが有効であればそのまま返す if (this.extensionAccessToken && now < this.extensionTokenExpiry) { return this.extensionAccessToken; } const { authorizationExtension } = this.config; if (!authorizationExtension?.clientId || !authorizationExtension?.clientSecret) { throw new Error('Authorization Extension configuration is missing'); } try { const response = await fetch(`https://${this.config.domain}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ client_id: authorizationExtension.clientId, client_secret: authorizationExtension.clientSecret, audience: authorizationExtension.audience, grant_type: 'client_credentials', }), }); if (!response.ok) { throw new Error(`Auth0 OAuth error: ${response.status}`); } const data = await response.json(); this.extensionAccessToken = data.access_token; this.extensionTokenExpiry = now + (data.expires_in * 1000) - 60000; // 1分のマージン log.info('Extension Access Token acquired'); return this.extensionAccessToken; } catch (error) { log.error(`Failed to get Extension Access Token: ${error}`); throw error; } } /** * 🔐 NEW: Extension APIリクエストヘルパー */ async extensionApiRequest(endpoint, method = 'GET', body) { const { authorizationExtension } = this.config; if (!authorizationExtension?.url) { throw new Error('Authorization Extension URL is not configured'); } const token = await this.getExtensionAccessToken(); const url = `${authorizationExtension.url}${endpoint}`; const response = await fetch(url, { method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Extension API error (${response.status}): ${errorText}`); } // DELETEリクエストは通常空のレスポンスを返す if (method === 'DELETE') { return {}; } return response.json(); } /** * Auth0 JWTトークンを検証 */ async verifyAuth0Token(token) { try { return new Promise((resolve, reject) => { // JWTヘッダーからkidを取得 const decoded = jwt.decode(token, { complete: true }); if (!decoded || !decoded.header.kid) { reject(new Error('Invalid token: missing kid')); return; } // JWKSから署名キーを取得 this.jwksClient.getSigningKey(decoded.header.kid, (err, key) => { if (err) { reject(err); return; } const signingKey = key.getPublicKey(); // トークンを検証 jwt.verify(token, signingKey, { audience: this.config.audience, issuer: `https://${this.config.domain}/`, algorithms: ['RS256'], }, (verifyErr, payload) => { if (verifyErr) { reject(verifyErr); return; } resolve(payload); }); }); }); } catch (error) { log.error(`Auth0 token verification failed: ${error}`); return null; } } /** * Auth0クレームをGFTD ORMユーザーペイロードに変換 */ mapAuth0ToUserPayload(auth0Claims) { // カスタムクレームからロールとテナントIDを取得 const roles = auth0Claims['https://your-app.com/roles'] || []; const permissions = auth0Claims['https://your-app.com/permissions'] || []; const tenantId = auth0Claims['https://your-app.com/tenant_id'] || 'default'; // ロールの決定(最初に見つかったロールを使用) let userRole = 'authenticated'; if (roles.includes('admin') || roles.includes('service_role')) { userRole = 'service_role'; } else if (roles.includes('user') || auth0Claims.email_verified) { userRole = 'authenticated'; } const userPayload = { sub: auth0Claims.sub, email: auth0Claims.email, role: userRole, tenant_id: tenantId, metadata: { auth0_user_id: auth0Claims.sub, email_verified: auth0Claims.email_verified, name: auth0Claims.name, picture: auth0Claims.picture, nickname: auth0Claims.nickname, roles, permissions, }, app_metadata: { provider: 'auth0', domain: this.config.domain, client_id: this.config.clientId, }, user_metadata: { email: auth0Claims.email, name: auth0Claims.name, picture: auth0Claims.picture, }, }; return userPayload; } /** * Auth0トークンからGFTD ORMユーザーを認証 */ async authenticateWithAuth0(token) { try { // Auth0トークンを検証 const auth0Claims = await this.verifyAuth0Token(token); if (!auth0Claims) { return { success: false, error: 'Invalid Auth0 token', }; } // GFTD ORMユーザーペイロードに変換 const user = this.mapAuth0ToUserPayload(auth0Claims); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.AUTH_LOGIN, userId: user.sub, tenantId: user.tenant_id, result: 'SUCCESS', message: `Auth0 authentication successful for user ${user.sub}`, details: { auth0_domain: this.config.domain, email: user.email, roles: user.metadata?.roles, }, }); log.info(`Auth0 authentication successful for user: ${user.sub}`); return { success: true, user, }; } catch (error) { log.error(`Auth0 authentication failed: ${error}`); AuditLogManager.log({ level: AuditLogLevel.ERROR, eventType: AuditEventType.AUTH_FAILED, result: 'FAILURE', message: `Auth0 authentication failed: ${error}`, details: { token: token.substring(0, 50) + '...' }, }); return { success: false, error: String(error), }; } } /** * Auth0権限をチェック */ checkAuth0Permission(user, permission) { const permissions = user.metadata?.permissions || []; const roles = user.metadata?.roles || []; // 管理者は全権限を持つ if (roles.includes('admin') || user.role === 'service_role') { return true; } // 特定の権限をチェック return permissions.includes(permission); } /** * Auth0ロールをチェック */ checkAuth0Role(user, role) { const roles = user.metadata?.roles || []; return roles.includes(role); } /** * Auth0のManagement APIを使ってユーザー情報を取得 */ async getAuth0UserInfo(managementToken, userId) { try { const response = await fetch(`https://${this.config.domain}/api/v2/users/${userId}`, { headers: { Authorization: `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error(`Auth0 API error: ${response.status}`); } const userInfo = await response.json(); return userInfo; } catch (error) { log.error(`Failed to get Auth0 user info: ${error}`); throw error; } } /** * Auth0のManagement APIを使ってユーザーロールを更新 */ async updateAuth0UserRoles(managementToken, userId, roles) { try { const response = await fetch(`https://${this.config.domain}/api/v2/users/${userId}/roles`, { method: 'POST', headers: { Authorization: `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ roles }), }); if (!response.ok) { throw new Error(`Auth0 API error: ${response.status}`); } log.info(`Updated Auth0 user roles for ${userId}: ${roles.join(', ')}`); } catch (error) { log.error(`Failed to update Auth0 user roles: ${error}`); throw error; } } /** * �� NEW: Extension API - 全グループを取得 */ async getGroups() { return this.extensionApiRequest('/groups'); } /** * 🔐 NEW: Extension API - 特定のグループを取得 */ async getGroup(groupId, expand) { const endpoint = expand ? `/groups/${groupId}?expand` : `/groups/${groupId}`; return this.extensionApiRequest(endpoint); } /** * 🔐 NEW: Extension API - グループを作成 */ async createGroup(name, description) { return this.extensionApiRequest('/groups', 'POST', { name, description }); } /** * 🔐 NEW: Extension API - グループを更新 */ async updateGroup(groupId, updates) { return this.extensionApiRequest(`/groups/${groupId}`, 'PUT', updates); } /** * 🔐 NEW: Extension API - グループを削除 */ async deleteGroup(groupId) { await this.extensionApiRequest(`/groups/${groupId}`, 'DELETE'); } /** * 🔐 NEW: Extension API - 全ロールを取得 */ async getRoles() { return this.extensionApiRequest('/roles'); } /** * 🔐 NEW: Extension API - 特定のロールを取得 */ async getRole(roleId) { return this.extensionApiRequest(`/roles/${roleId}`); } /** * 🔐 NEW: Extension API - ロールを作成 */ async createRole(name, description, applicationId) { return this.extensionApiRequest('/roles', 'POST', { name, description, applicationId }); } /** * 🔐 NEW: Extension API - ロールを更新 */ async updateRole(roleId, updates) { return this.extensionApiRequest(`/roles/${roleId}`, 'PUT', updates); } /** * 🔐 NEW: Extension API - ロールを削除 */ async deleteRole(roleId) { await this.extensionApiRequest(`/roles/${roleId}`, 'DELETE'); } /** * 🔐 NEW: Extension API - ユーザーのロールを取得 */ async getUserRoles(userId) { return this.extensionApiRequest(`/users/${userId}/roles`); } /** * 🔐 NEW: Extension API - ユーザーにロールを追加 */ async addUserToRoles(userId, roleIds) { await this.extensionApiRequest(`/users/${userId}/roles`, 'PATCH', roleIds); } /** * 🔐 NEW: Extension API - ユーザーからロールを削除 */ async removeUserFromRoles(userId, roleIds) { await this.extensionApiRequest(`/users/${userId}/roles`, 'DELETE', roleIds); } /** * 🔐 NEW: Extension API - ユーザーのロールを計算(グループ含む) */ async calculateUserRoles(userId) { return this.extensionApiRequest(`/users/${userId}/roles/calculate`); } /** * 🔐 NEW: Extension API - 認可ポリシーを実行 */ async executeAuthorizationPolicy(userId, clientId, connectionName, groups) { return this.extensionApiRequest(`/users/${userId}/policy/${clientId}`, 'POST', { connectionName, groups, }); } /** * 🔐 NEW: Extension API - ユーザーのグループを取得 */ async getUserGroups(userId) { return this.extensionApiRequest(`/users/${userId}/groups`); } /** * 🔐 NEW: Extension API - ユーザーをグループに追加 */ async addUserToGroups(userId, groupIds) { await this.extensionApiRequest(`/users/${userId}/groups`, 'PATCH', groupIds); } /** * 🔐 NEW: Extension API - ユーザーをグループから削除 */ async removeUserFromGroups(userId, groupIds) { await this.extensionApiRequest(`/users/${userId}/groups`, 'DELETE', groupIds); } // 🔐 NEW: Standard Auth0 Authentication Flow Methods /** * Auth0 Universal Login URLを生成 */ buildLoginUrl(options) { const { redirectUri, responseType = 'code', scope = 'openid profile email', state, nonce, connection, prompt, } = options; const params = new URLSearchParams({ client_id: this.config.clientId, redirect_uri: redirectUri, response_type: responseType, scope, }); if (state) params.append('state', state); if (nonce) params.append('nonce', nonce); if (connection) params.append('connection', connection); if (prompt) params.append('prompt', prompt); return `https://${this.config.domain}/authorize?${params.toString()}`; } /** * Auth0 Universal Signup URLを生成 */ buildSignupUrl(options) { const loginUrl = this.buildLoginUrl(options); return loginUrl + '&screen_hint=signup'; } /** * Auth0 Logout URLを生成 */ buildLogoutUrl(options) { const { returnTo, clientId } = options; const params = new URLSearchParams({ returnTo, client_id: clientId || this.config.clientId, }); return `https://${this.config.domain}/v2/logout?${params.toString()}`; } /** * Auth0 Password Reset URLを生成 */ buildPasswordResetUrl(options) { const { email, connection = 'Username-Password-Authentication' } = options; const params = new URLSearchParams({ email, connection, }); return `https://${this.config.domain}/dbconnections/change_password?${params.toString()}`; } /** * Authorization Codeを使ってTokenを取得 */ async exchangeCodeForToken(options) { const { code, redirectUri, codeVerifier, clientSecret } = options; const body = { grant_type: 'authorization_code', client_id: this.config.clientId, code, redirect_uri: redirectUri, }; if (codeVerifier) { body.code_verifier = codeVerifier; } else if (clientSecret) { body.client_secret = clientSecret; } const response = await fetch(`https://${this.config.domain}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams(body).toString(), }); if (!response.ok) { const error = await response.json(); throw new Error(`Token exchange failed: ${error.error_description || error.error}`); } return await response.json(); } /** * Refresh Tokenを使って新しいAccess Tokenを取得 */ async refreshAccessToken(refreshToken) { const response = await fetch(`https://${this.config.domain}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'refresh_token', client_id: this.config.clientId, refresh_token: refreshToken, }).toString(), }); if (!response.ok) { const error = await response.json(); throw new Error(`Token refresh failed: ${error.error_description || error.error}`); } return response.json(); } /** * Management APIアクセストークンを取得 */ async getManagementApiToken() { const response = await fetch(`https://${this.config.domain}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ client_id: this.config.clientId, client_secret: process.env.GFTD_AUTH0_CLIENT_SECRET || '', audience: `https://${this.config.domain}/api/v2/`, grant_type: 'client_credentials', }), }); if (!response.ok) { const error = await response.json(); throw new Error(`Management API token failed: ${error.error_description || error.error}`); } const data = await response.json(); return data.access_token; } /** * Management APIを使ってユーザーを作成 */ async createUser(options) { const { email, password, name, connection = 'Username-Password-Authentication', email_verified = false, user_metadata = {}, app_metadata = {}, } = options; const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/users`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password, name, connection, email_verified, user_metadata, app_metadata, }), }); if (!response.ok) { const error = await response.json(); throw new Error(`User creation failed: ${error.message || error.error}`); } const user = await response.json(); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_CREATE, userId: user.user_id, tenantId: 'default', result: 'SUCCESS', message: `User created successfully: ${email}`, details: { email, name, connection }, }); return user; } /** * Management APIを使ってユーザーを更新 */ async updateUser(userId, updates) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/users/${userId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify(updates), }); if (!response.ok) { const error = await response.json(); throw new Error(`User update failed: ${error.message || error.error}`); } const user = await response.json(); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_CREATE, userId: user.user_id, tenantId: 'default', result: 'SUCCESS', message: `User updated successfully: ${userId}`, details: { updates }, }); return user; } /** * Management APIを使ってユーザーを削除 */ async deleteUser(userId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/users/${userId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { const error = await response.json(); throw new Error(`User deletion failed: ${error.message || error.error}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_DELETE, userId: userId, tenantId: 'default', result: 'SUCCESS', message: `User deleted successfully: ${userId}`, }); } /** * パスワードリセットメールを送信 */ async sendPasswordResetEmail(email, connection) { const response = await fetch(`https://${this.config.domain}/dbconnections/change_password`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ client_id: this.config.clientId, email, connection: connection || 'Username-Password-Authentication', }), }); if (!response.ok) { const error = await response.json(); throw new Error(`Password reset failed: ${error.error_description || error.error}`); } log.info(`Password reset email sent to: ${email}`); } /** * Email verification を送信 */ async sendEmailVerification(userId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/jobs/verification-email`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ user_id: userId, client_id: this.config.clientId, }), }); if (!response.ok) { const error = await response.json(); throw new Error(`Email verification failed: ${error.message || error.error}`); } log.info(`Email verification sent to user: ${userId}`); } /** * ユーザーのプロファイルを取得 */ async getUserProfile(userId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/users/${userId}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { const error = await response.json(); throw new Error(`Get user profile failed: ${error.message || error.error}`); } return response.json(); } /** * ユーザーリストを取得 */ async getUsers(options = {}) { const managementToken = await this.getManagementApiToken(); const params = new URLSearchParams(); if (options.page) params.append('page', options.page.toString()); if (options.per_page) params.append('per_page', options.per_page.toString()); if (options.search) params.append('search', options.search); if (options.sort) params.append('sort', options.sort); if (options.connection) params.append('connection', options.connection); const response = await fetch(`https://${this.config.domain}/api/v2/users?${params.toString()}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { const error = await response.json(); throw new Error(`Get users failed: ${error.message || error.error}`); } return response.json(); } /** * PKCEチャレンジを生成 */ generatePKCEChallenge() { // 簡単な実装(実際のプロダクションではcrypto.randomBytesなどを使用) const codeVerifier = this.generateRandomString(128); const codeChallenge = this.base64URLEncode(Buffer.from(this.sha256(codeVerifier), 'hex')); return { codeVerifier, codeChallenge, }; } /** * ランダム文字列を生成 */ generateRandomString(length) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } /** * SHA256ハッシュを計算 */ sha256(plain) { // 簡単な実装(実際のプロダクションではcrypto.createHashを使用) const crypto = require('crypto'); return crypto.createHash('sha256').update(plain).digest(); } /** * Base64URLエンコード */ base64URLEncode(buffer) { return buffer.toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } // 🏢 NEW: Auth0 Organizations Support /** * 🏢 Organizations一覧を取得 */ async getOrganizations(options = {}) { const managementToken = await this.getManagementApiToken(); const params = new URLSearchParams(); if (options.page) params.append('page', options.page.toString()); if (options.per_page) params.append('per_page', options.per_page.toString()); if (options.include_totals) params.append('include_totals', 'true'); if (options.from) params.append('from', options.from); if (options.take) params.append('take', options.take.toString()); const response = await fetch(`https://${this.config.domain}/api/v2/organizations?${params.toString()}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organizations failed: ${response.status}`); } return response.json(); } /** * 🏢 特定のOrganizationを取得 */ async getOrganization(organizationId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization failed: ${response.status}`); } return response.json(); } /** * 🏢 Organizationを作成 */ async createOrganization(organization) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify(organization), }); if (!response.ok) { throw new Error(`Create organization failed: ${response.status}`); } const createdOrg = await response.json(); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_CREATE, // Organization作成用のイベントタイプを追加すべき tenantId: 'default', result: 'SUCCESS', message: `Organization created: ${organization.name}`, details: { organizationId: createdOrg.id, name: organization.name }, }); return createdOrg; } /** * 🏢 Organizationを更新 */ async updateOrganization(organizationId, updates) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify(updates), }); if (!response.ok) { throw new Error(`Update organization failed: ${response.status}`); } const updatedOrg = await response.json(); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_POLICY_CHANGE, tenantId: 'default', result: 'SUCCESS', message: `Organization updated: ${organizationId}`, details: { organizationId, updates }, }); return updatedOrg; } /** * 🏢 Organizationを削除 */ async deleteOrganization(organizationId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Delete organization failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_DELETE, tenantId: 'default', result: 'SUCCESS', message: `Organization deleted: ${organizationId}`, details: { organizationId }, }); } /** * 🏢 Organization Membersを取得 */ async getOrganizationMembers(organizationId, options = {}) { const managementToken = await this.getManagementApiToken(); const params = new URLSearchParams(); if (options.page) params.append('page', options.page.toString()); if (options.per_page) params.append('per_page', options.per_page.toString()); if (options.include_totals) params.append('include_totals', 'true'); if (options.from) params.append('from', options.from); if (options.take) params.append('take', options.take.toString()); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members?${params.toString()}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization members failed: ${response.status}`); } return response.json(); } /** * 🏢 OrganizationにMemberを追加 */ async addOrganizationMembers(organizationId, userIds) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ users: userIds }), }); if (!response.ok) { throw new Error(`Add organization members failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_CREATE, tenantId: 'default', result: 'SUCCESS', message: `Members added to organization: ${organizationId}`, details: { organizationId, userIds }, }); } /** * 🏢 OrganizationからMemberを削除 */ async removeOrganizationMembers(organizationId, userIds) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ users: userIds }), }); if (!response.ok) { throw new Error(`Remove organization members failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_DELETE, tenantId: 'default', result: 'SUCCESS', message: `Members removed from organization: ${organizationId}`, details: { organizationId, userIds }, }); } /** * 🏢 Organization Invitationを作成(メンバー招待) */ async createOrganizationInvitation(organizationId, invitation) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/invitations`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify(invitation), }); if (!response.ok) { throw new Error(`Create organization invitation failed: ${response.status}`); } const createdInvitation = await response.json(); // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_CREATE, tenantId: 'default', result: 'SUCCESS', message: `Organization invitation created for: ${invitation.invitee.email}`, details: { organizationId, inviteeEmail: invitation.invitee.email, invitationId: createdInvitation.id }, }); return createdInvitation; } /** * 🏢 Organization Invitationsを取得 */ async getOrganizationInvitations(organizationId, options = {}) { const managementToken = await this.getManagementApiToken(); const params = new URLSearchParams(); if (options.page) params.append('page', options.page.toString()); if (options.per_page) params.append('per_page', options.per_page.toString()); if (options.include_totals) params.append('include_totals', 'true'); if (options.sort) params.append('sort', options.sort); if (options.from) params.append('from', options.from); if (options.take) params.append('take', options.take.toString()); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/invitations?${params.toString()}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization invitations failed: ${response.status}`); } return response.json(); } /** * 🏢 Organization Invitationを取得 */ async getOrganizationInvitation(organizationId, invitationId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/invitations/${invitationId}`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization invitation failed: ${response.status}`); } return response.json(); } /** * 🏢 Organization Invitationを削除 */ async deleteOrganizationInvitation(organizationId, invitationId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/invitations/${invitationId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Delete organization invitation failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_USER_DELETE, tenantId: 'default', result: 'SUCCESS', message: `Organization invitation deleted: ${invitationId}`, details: { organizationId, invitationId }, }); } /** * 🏢 Organization-scoped Access Tokenを取得 */ async getOrganizationAccessToken(options) { const response = await fetch(`https://${this.config.domain}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: options.grant_type || 'client_credentials', client_id: this.config.clientId, client_secret: process.env.GFTD_AUTH0_CLIENT_SECRET || '', audience: options.audience || this.config.audience || '', scope: options.scope || 'openid profile email', organization: options.organizationId, }).toString(), }); if (!response.ok) { throw new Error(`Get organization access token failed: ${response.status}`); } return response.json(); } /** * 🏢 Organization Rolesを取得 */ async getOrganizationRoles(organizationId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/organization_roles`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization roles failed: ${response.status}`); } return response.json(); } /** * 🏢 Organization Member Rolesを取得 */ async getOrganizationMemberRoles(organizationId, userId) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members/${userId}/roles`, { headers: { 'Authorization': `Bearer ${managementToken}`, }, }); if (!response.ok) { throw new Error(`Get organization member roles failed: ${response.status}`); } return response.json(); } /** * 🏢 Organization Memberにロールを追加 */ async addOrganizationMemberRoles(organizationId, userId, roleIds) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members/${userId}/roles`, { method: 'POST', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ roles: roleIds }), }); if (!response.ok) { throw new Error(`Add organization member roles failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_POLICY_CHANGE, userId: userId, tenantId: 'default', result: 'SUCCESS', message: `Organization roles added to member: ${userId}`, details: { organizationId, userId, roleIds }, }); } /** * 🏢 Organization Memberからロールを削除 */ async removeOrganizationMemberRoles(organizationId, userId, roleIds) { const managementToken = await this.getManagementApiToken(); const response = await fetch(`https://${this.config.domain}/api/v2/organizations/${organizationId}/members/${userId}/roles`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${managementToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ roles: roleIds }), }); if (!response.ok) { throw new Error(`Remove organization member roles failed: ${response.status}`); } // 監査ログ記録 AuditLogManager.log({ level: AuditLogLevel.INFO, eventType: AuditEventType.ADMIN_POLICY_CHANGE, userId: userId, tenantId: 'default', result: 'SUCCESS', message: `Organization roles removed from member: ${userId}`, details: { organizationId, userId, roleIds }, }); } } /** * Express.js ミドルウェア: Auth0認証 */ export function auth0AuthMiddleware(options = {}) { const auth0Integration = Auth0Integration.getInstance(); const { requireAuth = true, requiredPermissions = [], requiredRoles = [] } = options; return async (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { if (!requireAuth) { return next(); } return res.status(401).json({ error: 'Unauthorized', message: 'Auth0 Bearer token required', }); } const token = authHeader.substring(7); // "Bearer " を除去 // Auth0トークンで認証 const authResult = await auth0Integration.authenticateWithAuth0(token); if (!authResult.success || !authResult.user) { return res.status(401).json({ error: 'Unauthorized', message: authResult.error || 'Auth0 authentication failed', }); } // 権限チェック for (const permission of requiredPermissions) { if (!auth0Integration.checkAuth0Permission(authResult.user, permission)) { return res.status(403).json({ error: 'Forbidden', message: `Missing required permission: ${permission}`, }); } } // ロールチェック for (const role of requiredRoles) { if (!auth0Integration.checkAuth0Role(authResult.user, role)) { return res.status(403).json({ error: 'Forbidden', message: `Missing required role: ${role}`, }); } } // ユーザー情報をリクエストに追加 req.user = authResult.user; req.auth0Token = token; next(); }; } /** * Auth0統合のヘルパー関数 */ export const auth0 = { /** * 統合マネージャーのインスタンスを取得 */ manager: () => Auth0Integration.getInstance(), /** * Auth0トークンで認証 */ authenticate: async (token, customConfig) => { const manager = Auth0Integration.getInstance(customConfig); return manager.authenticateWithAuth0(token); }, /** * 権限チェック */ checkPermission: (user, permission) => { const manager = Auth0Integration.getInstance(); return manager.checkAuth0Permission(user, permission); }, /** * ロールチェック */ checkRole: (user, role) => { const manager = Auth0Integration.getInstance(); return manager.checkAuth0Role(user, role); }, /** * トークン検証 */ verifyToken: async (token) => { const manager = Auth0Integration.getInstance(); return manager.verifyAuth0Token(token); }, /** * ユーザー情報取得 */ getUserInfo: async (managementToken, userId) => { const manager = Auth0Integration.getInstance(); return manager.getAuth0UserInfo(managementToken, userId); }, }; //# sourceMappingURL=auth0-integration.js.map