diff --git a/backend/src/federation/authenticateCommunities.ts b/backend/src/federation/authenticateCommunities.ts index 0a94b24c8..4b2e5b77b 100644 --- a/backend/src/federation/authenticateCommunities.ts +++ b/backend/src/federation/authenticateCommunities.ts @@ -63,29 +63,28 @@ export async function startCommunityAuthentication( // check if a authentication is already in progress const existingState = await findPendingCommunityHandshake(fedComB, false) if (existingState) { - const logic = new CommunityHandshakeStateLogic(existingState) - if (!await logic.isTimeoutUpdate()) { + const stateLogic = new CommunityHandshakeStateLogic(existingState) + // retry on timeout or failure + if (!await stateLogic.isTimeoutUpdate()) { // authentication with community and api version is still in progress and it is not timeout yet methodLogger.debug('existingState', new CommunityHandshakeStateLoggingView(existingState)) return } } - - const state = new DbCommunityHandshakeState() - state.publicKey = fedComB.publicKey - state.apiVersion = fedComB.apiVersion - state.status = CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION - state.handshakeId = parseInt(handshakeID) - + const client = AuthenticationClientFactory.getInstance(fedComB) if (client instanceof V1_0_AuthenticationClient) { if (!comB.publicJwtKey) { - state.lastError = 'Public JWT key still not exist for comB ' + comB.name - await state.save() - throw new Error(state.lastError) + throw new Error(`Public JWT key still not exist for comB ${comB.name}`) } + const state = new DbCommunityHandshakeState() + state.publicKey = fedComB.publicKey + state.apiVersion = fedComB.apiVersion + state.status = CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION + state.handshakeId = parseInt(handshakeID) const stateSaveResolver = state.save() + //create JWT with url in payload encrypted by foreignCom.publicJwtKey and signed with homeCom.privateJwtKey const payload = new OpenConnectionJwtPayloadType(handshakeID, ensureUrlEndsWithSlash(homeFedComA.endPoint).concat(homeFedComA.apiVersion), @@ -104,7 +103,11 @@ export async function startCommunityAuthentication( if (result) { methodLogger.info(`successful initiated at community:`, fedComB.endPoint) } else { - methodLogger.error(`can't initiate at community:`, fedComB.endPoint) + const errorMsg = `can't initiate at community: ${fedComB.endPoint}` + methodLogger.error(errorMsg) + state.status = CommunityHandshakeStateType.FAILED + state.lastError = errorMsg + await state.save() } } } diff --git a/core/src/logic/CommunityHandshakeState.logic.ts b/core/src/logic/CommunityHandshakeState.logic.ts index a3e8631eb..03bf9a762 100644 --- a/core/src/logic/CommunityHandshakeState.logic.ts +++ b/core/src/logic/CommunityHandshakeState.logic.ts @@ -9,14 +9,25 @@ export class CommunityHandshakeStateLogic { * @returns true if the community handshake state is expired */ public async isTimeoutUpdate(): Promise { + const timeout = this.isTimeout() + if (timeout && this.communityHandshakeStateEntity.status !== CommunityHandshakeStateType.EXPIRED) { + this.communityHandshakeStateEntity.status = CommunityHandshakeStateType.EXPIRED + await this.communityHandshakeStateEntity.save() + } + return timeout + } + + public isTimeout(): boolean { if (this.communityHandshakeStateEntity.status === CommunityHandshakeStateType.EXPIRED) { return true } if (Date.now() - this.communityHandshakeStateEntity.updatedAt.getTime() > FEDERATION_AUTHENTICATION_TIMEOUT_MS) { - this.communityHandshakeStateEntity.status = CommunityHandshakeStateType.EXPIRED - await this.communityHandshakeStateEntity.save() return true } return false } + + public isFailed(): boolean { + return this.communityHandshakeStateEntity.status === CommunityHandshakeStateType.FAILED + } } diff --git a/database/src/queries/communityHandshakes.ts b/database/src/queries/communityHandshakes.ts index 991bd9c47..3248b311e 100644 --- a/database/src/queries/communityHandshakes.ts +++ b/database/src/queries/communityHandshakes.ts @@ -1,5 +1,5 @@ -import { CommunityHandshakeState } from '../entity' -import { FederatedCommunity } from '../entity/FederatedCommunity' +import { Not, In } from 'typeorm' +import { CommunityHandshakeState, CommunityHandshakeStateType, FederatedCommunity} from '..' /** * Find a pending community handshake by public key. @@ -9,7 +9,15 @@ import { FederatedCommunity } from '../entity/FederatedCommunity' */ export function findPendingCommunityHandshake(federatedCommunity: FederatedCommunity, withRelations = true): Promise { return CommunityHandshakeState.findOne({ - where: { publicKey: federatedCommunity.publicKey, apiVersion: federatedCommunity.apiVersion }, + where: { + publicKey: federatedCommunity.publicKey, + apiVersion: federatedCommunity.apiVersion, + status: Not(In([ + CommunityHandshakeStateType.EXPIRED, + CommunityHandshakeStateType.FAILED, + CommunityHandshakeStateType.SUCCESS + ])) + }, relations: withRelations ? { federatedCommunity: { community: true } } : undefined, }) } \ No newline at end of file diff --git a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts index fd948300e..dc6e1347b 100644 --- a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts @@ -6,9 +6,11 @@ import { Community as DbCommunity, FederatedCommunity as DbFedCommunity, FederatedCommunityLoggingView, + CommunityHandshakeStateType, + CommunityHandshakeState as DbCommunityHandshakeState, getHomeCommunity, } from 'database' -import { getLogger } from 'log4js' +import { getLogger, Logger } from 'log4js' import { AuthenticationJwtPayloadType, AuthenticationResponseJwtPayloadType, @@ -25,6 +27,13 @@ const createLogger = (method: string ) => getLogger(`${LOG4JS_BASE_CATEGORY_NAM @Resolver() export class AuthenticationResolver { + + private async errorState(errmsg: string, methodLogger: Logger, state: DbCommunityHandshakeState) { + methodLogger.error(errmsg) + state.lastError = errmsg + await state.save() + } + @Mutation(() => Boolean) async openConnection( @Arg('data')