@gftdcojp/gftd-orm
Version:
Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture
1,331 lines • 50.6 kB
JavaScript
/**
* 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