UNPKG

@gftdcojp/auth

Version:

✅ Enterprise-grade Auth0 integration for GFTD platform - 90% Complete, High Quality Implementation

303 lines 11 kB
"use strict"; /** * Next.js 15 Server Actions実装 * * 機能: * - 認証が必要なServer Actionsの保護 * - 組織コンテキスト対応 * - セッション管理統合 * - エラーハンドリング * - 型安全性確保 * * ✅ Next.js 15完全対応 - App Router Server Actions */ Object.defineProperty(exports, "__esModule", { value: true }); exports.updateUserProfile = exports.getOrganizationMembers = void 0; exports.withServerAuth = withServerAuth; exports.withOrganizationServerAuth = withOrganizationServerAuth; exports.withAdminServerAuth = withAdminServerAuth; exports.getCurrentUser = getCurrentUser; const headers_1 = require("next/headers"); const navigation_1 = require("next/navigation"); const nextjs_auth0_server_1 = require("./nextjs-auth0-server"); const logger_1 = require("./utils/logger"); /** * 認証が必要なServer Actionラッパー */ function withServerAuth(action, config = {}) { return async (...args) => { try { const { requireAuth = true, requireOrganization = false, allowedRoles = [], allowedPermissions = [], redirectOnError, } = config; // Cookieからセッション情報を取得 const cookieStore = await (0, headers_1.cookies)(); const sessionCookie = cookieStore.get('auth0.session'); if (requireAuth && !sessionCookie) { logger_1.log.warn('Server Action: Authentication required but no session found'); if (redirectOnError) { (0, navigation_1.redirect)(redirectOnError); } return { success: false, error: { message: 'Authentication required', code: 'UNAUTHORIZED', }, redirect: '/auth/login', }; } let authContext = null; if (sessionCookie) { try { // セッションの復号化と検証 const client = new nextjs_auth0_server_1.NextJsAuth0Client(); const session = await client.decryptSession(sessionCookie.value); if (!session || !session.user) { throw new Error('Invalid session'); } authContext = { user: session.user, organizationId: session.user.organization_id, session, }; // 組織コンテキストチェック if (requireOrganization && !authContext.organizationId) { logger_1.log.warn('Server Action: Organization context required but not found'); return { success: false, error: { message: 'Organization context required', code: 'ORG_REQUIRED', }, redirect: '/auth/select-organization', }; } // ロール・権限チェック if (allowedRoles.length > 0) { const userRoles = authContext.user.metadata?.roles || []; const hasRole = allowedRoles.some(role => userRoles.includes(role)); if (!hasRole) { logger_1.log.warn(`Server Action: Insufficient roles. Required: ${allowedRoles}, User: ${userRoles}`); return { success: false, error: { message: 'Insufficient permissions', code: 'FORBIDDEN', }, }; } } if (allowedPermissions.length > 0) { const userPermissions = authContext.user.metadata?.permissions || []; const hasPermission = allowedPermissions.some(permission => userPermissions.includes(permission)); if (!hasPermission) { logger_1.log.warn(`Server Action: Insufficient permissions. Required: ${allowedPermissions}, User: ${userPermissions}`); return { success: false, error: { message: 'Insufficient permissions', code: 'FORBIDDEN', }, }; } } } catch (error) { logger_1.log.error(`Server Action: Session validation failed: ${error}`); if (requireAuth) { return { success: false, error: { message: 'Invalid session', code: 'INVALID_SESSION', }, redirect: '/auth/login', }; } } } // 認証が不要な場合は空のコンテキストを作成 if (!authContext && !requireAuth) { authContext = { user: { sub: 'anonymous', role: 'anon', tenant_id: 'anonymous', }, session: null, }; } if (!authContext) { return { success: false, error: { message: 'Authentication failed', code: 'AUTH_FAILED', }, }; } // Server Actionを実行 return await action(authContext, ...args); } catch (error) { logger_1.log.error(`Server Action error: ${error}`); return { success: false, error: { message: error instanceof Error ? error.message : 'Unknown error', code: 'SERVER_ERROR', details: error, }, }; } }; } /** * 組織が必要なServer Actionラッパー */ function withOrganizationServerAuth(organizationId, action, config = {}) { return withServerAuth(async (authContext, ...args) => { // 組織IDチェック if (authContext.organizationId !== organizationId) { logger_1.log.warn(`Server Action: Organization mismatch. Expected: ${organizationId}, Got: ${authContext.organizationId}`); return { success: false, error: { message: 'Organization access denied', code: 'ORG_ACCESS_DENIED', }, }; } return action(authContext, ...args); }, { ...config, requireAuth: true, requireOrganization: true, }); } /** * 管理者専用Server Actionラッパー */ function withAdminServerAuth(action, config = {}) { return withServerAuth(action, { ...config, requireAuth: true, allowedRoles: ['admin', 'super_admin'], }); } /** * 認証済みユーザー情報を取得するServer Action */ async function getCurrentUser() { 'use server'; try { const cookieStore = await (0, headers_1.cookies)(); const sessionCookie = cookieStore.get('auth0.session'); if (!sessionCookie) { return { success: true, data: null, }; } const client = new nextjs_auth0_server_1.NextJsAuth0Client(); const session = await client.decryptSession(sessionCookie.value); return { success: true, data: session?.user || null, }; } catch (error) { logger_1.log.error(`getCurrentUser error: ${error}`); return { success: false, error: { message: 'Failed to get current user', code: 'SESSION_ERROR', }, }; } } /** * 組織メンバー一覧取得のServer Action例 */ exports.getOrganizationMembers = withOrganizationServerAuth('org_123', // 組織ID(実際の実装では動的に指定) async (authContext, page = 1, limit = 10) => { 'use server'; try { // 組織メンバー取得ロジック logger_1.log.info(`Getting organization members for ${authContext.organizationId} (page: ${page})`); // TODO: 実際のデータ取得実装 const members = [ { id: '1', email: 'user1@example.com', name: 'User 1', role: 'member', }, { id: '2', email: 'user2@example.com', name: 'User 2', role: 'admin', }, ]; return { success: true, data: { members, total: members.length, page, limit, }, }; } catch (error) { logger_1.log.error(`getOrganizationMembers error: ${error}`); return { success: false, error: { message: 'Failed to get organization members', code: 'DATA_FETCH_ERROR', }, }; } }); /** * ユーザープロフィール更新のServer Action例 */ exports.updateUserProfile = withServerAuth(async (authContext, formData) => { 'use server'; try { const name = formData.get('name'); const email = formData.get('email'); if (!name || !email) { return { success: false, error: { message: 'Name and email are required', code: 'VALIDATION_ERROR', }, }; } logger_1.log.info(`Updating profile for user ${authContext.user.sub}`); // TODO: 実際のプロフィール更新実装 return { success: true, data: { message: 'Profile updated successfully', }, }; } catch (error) { logger_1.log.error(`updateUserProfile error: ${error}`); return { success: false, error: { message: 'Failed to update profile', code: 'UPDATE_ERROR', }, }; } }, { requireAuth: true, }); //# sourceMappingURL=server-actions.js.map