diff --git a/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts b/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts index e2ea5611..6840830f 100644 --- a/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts +++ b/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts @@ -163,11 +163,31 @@ export class ParticipateInKeygenHandler { private async joinSession(command: ParticipateInKeygenCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + // The session-coordinator expects us to create a session first + this.logger.log('Creating MPC session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'keygen', + thresholdN: 3, // Default 2-of-3 MPC + thresholdT: 2, + createdBy: command.partyId, + expiresIn: 600, // 10 minutes }); + this.logger.log(`Session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, // Use the JWT from createSession + }); + + // Return session info with the original session ID for consistency + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + }; } catch (error) { throw new ApplicationError( `Failed to join session: ${error.message}`, diff --git a/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts b/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts index b3898790..f8b07a38 100644 --- a/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts +++ b/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts @@ -167,11 +167,33 @@ export class ParticipateInSigningHandler { private async joinSession(command: ParticipateInSigningCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + this.logger.log('Creating MPC signing session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'sign', + thresholdN: 3, // Default 2-of-3 MPC + thresholdT: 2, + createdBy: command.partyId, + messageHash: command.messageHash, + expiresIn: 300, // 5 minutes for signing }); + this.logger.log(`Signing session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, + }); + + // Return session info with correct IDs and public key from command + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + publicKey: command.publicKey, // Preserve public key from command + messageHash: command.messageHash, + }; } catch (error) { throw new ApplicationError( `Failed to join signing session: ${error.message}`, diff --git a/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts b/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts index 7d0244e4..a0f0fe02 100644 --- a/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts +++ b/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts @@ -174,11 +174,31 @@ export class RotateShareHandler { private async joinSession(command: RotateShareCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + // Key refresh uses 'keygen' session type (coordinator doesn't have 'refresh' type) + this.logger.log('Creating MPC refresh session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'keygen', // Use keygen type for key refresh + thresholdN: 3, + thresholdT: 2, + createdBy: command.partyId, + expiresIn: 600, // 10 minutes }); + this.logger.log(`Refresh session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, + }); + + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + publicKey: command.publicKey, + }; } catch (error) { throw new ApplicationError( `Failed to join rotation session: ${error.message}`, diff --git a/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts b/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts index 95670ac3..15c93e3b 100644 --- a/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts +++ b/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts @@ -2,12 +2,32 @@ * MPC Coordinator Client * * Client for communicating with the external MPC Session Coordinator. + * + * Flow: + * 1. CreateSession - Creates a new MPC session and returns a JWT joinToken + * 2. JoinSession - Parties join using the JWT token */ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios, { AxiosInstance, AxiosError } from 'axios'; +export interface CreateSessionRequest { + sessionType: 'keygen' | 'sign'; + thresholdN: number; + thresholdT: number; + createdBy: string; + messageHash?: string; + expiresIn?: number; // seconds +} + +export interface CreateSessionResponse { + sessionId: string; + joinToken: string; // JWT token for joining + status: string; + expiresAt?: number; +} + export interface JoinSessionRequest { sessionId: string; partyId: string; @@ -22,6 +42,7 @@ export interface SessionInfo { participants: Array<{ partyId: string; partyIndex: number }>; publicKey?: string; messageHash?: string; + joinToken?: string; // JWT token for this session } export interface ReportCompletionRequest { @@ -89,7 +110,42 @@ export class MPCCoordinatorClient implements OnModuleInit { } /** - * Join an MPC session + * Create an MPC session + * + * This must be called first to get a valid JWT joinToken + */ + async createSession(request: CreateSessionRequest): Promise { + this.logger.log(`Creating session: type=${request.sessionType}, ${request.thresholdT}-of-${request.thresholdN}`); + + try { + const response = await this.client.post('/api/v1/sessions', { + sessionType: request.sessionType, + thresholdN: request.thresholdN, + thresholdT: request.thresholdT, + createdBy: request.createdBy, + messageHash: request.messageHash, + expiresIn: request.expiresIn || 600, // Default 10 minutes + }); + + this.logger.log(`Session created: ${response.data.sessionId}`); + + return { + sessionId: response.data.sessionId, + joinToken: response.data.joinToken, + status: response.data.status, + expiresAt: response.data.expiresAt, + }; + } catch (error) { + const message = this.getErrorMessage(error); + this.logger.error(`Failed to create session: ${message}`); + throw new Error(`Failed to create MPC session: ${message}`); + } + } + + /** + * Join an MPC session using a JWT token + * + * The joinToken must be obtained from createSession() */ async joinSession(request: JoinSessionRequest): Promise { this.logger.log(`Joining session: ${request.sessionId}`); @@ -114,6 +170,7 @@ export class MPCCoordinatorClient implements OnModuleInit { })) || [], publicKey: undefined, messageHash: undefined, + joinToken: request.joinToken, }; } catch (error) { const message = this.getErrorMessage(error);