diff --git a/apps/armory/src/__test__/fixture/authorization-request.fixture.ts b/apps/armory/src/__test__/fixture/authorization-request.fixture.ts index 65de89efb..330d7ff9c 100644 --- a/apps/armory/src/__test__/fixture/authorization-request.fixture.ts +++ b/apps/armory/src/__test__/fixture/authorization-request.fixture.ts @@ -1,19 +1,13 @@ -import { Decision, Signature, TransactionRequest } from '@narval/policy-engine-shared' +import { Decision, TransactionRequest } from '@narval/policy-engine-shared' import { AuthorizationRequestStatus } from '@prisma/client/armory' import { z } from 'zod' import { Fixture } from 'zod-fixture' -import { Approval, AuthorizationRequest, SignTransaction } from '../../orchestration/core/type/domain.type' +import { AuthorizationRequest, SignTransaction } from '../../orchestration/core/type/domain.type' import { readRequestSchema } from '../../orchestration/persistence/schema/request.schema' import { readSignTransactionSchema } from '../../orchestration/persistence/schema/sign-transaction.schema' -import { signatureSchema } from '../../orchestration/persistence/schema/signature.schema' import { readTransactionRequestSchema } from '../../orchestration/persistence/schema/transaction-request.schema' import { addressGenerator, chainIdGenerator, hexGenerator } from './shared.fixture' -const approvalSchema = signatureSchema.extend({ - id: z.string().uuid(), - createdAt: z.date() -}) - const evaluationSchema = z.object({ id: z.string().uuid(), decision: z.nativeEnum(Decision), @@ -26,8 +20,8 @@ const authorizationRequestSchema = z.object({ orgId: z.string().uuid(), status: z.nativeEnum(AuthorizationRequestStatus), request: readRequestSchema, - authentication: signatureSchema, - approvals: z.array(approvalSchema), + authentication: z.string(), + approvals: z.array(z.string()), evaluations: z.array(evaluationSchema), idempotencyKey: z.string().nullish(), createdAt: z.date(), @@ -66,13 +60,3 @@ export const generateAuthorizationRequest = (partial?: Partial): Approval => ({ - ...new Fixture().fromSchema(approvalSchema), - ...partial -}) - -export const generateSignature = (partial?: Partial): Signature => ({ - ...new Fixture().fromSchema(approvalSchema), - ...partial -}) diff --git a/apps/armory/src/__test__/fixture/feed.fixture.ts b/apps/armory/src/__test__/fixture/feed.fixture.ts index f6f52c2a1..6f0b14d74 100644 --- a/apps/armory/src/__test__/fixture/feed.fixture.ts +++ b/apps/armory/src/__test__/fixture/feed.fixture.ts @@ -3,13 +3,12 @@ import { times } from 'lodash/fp' import { z } from 'zod' import { Fixture } from 'zod-fixture' import { HistoricalTransferFeedService } from '../../data-feed/core/service/historical-transfer-feed.service' -import { signatureSchema } from '../../orchestration/persistence/schema/signature.schema' import { hexGenerator } from './shared.fixture' import { generateTransfer } from './transfer-tracking.fixture' const feedSchema = z.object({ source: z.string().min(1).max(42), - sig: signatureSchema.nullable() + sig: z.string().nullable() }) export const generateHistoricalTransfers = (): HistoricalTransfer[] => diff --git a/apps/armory/src/data-feed/core/service/__test__/unit/feed.service.spec.ts b/apps/armory/src/data-feed/core/service/__test__/unit/feed.service.spec.ts index cbf1095c7..ec5813398 100644 --- a/apps/armory/src/data-feed/core/service/__test__/unit/feed.service.spec.ts +++ b/apps/armory/src/data-feed/core/service/__test__/unit/feed.service.spec.ts @@ -4,7 +4,6 @@ import { MockProxy, mock, mockDeep } from 'jest-mock-extended' import { generateAuthorizationRequest, generateSignTransactionRequest, - generateSignature, generateTransactionRequest } from '../../../../../__test__/fixture/authorization-request.fixture' import { generateHistoricalTransfers } from '../../../../../__test__/fixture/feed.fixture' @@ -17,6 +16,9 @@ import { HistoricalTransferFeedService } from '../../historical-transfer-feed.se import { PriceFeedService } from '../../price-feed.service' describe(FeedService.name, () => { + const jwt = + 'eyJraWQiOiIweDJjNDg5NTIxNTk3M0NiQmQ3NzhDMzJjNDU2QzA3NGI5OWRhRjhCZjEiLCJhbGciOiJFSVAxOTEiLCJ0eXAiOiJKV1QifQ.eyJyZXF1ZXN0SGFzaCI6IjYwOGFiZTkwOGNmZmVhYjFmYzMzZWRkZTZiNDQ1ODZmOWRhY2JjOWM2ZmU2ZjBhMTNmYTMwNzIzNzI5MGNlNWEiLCJzdWIiOiJ0ZXN0LXJvb3QtdXNlci11aWQiLCJpc3MiOiJodHRwczovL2FybW9yeS5uYXJ2YWwueHl6IiwiY25mIjp7Imt0eSI6IkVDIiwiY3J2Ijoic2VjcDI1NmsxIiwiYWxnIjoiRVMyNTZLIiwidXNlIjoic2lnIiwia2lkIjoiMHgwMDBjMGQxOTEzMDhBMzM2MzU2QkVlMzgxM0NDMTdGNjg2ODk3MkM0IiwieCI6IjA0YTlmM2JjZjY1MDUwNTk1OTdmNmYyN2FkOGMwZjAzYTNiZDdhMTc2MzUyMGIwYmZlYzIwNDQ4OGI4ZTU4NDAiLCJ5IjoiN2VlOTI4NDVhYjFjMzVhNzg0YjA1ZmRmYTU2NzcxNWM1M2JiMmYyOTk0OWIyNzcxNGUzYzE3NjBlMzcwOTAwOWE2In19.gFDywYsxY2-uT6H6hyxk51CtJhAZpI8WtcvoXHltiWsoBVOot1zMo3nHAhkWlYRmD3RuLtmOYzi6TwTUM8mFyBs' + let module: TestingModule let service: FeedService let prismaServiceMock: MockProxy @@ -33,13 +35,13 @@ describe(FeedService.name, () => { const historicalTransferFeed: Feed = { source: HistoricalTransferFeedService.SOURCE_ID, - sig: generateSignature(), + sig: jwt, data: generateHistoricalTransfers() } const priceFeed: Feed = { source: PriceFeedService.SOURCE_ID, - sig: generateSignature(), + sig: jwt, data: generatePrices() } diff --git a/apps/armory/src/data-feed/core/service/__test__/unit/historical-transfer-feed.service.spec.ts b/apps/armory/src/data-feed/core/service/__test__/unit/historical-transfer-feed.service.spec.ts index a54b3f0c9..a3ea7cf2b 100644 --- a/apps/armory/src/data-feed/core/service/__test__/unit/historical-transfer-feed.service.spec.ts +++ b/apps/armory/src/data-feed/core/service/__test__/unit/historical-transfer-feed.service.spec.ts @@ -1,4 +1,3 @@ -import { Alg } from '@narval/signature' import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' import { MockProxy, mock } from 'jest-mock-extended' @@ -70,12 +69,7 @@ describe(HistoricalTransferFeedService.name, () => { expect(feed).toMatchObject({ data: HistoricalTransferFeedService.build(transfers), source: HistoricalTransferFeedService.SOURCE_ID, - sig: { - alg: Alg.ES256K, - pubKey: - '0x041a2a9746efacc23443530a75092ad75c6cd5dd10d2ccc1d9c866acf9545974bcacc6b755c3c241d6a35b9b27b00cd2df8f46525a751d6872360c3be3015bb563', - sig: expect.any(String) - } + sig: expect.any(String) }) }) diff --git a/apps/armory/src/data-feed/core/service/__test__/unit/price-feed.service.spec.ts b/apps/armory/src/data-feed/core/service/__test__/unit/price-feed.service.spec.ts index e78494cfe..fd11d26f8 100644 --- a/apps/armory/src/data-feed/core/service/__test__/unit/price-feed.service.spec.ts +++ b/apps/armory/src/data-feed/core/service/__test__/unit/price-feed.service.spec.ts @@ -1,5 +1,4 @@ import { Prices } from '@narval/policy-engine-shared' -import { Alg } from '@narval/signature' import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' import { MockProxy, mock } from 'jest-mock-extended' @@ -73,12 +72,7 @@ describe(PriceFeedService.name, () => { expect(feed).toMatchObject({ data: prices, source: PriceFeedService.SOURCE_ID, - sig: { - alg: Alg.ES256K, - pubKey: - '0x04583c9cf37f209ca3afbdb43f83dfaed0e758b89545bfa8e297d3d727fc3ed9c6f16607bc859f2304704329bf19c843acce511d0639bbd8abb1fe70e7dd05f8f5', - sig: expect.any(String) - } + sig: expect.any(String) }) }) diff --git a/apps/armory/src/data-feed/core/service/feed.service.ts b/apps/armory/src/data-feed/core/service/feed.service.ts index dbddc807b..9aba20833 100644 --- a/apps/armory/src/data-feed/core/service/feed.service.ts +++ b/apps/armory/src/data-feed/core/service/feed.service.ts @@ -46,9 +46,7 @@ export class FeedService { orgId, requestId, source: feed.source, - sig: feed.sig?.sig, - alg: feed.sig?.alg, - pubKey: feed.sig?.pubKey, + sig: feed.sig, data: this.getPersistableJson(feed.data), createdAt: new Date() })) diff --git a/apps/armory/src/data-feed/core/service/historical-transfer-feed.service.ts b/apps/armory/src/data-feed/core/service/historical-transfer-feed.service.ts index ecb5ba125..3d3813e1b 100644 --- a/apps/armory/src/data-feed/core/service/historical-transfer-feed.service.ts +++ b/apps/armory/src/data-feed/core/service/historical-transfer-feed.service.ts @@ -1,5 +1,5 @@ -import { Feed, HistoricalTransfer, Signature } from '@narval/policy-engine-shared' -import { Alg, hash } from '@narval/signature' +import { Feed, HistoricalTransfer, JwtString } from '@narval/policy-engine-shared' +import { Payload, SigningAlg, hash, hexToBase64Url, privateKeyToJwk, signJwt } from '@narval/signature' import { Injectable } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { mapValues, omit } from 'lodash/fp' @@ -27,17 +27,26 @@ export class HistoricalTransferFeedService implements DataFeed { + async sign(data: HistoricalTransfer[]): Promise { const account = privateKeyToAccount(this.getPrivateKey()) - const sig = await account.signMessage({ - message: hash(data) - }) - return { - alg: Alg.ES256K, - pubKey: account.publicKey, - sig + const jwtSigner = async (msg: string) => { + const jwtSig = await account.signMessage({ message: msg }) + + return hexToBase64Url(jwtSig) } + + const now = Math.floor(Date.now() / 1000) + const jwk = privateKeyToJwk(this.getPrivateKey()) + const payload: Payload = { + data: hash(data), + sub: account.address, + iss: 'https://armory.narval.xyz', + iat: now + } + const jwt = await signJwt(payload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) + + return jwt } private getPrivateKey(): `0x${string}` { diff --git a/apps/armory/src/data-feed/core/service/price-feed.service.ts b/apps/armory/src/data-feed/core/service/price-feed.service.ts index 71a79bd5f..305549540 100644 --- a/apps/armory/src/data-feed/core/service/price-feed.service.ts +++ b/apps/armory/src/data-feed/core/service/price-feed.service.ts @@ -1,5 +1,5 @@ -import { Action, AssetId, Feed, Signature } from '@narval/policy-engine-shared' -import { Alg, hash } from '@narval/signature' +import { Action, AssetId, Feed, JwtString } from '@narval/policy-engine-shared' +import { Payload, SigningAlg, hash, hexToBase64Url, privateKeyToJwk, signJwt } from '@narval/signature' import { InputType, Intents, safeDecode } from '@narval/transaction-request-intent' import { Injectable } from '@nestjs/common' import { ConfigService } from '@nestjs/config' @@ -26,17 +26,26 @@ export class PriceFeedService implements DataFeed { return PriceFeedService.SOURCE_ID } - async sign(data: Prices): Promise { + async sign(data: Prices): Promise { const account = privateKeyToAccount(this.getPrivateKey()) - const sig = await account.signMessage({ - message: hash(data) - }) - return { - alg: Alg.ES256K, - pubKey: account.publicKey, - sig + const jwtSigner = async (msg: string) => { + const jwtSig = await account.signMessage({ message: msg }) + + return hexToBase64Url(jwtSig) } + + const now = Math.floor(Date.now() / 1000) + const jwk = privateKeyToJwk(this.getPrivateKey()) + const payload: Payload = { + data: hash(data), + sub: account.address, + iss: 'https://armory.narval.xyz', + iat: now + } + const jwt = await signJwt(payload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) + + return jwt } getPubKey(): string { diff --git a/apps/armory/src/data-feed/core/type/data-feed.type.ts b/apps/armory/src/data-feed/core/type/data-feed.type.ts index a5564bb8f..f44d4993a 100644 --- a/apps/armory/src/data-feed/core/type/data-feed.type.ts +++ b/apps/armory/src/data-feed/core/type/data-feed.type.ts @@ -1,4 +1,4 @@ -import { Feed, Signature } from '@narval/policy-engine-shared' +import { Feed } from '@narval/policy-engine-shared' // TODO (@wcalderipe, 06/02/24): Move the AuthorizationRequest type to shared import { AuthorizationRequest } from '../../../orchestration/core/type/domain.type' @@ -6,5 +6,5 @@ export interface DataFeed { getId(): string getPubKey(): string getFeed(input: AuthorizationRequest): Promise> - sign(data: Data): Promise + sign(data: Data): Promise } diff --git a/apps/armory/src/orchestration/core/service/__test__/unit/authorization-request.service.spec.ts b/apps/armory/src/orchestration/core/service/__test__/unit/authorization-request.service.spec.ts index 6306b3a2d..bba39b5f6 100644 --- a/apps/armory/src/orchestration/core/service/__test__/unit/authorization-request.service.spec.ts +++ b/apps/armory/src/orchestration/core/service/__test__/unit/authorization-request.service.spec.ts @@ -4,10 +4,8 @@ import { Test, TestingModule } from '@nestjs/testing' import { MockProxy, mock } from 'jest-mock-extended' import { times } from 'lodash/fp' import { - generateApproval, generateAuthorizationRequest, generateSignTransactionRequest, - generateSignature, generateTransactionRequest } from '../../../../../__test__/fixture/authorization-request.fixture' import { generateTransfer } from '../../../../../__test__/fixture/transfer-tracking.fixture' @@ -20,16 +18,14 @@ import { TransferTrackingService } from '../../../../../transfer-tracking/core/s import { AuthorizationRequestAlreadyProcessingException } from '../../../../core/exception/authorization-request-already-processing.exception' import { AuthorizationRequestService } from '../../../../core/service/authorization-request.service' import { ClusterService } from '../../../../core/service/cluster.service' -import { - Approval, - AuthorizationRequest, - AuthorizationRequestStatus, - SignTransaction -} from '../../../../core/type/domain.type' +import { AuthorizationRequest, AuthorizationRequestStatus, SignTransaction } from '../../../../core/type/domain.type' import { AuthorizationRequestRepository } from '../../../../persistence/repository/authorization-request.repository' import { AuthorizationRequestProcessingProducer } from '../../../../queue/producer/authorization-request-processing.producer' describe(AuthorizationRequestService.name, () => { + const jwt = + 'eyJraWQiOiIweDJjNDg5NTIxNTk3M0NiQmQ3NzhDMzJjNDU2QzA3NGI5OWRhRjhCZjEiLCJhbGciOiJFSVAxOTEiLCJ0eXAiOiJKV1QifQ.eyJyZXF1ZXN0SGFzaCI6IjYwOGFiZTkwOGNmZmVhYjFmYzMzZWRkZTZiNDQ1ODZmOWRhY2JjOWM2ZmU2ZjBhMTNmYTMwNzIzNzI5MGNlNWEiLCJzdWIiOiJ0ZXN0LXJvb3QtdXNlci11aWQiLCJpc3MiOiJodHRwczovL2FybW9yeS5uYXJ2YWwueHl6IiwiY25mIjp7Imt0eSI6IkVDIiwiY3J2Ijoic2VjcDI1NmsxIiwiYWxnIjoiRVMyNTZLIiwidXNlIjoic2lnIiwia2lkIjoiMHgwMDBjMGQxOTEzMDhBMzM2MzU2QkVlMzgxM0NDMTdGNjg2ODk3MkM0IiwieCI6IjA0YTlmM2JjZjY1MDUwNTk1OTdmNmYyN2FkOGMwZjAzYTNiZDdhMTc2MzUyMGIwYmZlYzIwNDQ4OGI4ZTU4NDAiLCJ5IjoiN2VlOTI4NDVhYjFjMzVhNzg0YjA1ZmRmYTU2NzcxNWM1M2JiMmYyOTk0OWIyNzcxNGUzYzE3NjBlMzcwOTAwOWE2In19.gFDywYsxY2-uT6H6hyxk51CtJhAZpI8WtcvoXHltiWsoBVOot1zMo3nHAhkWlYRmD3RuLtmOYzi6TwTUM8mFyBs' + let module: TestingModule let authzRequestRepositoryMock: MockProxy let authzRequestProcessingProducerMock: MockProxy @@ -90,11 +86,9 @@ describe(AuthorizationRequestService.name, () => { }) describe('approve', () => { - const approval: Approval = generateApproval() - const updatedAuthzRequest: AuthorizationRequest = { ...authzRequest, - approvals: [approval] + approvals: [jwt] } beforeEach(() => { @@ -106,11 +100,11 @@ describe(AuthorizationRequestService.name, () => { it('creates a new approval and evaluates the authorization request', async () => { authzRequestRepositoryMock.update.mockResolvedValue(updatedAuthzRequest) - await service.approve(authzRequest.id, approval) + await service.approve(authzRequest.id, jwt) expect(authzRequestRepositoryMock.update).toHaveBeenCalledWith({ id: authzRequest.id, - approvals: [approval] + approvals: [jwt] }) expect(service.evaluate).toHaveBeenCalledWith(updatedAuthzRequest) }) @@ -120,7 +114,9 @@ describe(AuthorizationRequestService.name, () => { const evaluationResponse: EvaluationResponse = { decision: Decision.PERMIT, request: authzRequest.request, - attestation: generateSignature(), + accessToken: { + value: jwt + }, transactionRequestIntent: { type: Intents.TRANSFER_NATIVE, amount: '1000000000000000000', @@ -176,7 +172,7 @@ describe(AuthorizationRequestService.name, () => { expect.objectContaining({ id: expect.any(String), decision: evaluationResponse.decision, - signature: evaluationResponse.attestation?.sig, + signature: evaluationResponse.accessToken?.value, createdAt: expect.any(Date) }) ] @@ -207,7 +203,7 @@ describe(AuthorizationRequestService.name, () => { chainId: request.transactionRequest.chainId, orgId: authzRequest.orgId, requestId: authzRequest.id, - initiatedBy: authzRequest.authentication.pubKey, + initiatedBy: authzRequest.authentication, // TODO: this will change when the underlying data is corrected. rates: { 'fiat:usd': 0.99 }, diff --git a/apps/armory/src/orchestration/core/service/__test__/unit/cluster.service.spec.ts b/apps/armory/src/orchestration/core/service/__test__/unit/cluster.service.spec.ts index 24cb5290b..817e0b301 100644 --- a/apps/armory/src/orchestration/core/service/__test__/unit/cluster.service.spec.ts +++ b/apps/armory/src/orchestration/core/service/__test__/unit/cluster.service.spec.ts @@ -1,12 +1,11 @@ import { Decision, EvaluationResponse, Feed, Prices } from '@narval/policy-engine-shared' -import { Alg, hash } from '@narval/signature' +import { hash } from '@narval/signature' import { Test } from '@nestjs/testing' import { MockProxy, mock } from 'jest-mock-extended' import { PrivateKeyAccount, generatePrivateKey, privateKeyToAccount } from 'viem/accounts' import { generateAuthorizationRequest, generateSignTransactionRequest, - generateSignature, generateTransactionRequest } from '../../../../../__test__/fixture/authorization-request.fixture' import { generatePrices } from '../../../../../__test__/fixture/price.fixture' @@ -21,6 +20,9 @@ import { AuthorizationRequest } from '../../../../core/type/domain.type' import { AuthzApplicationClient } from '../../../../http/client/authz-application.client' describe(ClusterService.name, () => { + const jwt = + 'eyJraWQiOiIweDJjNDg5NTIxNTk3M0NiQmQ3NzhDMzJjNDU2QzA3NGI5OWRhRjhCZjEiLCJhbGciOiJFSVAxOTEiLCJ0eXAiOiJKV1QifQ.eyJyZXF1ZXN0SGFzaCI6IjYwOGFiZTkwOGNmZmVhYjFmYzMzZWRkZTZiNDQ1ODZmOWRhY2JjOWM2ZmU2ZjBhMTNmYTMwNzIzNzI5MGNlNWEiLCJzdWIiOiJ0ZXN0LXJvb3QtdXNlci11aWQiLCJpc3MiOiJodHRwczovL2FybW9yeS5uYXJ2YWwueHl6IiwiY25mIjp7Imt0eSI6IkVDIiwiY3J2Ijoic2VjcDI1NmsxIiwiYWxnIjoiRVMyNTZLIiwidXNlIjoic2lnIiwia2lkIjoiMHgwMDBjMGQxOTEzMDhBMzM2MzU2QkVlMzgxM0NDMTdGNjg2ODk3MkM0IiwieCI6IjA0YTlmM2JjZjY1MDUwNTk1OTdmNmYyN2FkOGMwZjAzYTNiZDdhMTc2MzUyMGIwYmZlYzIwNDQ4OGI4ZTU4NDAiLCJ5IjoiN2VlOTI4NDVhYjFjMzVhNzg0YjA1ZmRmYTU2NzcxNWM1M2JiMmYyOTk0OWIyNzcxNGUzYzE3NjBlMzcwOTAwOWE2In19.gFDywYsxY2-uT6H6hyxk51CtJhAZpI8WtcvoXHltiWsoBVOot1zMo3nHAhkWlYRmD3RuLtmOYzi6TwTUM8mFyBs' + let service: ClusterService let authzApplicationClientMock: MockProxy @@ -51,7 +53,7 @@ describe(ClusterService.name, () => { const priceFeed: Feed = { source: PriceFeedService.SOURCE_ID, - sig: generateSignature(), + sig: jwt, data: generatePrices() } @@ -110,10 +112,8 @@ describe(ClusterService.name, () => { satisfied: [], missing: [] }, - attestation: { - sig: signature, - alg: Alg.ES256K, - pubKey: account.address + accessToken: { + value: signature }, ...partial } @@ -173,11 +173,7 @@ describe(ClusterService.name, () => { authzApplicationClientMock.evaluation.mockResolvedValue({ ...permit, - attestation: { - alg: Alg.ES256K, - sig: signature, - pubKey: nodeAccount.address - } + accessToken: { value: signature } }) await expect(service.evaluation(input)).rejects.toThrow(InvalidAttestationSignatureException) diff --git a/apps/armory/src/orchestration/core/service/authorization-request.service.ts b/apps/armory/src/orchestration/core/service/authorization-request.service.ts index e8d416f4f..15c33c8fd 100644 --- a/apps/armory/src/orchestration/core/service/authorization-request.service.ts +++ b/apps/armory/src/orchestration/core/service/authorization-request.service.ts @@ -1,14 +1,12 @@ -import { Action, Decision } from '@narval/policy-engine-shared' +import { Action, Decision, JwtString } from '@narval/policy-engine-shared' import { Intent, Intents } from '@narval/transaction-request-intent' import { Injectable, Logger } from '@nestjs/common' -import { SetOptional } from 'type-fest' import { v4 as uuid } from 'uuid' import { FIAT_ID_USD } from '../../../armory.constant' import { FeedService } from '../../../data-feed/core/service/feed.service' import { PriceService } from '../../../price/core/service/price.service' import { TransferTrackingService } from '../../../transfer-tracking/core/service/transfer-tracking.service' import { - Approval, AuthorizationRequest, AuthorizationRequestStatus, CreateAuthorizationRequest @@ -87,16 +85,10 @@ export class AuthorizationRequestService { }) } - async approve(id: string, approval: SetOptional): Promise { + async approve(id: string, approval: JwtString): Promise { const authzRequest = await this.authzRequestRepository.update({ id: id, - approvals: [ - { - id: approval.id || uuid(), - createdAt: approval.createdAt || new Date(), - ...approval - } - ] + approvals: [approval] }) return this.evaluate(authzRequest) @@ -137,8 +129,7 @@ export class AuthorizationRequestService { { id: uuid(), decision: evaluation.decision, - // TODO (@mattschoch, 23/01/24): return the full attestation? - signature: evaluation?.attestation?.sig || null, + signature: evaluation.accessToken?.value || null, createdAt: new Date() } ] @@ -161,7 +152,7 @@ export class AuthorizationRequestService { to: intent.to, token: intent.token, chainId: authzRequest.request.transactionRequest.chainId, - initiatedBy: authzRequest.authentication.pubKey, + initiatedBy: authzRequest.authentication, // TODO: Get real initiator? -- this used to reference publicKey but should actually pull data out of a decoded JWT createdAt: new Date(), amount: BigInt(intent.amount), rates: transferPrices[intent.token] diff --git a/apps/armory/src/orchestration/core/service/cluster.service.ts b/apps/armory/src/orchestration/core/service/cluster.service.ts index acb64c1d3..3b964d45d 100644 --- a/apps/armory/src/orchestration/core/service/cluster.service.ts +++ b/apps/armory/src/orchestration/core/service/cluster.service.ts @@ -124,7 +124,7 @@ export class ClusterService { return recoverMessageAddress({ message: requestHash, - signature: response.attestation?.sig as `0x${string}` + signature: response.accessToken?.value as `0x${string}` // TODO: This will fail for real because this is NOT a EIP191 sig, it's a JWT }) } diff --git a/apps/armory/src/orchestration/core/type/domain.type.ts b/apps/armory/src/orchestration/core/type/domain.type.ts index 427d158dc..dc022928a 100644 --- a/apps/armory/src/orchestration/core/type/domain.type.ts +++ b/apps/armory/src/orchestration/core/type/domain.type.ts @@ -1,4 +1,4 @@ -import { Action, Signature, TransactionRequest } from '@narval/policy-engine-shared' +import { Action, JwtString, TransactionRequest } from '@narval/policy-engine-shared' import { OverrideProperties, SetOptional } from 'type-fest' export enum AuthorizationRequestStatus { @@ -11,11 +11,6 @@ export enum AuthorizationRequestStatus { FORBIDDEN = 'FORBIDDEN' } -export type Approval = Signature & { - id: string - createdAt: Date -} - export type Evaluation = { id: string decision: string @@ -51,21 +46,19 @@ export type AuthorizationRequest = { id: string orgId: string status: `${AuthorizationRequestStatus}` - authentication: Signature + authentication: JwtString request: Request - approvals: Approval[] + approvals: JwtString[] evaluations: Evaluation[] idempotencyKey?: string | null createdAt: Date updatedAt: Date } -export type CreateApproval = SetOptional - export type CreateAuthorizationRequest = OverrideProperties< SetOptional, { - approvals: CreateApproval[] + approvals: JwtString[] } > diff --git a/apps/armory/src/orchestration/gateway/authorization-request.gateway.ts b/apps/armory/src/orchestration/gateway/authorization-request.gateway.ts index fedb9c4f2..3e8df6556 100644 --- a/apps/armory/src/orchestration/gateway/authorization-request.gateway.ts +++ b/apps/armory/src/orchestration/gateway/authorization-request.gateway.ts @@ -1,7 +1,7 @@ +import { JwtString } from '@narval/policy-engine-shared' import { Injectable } from '@nestjs/common' -import { SetOptional } from 'type-fest' import { AuthorizationRequestService } from '../core/service/authorization-request.service' -import { Approval, AuthorizationRequest, CreateAuthorizationRequest } from '../core/type/domain.type' +import { AuthorizationRequest, CreateAuthorizationRequest } from '../core/type/domain.type' @Injectable() export class AuthorizationRequestGateway { @@ -15,7 +15,7 @@ export class AuthorizationRequestGateway { return this.authorizationRequestService.findById(id) } - async approve(id: string, approval: SetOptional): Promise { + async approve(id: string, approval: JwtString): Promise { return this.authorizationRequestService.approve(id, approval) } } diff --git a/apps/armory/src/orchestration/http/rest/controller/authorization-request.controller.ts b/apps/armory/src/orchestration/http/rest/controller/authorization-request.controller.ts index b713e1584..e1757bb0f 100644 --- a/apps/armory/src/orchestration/http/rest/controller/authorization-request.controller.ts +++ b/apps/armory/src/orchestration/http/rest/controller/authorization-request.controller.ts @@ -6,7 +6,6 @@ import { ErrorResponseDto } from '../../../../shared/dto/error-response.dto' import { AuthorizationRequestService } from '../../../core/service/authorization-request.service' import { AuthorizationRequestDto } from '../../../http/rest/dto/authorization-request.dto' import { AuthorizationResponseDto } from '../../../http/rest/dto/authorization-response.dto' -import { SignatureDto } from '../../../http/rest/dto/signature.dto' import { toCreateAuthorizationRequest } from '../../../http/rest/util' @Controller('/authorization-requests') @@ -68,7 +67,7 @@ export class AuthorizationRequestController { status: HttpStatus.CREATED, type: AuthorizationResponseDto }) - async approve(@Param('id') id: string, @Body() body: SignatureDto): Promise { + async approve(@Param('id') id: string, @Body() body: string): Promise { const authzRequest = await this.authorizationRequestService.approve(id, body) return new AuthorizationResponseDto(authzRequest) diff --git a/apps/armory/src/orchestration/http/rest/dto/authorization-request.dto.ts b/apps/armory/src/orchestration/http/rest/dto/authorization-request.dto.ts index 080b3e995..e240c35f9 100644 --- a/apps/armory/src/orchestration/http/rest/dto/authorization-request.dto.ts +++ b/apps/armory/src/orchestration/http/rest/dto/authorization-request.dto.ts @@ -1,27 +1,23 @@ import { Action } from '@narval/policy-engine-shared' import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger' import { Type } from 'class-transformer' -import { IsDefined, ValidateNested } from 'class-validator' +import { IsDefined, IsString, ValidateNested } from 'class-validator' import { SignMessageRequestDto } from '../../../http/rest/dto/sign-message-request.dto' import { SignTransactionRequestDto } from '../../../http/rest/dto/sign-transaction-request.dto' -import { SignatureDto } from '../../../http/rest/dto/signature.dto' @ApiExtraModels(SignTransactionRequestDto, SignMessageRequestDto) export class AuthorizationRequestDto { @IsDefined() - @ValidateNested() + @IsString() @ApiProperty() - @Type(() => SignatureDto) - authentication: SignatureDto + authentication: string @IsDefined() - @ValidateNested() - @Type(() => SignatureDto) + @IsString() @ApiProperty({ - type: () => SignatureDto, isArray: true }) - approvals: SignatureDto[] + approvals: string[] // TODO (@wcalderipe, 22/01/24): Test the discrimination type option from // class-transformer instead of a custom function map. diff --git a/apps/armory/src/orchestration/http/rest/dto/authorization-response.dto.ts b/apps/armory/src/orchestration/http/rest/dto/authorization-response.dto.ts index 31925addd..a7a2435e2 100644 --- a/apps/armory/src/orchestration/http/rest/dto/authorization-response.dto.ts +++ b/apps/armory/src/orchestration/http/rest/dto/authorization-response.dto.ts @@ -6,7 +6,6 @@ import { AuthorizationRequestStatus } from '../../../core/type/domain.type' import { EvaluationDto } from '../../../http/rest/dto/evaluation.dto' import { SignMessageRequestDto } from '../../../http/rest/dto/sign-message-request.dto' import { SignTransactionRequestDto } from '../../../http/rest/dto/sign-transaction-request.dto' -import { SignatureDto } from '../../../http/rest/dto/signature.dto' import { TransactionResponseDto } from '../../../http/rest/dto/transaction-request.dto' class SignTransactionResponseDto extends SignTransactionRequestDto { @@ -44,10 +43,9 @@ export class AuthorizationResponseDto { idempotencyKey?: string | null @IsDefined() - @ValidateNested() + @IsString() @ApiProperty() - @Type(() => SignatureDto) - authentication: SignatureDto + authentication: string @ApiProperty({ enum: AuthorizationRequestStatus diff --git a/apps/armory/src/orchestration/http/rest/util.ts b/apps/armory/src/orchestration/http/rest/util.ts index 35e923c25..22fbd54f4 100644 --- a/apps/armory/src/orchestration/http/rest/util.ts +++ b/apps/armory/src/orchestration/http/rest/util.ts @@ -1,6 +1,5 @@ -import { Signature } from '@narval/policy-engine-shared' import { plainToInstance } from 'class-transformer' -import { CreateApproval, CreateAuthorizationRequest } from '../../core/type/domain.type' +import { CreateAuthorizationRequest } from '../../core/type/domain.type' import { AuthorizationRequestDto } from '../../http/rest/dto/authorization-request.dto' // Not in love with the gymnastics required to bend a DTO to a domain object. @@ -11,8 +10,8 @@ export const toCreateAuthorizationRequest = ( body: AuthorizationRequestDto ): CreateAuthorizationRequest => { const dto = plainToInstance(AuthorizationRequestDto, body) - const approvals: CreateApproval[] = dto.approvals - const authentication: Signature = dto.authentication + const approvals: string[] = dto.approvals + const authentication: string = dto.authentication return { orgId, diff --git a/apps/armory/src/orchestration/persistence/decode/__test__/unit/authorization-request.decode.spec.ts b/apps/armory/src/orchestration/persistence/decode/__test__/unit/authorization-request.decode.spec.ts index aadec158f..ce5fa1671 100644 --- a/apps/armory/src/orchestration/persistence/decode/__test__/unit/authorization-request.decode.spec.ts +++ b/apps/armory/src/orchestration/persistence/decode/__test__/unit/authorization-request.decode.spec.ts @@ -12,8 +12,6 @@ describe('decodeAuthorizationRequest', () => { idempotencyKey: null, authnSig: '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c', - authnAlg: 'ES256K', - authnPubKey: '0xd75D626a116D4a1959fE3bB938B2e7c116A05890', evaluationLog: [], approvals: [], createdAt: new Date(), diff --git a/apps/armory/src/orchestration/persistence/decode/authorization-request.decode.ts b/apps/armory/src/orchestration/persistence/decode/authorization-request.decode.ts index 273511008..2db5c6160 100644 --- a/apps/armory/src/orchestration/persistence/decode/authorization-request.decode.ts +++ b/apps/armory/src/orchestration/persistence/decode/authorization-request.decode.ts @@ -5,18 +5,12 @@ import { ZodIssueCode, ZodSchema, z } from 'zod' import { AuthorizationRequest, Evaluation } from '../../core/type/domain.type' import { ACTION_REQUEST } from '../../orchestration.constant' import { DecodeAuthorizationRequestException } from '../../persistence/exception/decode-authorization-request.exception' -import { signatureSchema } from '../../persistence/schema/signature.schema' import { AuthorizationRequestModel } from '../../persistence/type/model.type' type Model = SetOptional const actionSchema = z.nativeEnum(Action) -const approvalSchema = signatureSchema.extend({ - id: z.string().uuid(), - createdAt: z.date() -}) - const buildEvaluation = ({ id, decision, signature, createdAt }: EvaluationLog): Evaluation => ({ id, decision, @@ -29,12 +23,8 @@ const buildSharedAttributes = (model: Model): Omit ({ + sig: approval + })) + const model = await this.prismaService.authorizationRequest.create({ data: { id, @@ -31,12 +35,10 @@ export class AuthorizationRequestRepository { createdAt, updatedAt, action: request.action, - authnAlg: authentication.alg, - authnSig: authentication.sig, - authnPubKey: authentication.pubKey, + authnSig: authentication, approvals: { createMany: { - data: approvals + data: approvalsData } }, evaluationLog: { @@ -77,7 +79,7 @@ export class AuthorizationRequestRepository { status, approvals: { createMany: { - data: approvals?.length ? approvals : [], + data: approvals?.length ? approvals.map((sig) => ({ sig })) : [], skipDuplicates: true } }, @@ -141,11 +143,7 @@ export class AuthorizationRequestRepository { status: input.status || AuthorizationRequestStatus.CREATED, createdAt: input.createdAt || now, updatedAt: input.updatedAt || now, - approvals: input.approvals.map((approval) => ({ - ...approval, - id: approval.id || uuid(), - createdAt: approval.createdAt || now - })) + approvals: input.approvals } } diff --git a/apps/armory/src/orchestration/persistence/schema/signature.schema.ts b/apps/armory/src/orchestration/persistence/schema/signature.schema.ts index cca447cb8..8d1928b34 100644 --- a/apps/armory/src/orchestration/persistence/schema/signature.schema.ts +++ b/apps/armory/src/orchestration/persistence/schema/signature.schema.ts @@ -2,9 +2,3 @@ import { Alg } from '@narval/signature' import { z } from 'zod' export const algSchema = z.nativeEnum(Alg) - -export const signatureSchema = z.object({ - sig: z.string(), - alg: algSchema, - pubKey: z.string() -}) diff --git a/apps/armory/src/shared/module/persistence/schema/schema.prisma b/apps/armory/src/shared/module/persistence/schema/schema.prisma index 1917a5a7a..e7e308cfa 100644 --- a/apps/armory/src/shared/module/persistence/schema/schema.prisma +++ b/apps/armory/src/shared/module/persistence/schema/schema.prisma @@ -44,8 +44,6 @@ model AuthorizationRequest { action String request Json idempotencyKey String? @unique @map("idempotency_key") - authnAlg String @map("authn_alg") - authnPubKey String @map("authn_pub_key") authnSig String @map("authn_sig") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @@ -59,8 +57,6 @@ model AuthorizationRequest { model AuthorizationRequestApproval { id String @id @default(uuid()) @db.VarChar(255) requestId String @map("request_id") - alg String - pubKey String @map("pub_key") sig String createdAt DateTime @default(now()) @map("created_at") diff --git a/packages/policy-engine-shared/src/lib/dev.fixture.ts b/packages/policy-engine-shared/src/lib/dev.fixture.ts index 420e217a9..327122656 100644 --- a/packages/policy-engine-shared/src/lib/dev.fixture.ts +++ b/packages/policy-engine-shared/src/lib/dev.fixture.ts @@ -116,7 +116,6 @@ export const CREDENTIAL: Record = { userId: USER.Dave.id } } -console.log(CREDENTIAL) export const USER_GROUP: Record = { Engineering: {