From 63258f38eb83ab7ab414a060bfceee146f468185 Mon Sep 17 00:00:00 2001 From: Mads Christiansen Date: Wed, 4 Sep 2024 09:39:31 +0200 Subject: [PATCH] Support identity token (#306) * Support identity token * linter * Update lib/av_client/types.ts Co-authored-by: Martin Laursen <100691050+av-martin@users.noreply.github.com> * . --------- Co-authored-by: Martin Laursen <100691050+av-martin@users.noreply.github.com> --- lib/av_client.ts | 14 +- .../voter_authorization_coordinator.ts | 120 +++++++++++------- lib/av_client/types.ts | 5 + package.json | 2 +- 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/lib/av_client.ts b/lib/av_client.ts index aa39c6d2..ea123433 100644 --- a/lib/av_client.ts +++ b/lib/av_client.ts @@ -209,8 +209,18 @@ export class AVClient implements IAVClient { const coordinatorURL = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.url; const voterAuthorizerContextUuid = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.contextUuid; const coordinator = new VoterAuthorizationCoordinator(coordinatorURL, voterAuthorizerContextUuid); - const identification = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.authorizationMode === 'proof-of-identity' ? this.identityConfirmationToken : this.proofOfElectionCodes.mainKeyPair.publicKey - return await coordinator.getVoterInfo(identification) + + let identity; + + if (this.proofOfElectionCodes) { + identity = { publicKey: this.proofOfElectionCodes.mainKeyPair.publicKey } + } else if (this.identityConfirmationToken) { + identity = { identitiyConfirmationToken: this.identityConfirmationToken }; + } else { + throw new InvalidStateError("No way of identifying voter. Please generate a public key or supply an identityToken") + } + + return await coordinator.getVoterInfo(identity) } /** diff --git a/lib/av_client/connectors/voter_authorization_coordinator.ts b/lib/av_client/connectors/voter_authorization_coordinator.ts index a17120b9..4dac345b 100644 --- a/lib/av_client/connectors/voter_authorization_coordinator.ts +++ b/lib/av_client/connectors/voter_authorization_coordinator.ts @@ -2,7 +2,7 @@ import axios, { AxiosInstance, AxiosResponse } from 'axios' import { IdentityConfirmationToken } from "./otp_provider"; import { EmailDoesNotMatchVoterRecordError, NetworkError, UnsupportedServerReplyError, VoterRecordNotFoundError, DBBError, BallotReferenceNotOnVoterRecord } from "../errors"; import { ProofOfElectionCodes } from "../crypto/proof_of_election_codes"; -import { BallotBoxReceipt } from '../types'; +import { BallotBoxReceipt, VoterAuthorizationIdentification } from '../types'; export default class VoterAuthorizationCoordinator { private backend: AxiosInstance; @@ -18,77 +18,101 @@ export default class VoterAuthorizationCoordinator { * @param opaqueVoterId Gets * @returns */ - createSession(opaqueVoterId: string, email: string, ballotReference?: string): Promise { - return this.backend.post('create_session', { - electionContextUuid: this.electionContextUuid, - opaqueVoterId: opaqueVoterId, - ballotReference: ballotReference, - email, - }).catch(error => { - const response = error.response; + createSession( + opaqueVoterId: string, + email: string, + ballotReference?: string + ): Promise { + return this.backend + .post("create_session", { + electionContextUuid: this.electionContextUuid, + opaqueVoterId: opaqueVoterId, + ballotReference: ballotReference, + email, + }) + .catch((error) => { + const response = error.response; - if (error.request && !response) { - throw new NetworkError('Network error. Could not connect to Voter Authorization Coordinator.'); - } + if (error.request && !response) { + throw new NetworkError( + "Network error. Could not connect to Voter Authorization Coordinator." + ); + } - if ([403, 500].includes(response.status) && response.data) { - const errorCode = response.data.error_code; - const errorMessage = response.data.error_message; - switch(errorCode) { - case 'BALLOT_REFERERENCE_NOT_ON_VOTER_RECORD': - throw new BallotReferenceNotOnVoterRecord(errorMessage); - case 'EMAIL_DOES_NOT_MATCH_VOTER_RECORD': - throw new EmailDoesNotMatchVoterRecordError(errorMessage); - case 'COULD_NOT_CONNECT_TO_OTP_PROVIDER': - throw new NetworkError(errorMessage); - case 'VOTER_RECORD_NOT_FOUND_ERROR': - throw new VoterRecordNotFoundError(errorMessage) - case 'DBB_ERROR': - throw new DBBError(errorMessage) - default: throw new UnsupportedServerReplyError(`Unsupported server error: ${errorMessage}`); + if ([403, 500].includes(response.status) && response.data) { + const errorCode = response.data.error_code; + const errorMessage = response.data.error_message; + switch (errorCode) { + case "BALLOT_REFERERENCE_NOT_ON_VOTER_RECORD": + throw new BallotReferenceNotOnVoterRecord(errorMessage); + case "EMAIL_DOES_NOT_MATCH_VOTER_RECORD": + throw new EmailDoesNotMatchVoterRecordError(errorMessage); + case "COULD_NOT_CONNECT_TO_OTP_PROVIDER": + throw new NetworkError(errorMessage); + case "VOTER_RECORD_NOT_FOUND_ERROR": + throw new VoterRecordNotFoundError(errorMessage); + case "DBB_ERROR": + throw new DBBError(errorMessage); + default: + throw new UnsupportedServerReplyError( + `Unsupported server error: ${errorMessage}` + ); + } } - } - throw error; - }); + throw error; + }); } - sendReceipt(receipt: BallotBoxReceipt, authorizationSessionId: string, dbasUrl?: string, locale = "en"): Promise { + sendReceipt( + receipt: BallotBoxReceipt, + authorizationSessionId: string, + dbasUrl?: string, + locale = "en" + ): Promise { return this.backend.post(`${locale}/send_receipt`, { trackingCode: receipt.trackingCode, electionContextUuid: this.electionContextUuid, authorizationSessionId: authorizationSessionId, receipt: receipt.receipt, - dbasUrl: dbasUrl - }) + dbasUrl: dbasUrl, + }); } - requestPublicKeyAuthorization(sessionId: string, identityConfirmationToken: IdentityConfirmationToken, publicKey: string, votingRoundReference: string): Promise { - return this.backend.post('request_authorization', { + requestPublicKeyAuthorization( + sessionId: string, + identityConfirmationToken: IdentityConfirmationToken, + publicKey: string, + votingRoundReference: string + ): Promise { + return this.backend.post("request_authorization", { electionContextUuid: this.electionContextUuid, sessionId: sessionId, emailConfirmationToken: identityConfirmationToken, publicKey: publicKey, - votingRoundReference: votingRoundReference - }) + votingRoundReference: votingRoundReference, + }); } - authorizeProofOfElectionCodes(publicKey: string, proof: ProofOfElectionCodes, votingRoundReference: string, scope = "register"): Promise { - return this.backend.post('authorize_proof', { + authorizeProofOfElectionCodes( + publicKey: string, + proof: ProofOfElectionCodes, + votingRoundReference: string, + scope = "register" + ): Promise { + return this.backend.post("authorize_proof", { scope: scope, electionContextUuid: this.electionContextUuid, voterPublicKey: proof.mainKeyPair.publicKey, // This public key is used by the VA to find the voter to authorize. sessionPublicKey: publicKey, // This public key is used for the auth token proof: proof.proof, - votingRoundReference: votingRoundReference - }) + votingRoundReference: votingRoundReference, + }); } - getVoterInfo(identification: string) { + getVoterInfo(identification: VoterAuthorizationIdentification) { return this.backend.get("voter_info", { - params: { - identification: identification, - }, + params: identification, }); } @@ -98,9 +122,9 @@ export default class VoterAuthorizationCoordinator { withCredentials: false, timeout: timeout, headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } + Accept: "application/json", + "Content-Type": "application/json", + }, }); } } diff --git a/lib/av_client/types.ts b/lib/av_client/types.ts index 3a4e7d93..67008384 100644 --- a/lib/av_client/types.ts +++ b/lib/av_client/types.ts @@ -89,6 +89,11 @@ export type BoardItem = BallotCryptogramItem | CastRequestItem +interface IdentityConfirmationTokenIdentification { identitiyConfirmationToken: string } +interface PublicKeyIdentification { publicKey: string } + +export type VoterAuthorizationIdentification = IdentityConfirmationTokenIdentification | PublicKeyIdentification; + export type BoardItemType = "BallotCryptogramsItem" | "BoardEncryptionCommitmentItem" | diff --git a/package.json b/package.json index a575bcaa..b914db29 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "4.3.2", + "version": "4.3.3", "name": "@aion-dk/js-client", "license": "MIT", "description": "Assembly Voting JS client",