diff --git a/apps/armory/.lintstagedrc.js b/apps/armory/.lintstagedrc.js new file mode 100644 index 000000000..3cc2540b7 --- /dev/null +++ b/apps/armory/.lintstagedrc.js @@ -0,0 +1,6 @@ +module.exports = { + '*.{ts,tsx}': (filenames) => [ + `eslint --no-error-on-unmatched-pattern ${filenames.join(' ')}; echo "ESLint completed with exit code $?"`, + `prettier --write ${filenames.join(' ')}` + ] +} 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/__test__/e2e/authorization-request.spec.ts b/apps/armory/src/orchestration/__test__/e2e/authorization-request.spec.ts index 530a680df..0982bbd07 100644 --- a/apps/armory/src/orchestration/__test__/e2e/authorization-request.spec.ts +++ b/apps/armory/src/orchestration/__test__/e2e/authorization-request.spec.ts @@ -1,5 +1,4 @@ import { Action } from '@narval/policy-engine-shared' -import { Alg } from '@narval/signature' import { getQueueToken } from '@nestjs/bull' import { HttpStatus, INestApplication } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' @@ -11,7 +10,6 @@ import { stringToHex } from 'viem' import { load } from '../../../armory.config' import { AUTHORIZATION_REQUEST_PROCESSING_QUEUE, REQUEST_HEADER_ORG_ID } from '../../../armory.constant' import { AuthorizationRequest } from '../../../orchestration/core/type/domain.type' -import { SignatureDto } from '../../../orchestration/http/rest/dto/signature.dto' import { AuthorizationRequestRepository } from '../../../orchestration/persistence/repository/authorization-request.repository' import { PersistenceModule } from '../../../shared/module/persistence/persistence.module' import { TestPrismaService } from '../../../shared/module/persistence/service/test-prisma.service' @@ -27,23 +25,12 @@ describe('Authorization Request', () => { let authzRequestRepository: AuthorizationRequestRepository let authzRequestProcessingQueue: Queue - const authentication: SignatureDto = { - alg: Alg.ES256K, - pubKey: '0xd75D626a116D4a1959fE3bB938B2e7c116A05890', - sig: '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' - } + const authentication = + '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' - const approvals: SignatureDto[] = [ - { - alg: Alg.ES256K, - pubKey: '0x501D5c2Ce1EF208aadf9131a98BAa593258CfA06', - sig: '0x48510e3b74799b8e8f4e01aba0d196e18f66d86a62ae91abf5b89be9391c15661c7d29ee4654a300ed6db977da512475ed5a39f70f677e23d1b2f53c1554d0dd1b' - }, - { - alg: Alg.ES256K, - pubKey: '0xab88c8785D0C00082dE75D801Fcb1d5066a6311e', - sig: '0xcc645f43d8df80c4deeb2e60a8c0c15d58586d2c29ea7c85208cea81d1c47cbd787b1c8473dde70c3a7d49f573e491223107933257b2b99ecc4806b7cc16848d1c' - } + const approvals: string[] = [ + '0x48510e3b74799b8e8f4e01aba0d196e18f66d86a62ae91abf5b89be9391c15661c7d29ee4654a300ed6db977da512475ed5a39f70f677e23d1b2f53c1554d0dd1b', + '0xcc645f43d8df80c4deeb2e60a8c0c15d58586d2c29ea7c85208cea81d1c47cbd787b1c8473dde70c3a7d49f573e491223107933257b2b99ecc4806b7cc16848d1c' ] // TODO: Create domain type 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..30d95a32c 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,22 @@ 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) @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..664a4b18c 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 approval.sig)), evaluations: (model.evaluationLog || []).map(buildEvaluation), createdAt: model.createdAt, updatedAt: model.updatedAt diff --git a/apps/armory/src/orchestration/persistence/repository/__test__/integration/authorization-request.repository.spec.ts b/apps/armory/src/orchestration/persistence/repository/__test__/integration/authorization-request.repository.spec.ts index fd1872544..179ae4d6b 100644 --- a/apps/armory/src/orchestration/persistence/repository/__test__/integration/authorization-request.repository.spec.ts +++ b/apps/armory/src/orchestration/persistence/repository/__test__/integration/authorization-request.repository.spec.ts @@ -1,5 +1,4 @@ -import { Action, Signature } from '@narval/policy-engine-shared' -import { Alg } from '@narval/signature' +import { Action } from '@narval/policy-engine-shared' import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' import { AuthorizationRequestStatus, Organization } from '@prisma/client/armory' @@ -7,7 +6,7 @@ import { omit } from 'lodash/fp' import { load } from '../../../../../armory.config' import { PersistenceModule } from '../../../../../shared/module/persistence/persistence.module' import { TestPrismaService } from '../../../../../shared/module/persistence/service/test-prisma.service' -import { Approval, AuthorizationRequest, Evaluation, SignTransaction } from '../../../../core/type/domain.type' +import { AuthorizationRequest, Evaluation, SignTransaction } from '../../../../core/type/domain.type' import { AuthorizationRequestRepository } from '../../../repository/authorization-request.repository' describe(AuthorizationRequestRepository.name, () => { @@ -22,11 +21,8 @@ describe(AuthorizationRequestRepository.name, () => { updatedAt: new Date() } - const authentication: Signature = { - alg: Alg.ES256K, - pubKey: '0xd75D626a116D4a1959fE3bB938B2e7c116A05890', - sig: '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' - } + const authentication = + '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' const signMessageRequest: AuthorizationRequest = { authentication, @@ -80,11 +76,7 @@ describe(AuthorizationRequestRepository.name, () => { }) expect(request).toMatchObject(omit(['evaluations', 'approvals', 'authentication'], signMessageRequest)) - expect({ - sig: request?.authnSig, - alg: request?.authnAlg, - pubKey: request?.authnPubKey - }).toEqual(authentication) + expect(request?.authnSig).toEqual(authentication) }) it('defaults status to CREATED', async () => { @@ -128,13 +120,7 @@ describe(AuthorizationRequestRepository.name, () => { }) it('creates approvals', async () => { - const approval: Approval = { - id: 'c534332f-6dd9-4cc8-b727-e1ad21176238', - alg: Alg.ES256K, - sig: 'test-signature', - pubKey: 'test-public-key', - createdAt: new Date() - } + const approval = 'test-signature' await repository.create({ ...signMessageRequest, @@ -147,9 +133,9 @@ describe(AuthorizationRequestRepository.name, () => { } }) - expect(approvals).toEqual([ + expect(approvals.map(omit(['id', 'createdAt']))).toEqual([ { - ...approval, + sig: approval, requestId: signMessageRequest.id } ]) @@ -239,28 +225,12 @@ describe(AuthorizationRequestRepository.name, () => { it('appends approvals', async () => { const authzRequestOne = await repository.update({ ...signMessageRequest, - approvals: [ - { - id: 'c534332f-6dd9-4cc8-b727-e1ad21176238', - alg: Alg.ES256K, - sig: 'test-signature', - pubKey: 'test-public-key', - createdAt: new Date() - } - ] + approvals: ['test-signature'] }) const authzRequestTwo = await repository.update({ ...signMessageRequest, - approvals: [ - { - id: '790e30b0-35d1-4e22-8be5-71e64afbee89', - alg: Alg.ES256K, - sig: 'test-signature', - pubKey: 'test-public-key', - createdAt: new Date() - } - ] + approvals: ['test-signature'] }) const actual = await repository.findById(signMessageRequest.id) diff --git a/apps/armory/src/orchestration/persistence/repository/authorization-request.repository.ts b/apps/armory/src/orchestration/persistence/repository/authorization-request.repository.ts index b2ff59893..45cca8be1 100644 --- a/apps/armory/src/orchestration/persistence/repository/authorization-request.repository.ts +++ b/apps/armory/src/orchestration/persistence/repository/authorization-request.repository.ts @@ -21,6 +21,10 @@ export class AuthorizationRequestRepository { const request = createRequestSchema.parse(input.request) const evaluationLogs = this.toEvaluationLogs(orgId, evaluations) + const approvalsData = approvals.map((approval) => ({ + sig: approval + })) + const model = await this.prismaService.authorizationRequest.create({ data: { id, @@ -31,14 +35,14 @@ export class AuthorizationRequestRepository { createdAt, updatedAt, action: request.action, - authnAlg: authentication.alg, - authnSig: authentication.sig, - authnPubKey: authentication.pubKey, - approvals: { - createMany: { - data: approvals - } - }, + authnSig: authentication, + approvals: approvals + ? { + createMany: { + data: approvalsData + } + } + : undefined, evaluationLog: { createMany: { data: evaluationLogs @@ -77,7 +81,7 @@ export class AuthorizationRequestRepository { status, approvals: { createMany: { - data: approvals?.length ? approvals : [], + data: approvals?.length ? approvals.map((sig) => ({ sig })) : [], skipDuplicates: true } }, @@ -141,11 +145,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/orchestration/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts b/apps/armory/src/orchestration/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts index 1a8c22e91..304838839 100644 --- a/apps/armory/src/orchestration/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts +++ b/apps/armory/src/orchestration/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts @@ -1,5 +1,4 @@ -import { Action, Signature } from '@narval/policy-engine-shared' -import { Alg } from '@narval/signature' +import { Action } from '@narval/policy-engine-shared' import { HttpModule } from '@nestjs/axios' import { BullModule, getQueueToken } from '@nestjs/bull' import { ConfigModule } from '@nestjs/config' @@ -50,11 +49,8 @@ describe(AuthorizationRequestProcessingConsumer.name, () => { updatedAt: new Date() } - const authentication: Signature = { - alg: Alg.ES256K, - pubKey: '0xd75D626a116D4a1959fE3bB938B2e7c116A05890', - sig: '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' - } + const authentication = + '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' const authzRequest: AuthorizationRequest = { authentication, diff --git a/apps/armory/src/orchestration/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts b/apps/armory/src/orchestration/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts index 8bb67c5fb..be1387d85 100644 --- a/apps/armory/src/orchestration/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts +++ b/apps/armory/src/orchestration/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts @@ -1,5 +1,4 @@ -import { Action, Signature } from '@narval/policy-engine-shared' -import { Alg } from '@narval/signature' +import { Action } from '@narval/policy-engine-shared' import { BullModule, getQueueToken } from '@nestjs/bull' import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' @@ -26,11 +25,8 @@ describe(AuthorizationRequestProcessingProducer.name, () => { let producer: AuthorizationRequestProcessingProducer let testPrismaService: TestPrismaService - const authentication: Signature = { - alg: Alg.ES256K, - pubKey: '0xd75D626a116D4a1959fE3bB938B2e7c116A05890', - sig: '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' - } + const authentication = + '0xe24d097cea880a40f8be2cf42f497b9fbda5f9e4a31b596827e051d78dce75c032fa7e5ee3046f7c6f116e5b98cb8d268fa9b9d222ff44719e2ec2a0d9159d0d1c' const authzRequest: AuthorizationRequest = { authentication, diff --git a/apps/armory/src/shared/module/persistence/schema/migrations/20240312145427_sig_to_jwt/migration.sql b/apps/armory/src/shared/module/persistence/schema/migrations/20240312145427_sig_to_jwt/migration.sql new file mode 100644 index 000000000..22881ae7b --- /dev/null +++ b/apps/armory/src/shared/module/persistence/schema/migrations/20240312145427_sig_to_jwt/migration.sql @@ -0,0 +1,78 @@ +/* + Warnings: + + - You are about to drop the column `authn_alg` on the `authorization_request` table. All the data in the column will be lost. + - You are about to drop the column `authn_pub_key` on the `authorization_request` table. All the data in the column will be lost. + - You are about to drop the column `alg` on the `authorization_request_approval` table. All the data in the column will be lost. + - You are about to drop the column `pub_key` on the `authorization_request_approval` table. All the data in the column will be lost. + - You are about to drop the `address_book_account_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `auth_credential_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `organization_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `token_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `user_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `user_group_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `user_group_member_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `user_wallet_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `wallet_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `wallet_group_entity` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `wallet_group_member_entity` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "user_group_member_entity" DROP CONSTRAINT "user_group_member_entity_user_group_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "user_group_member_entity" DROP CONSTRAINT "user_group_member_entity_user_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "user_wallet_entity" DROP CONSTRAINT "user_wallet_entity_user_id_fkey"; + +-- DropForeignKey +ALTER TABLE "user_wallet_entity" DROP CONSTRAINT "user_wallet_entity_wallet_id_fkey"; + +-- DropForeignKey +ALTER TABLE "wallet_group_member_entity" DROP CONSTRAINT "wallet_group_member_entity_wallet_group_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "wallet_group_member_entity" DROP CONSTRAINT "wallet_group_member_entity_wallet_uid_fkey"; + +-- AlterTable +ALTER TABLE "authorization_request" DROP COLUMN "authn_alg", +DROP COLUMN "authn_pub_key"; + +-- AlterTable +ALTER TABLE "authorization_request_approval" DROP COLUMN "alg", +DROP COLUMN "pub_key"; + +-- DropTable +DROP TABLE "address_book_account_entity"; + +-- DropTable +DROP TABLE "auth_credential_entity"; + +-- DropTable +DROP TABLE "organization_entity"; + +-- DropTable +DROP TABLE "token_entity"; + +-- DropTable +DROP TABLE "user_entity"; + +-- DropTable +DROP TABLE "user_group_entity"; + +-- DropTable +DROP TABLE "user_group_member_entity"; + +-- DropTable +DROP TABLE "user_wallet_entity"; + +-- DropTable +DROP TABLE "wallet_entity"; + +-- DropTable +DROP TABLE "wallet_group_entity"; + +-- DropTable +DROP TABLE "wallet_group_member_entity"; 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/apps/policy-engine/src/engine/app.service.ts b/apps/policy-engine/src/engine/app.service.ts index 163e3212f..575dbf98b 100644 --- a/apps/policy-engine/src/engine/app.service.ts +++ b/apps/policy-engine/src/engine/app.service.ts @@ -6,10 +6,10 @@ import { EvaluationResponse, HistoricalTransfer, JsonWebKey, - Request, - Signature + JwtString, + Request } from '@narval/policy-engine-shared' -import { Alg, Payload, SigningAlg, hash, privateKeyToJwk, publicKeyToJwk } from '@narval/signature' +import { Payload, SigningAlg, decode, hash, privateKeyToJwk, publicKeyToJwk, verifyJwt } from '@narval/signature' import { safeDecode } from '@narval/transaction-request-intent' import { BadRequestException, @@ -20,7 +20,7 @@ import { } from '@nestjs/common' import { InputType } from 'packages/transaction-request-intent/src/lib/domain' import { Intent } from 'packages/transaction-request-intent/src/lib/intent.types' -import { Hex, verifyMessage } from 'viem' +import { Hex } from 'viem' import { OpaResult, RegoInput } from '../shared/type/domain.type' import { SigningService } from './core/service/signing.service' import { OpaService } from './opa/opa.service' @@ -74,44 +74,34 @@ export class AppService { private signingService: SigningService ) {} - async #verifySignature(requestSignature: Signature, verificationMessage: string): Promise { - const { pubKey, alg, sig } = requestSignature - const credential = this.entityRepository.getCredentialForPubKey(pubKey) + async #verifySignature(requestSignature: JwtString, verificationMessage: string): Promise { + const { header } = await decode(requestSignature) + + const credential = await this.entityRepository.getCredential(header.kid) if (!credential) { throw new NotFoundException('Credential not found') } - if (alg === Alg.ES256K) { - // TODO: ensure sig & pubkey begins with 0x - const signature = sig.startsWith('0x') ? sig : `0x${sig}` - const address = pubKey as Hex - const valid = await verifyMessage({ - message: verificationMessage, - address, - signature: signature as Hex - }) - if (!valid) { - console.log('### invalid', { - pubKey, - sig - }) + const jwk = publicKeyToJwk(credential.pubKey as Hex) - throw new BadRequestException('Invalid signature') - } + const validJwt = await verifyJwt(requestSignature, jwk) + // Check the data is the same + if (validJwt.payload.requestHash !== verificationMessage) { + throw new BadRequestException('Invalid signature') } return credential } async #populateApprovals( - approvals: Signature[] | undefined, + approvals: JwtString[] | undefined, verificationMessage: string ): Promise { if (!approvals) return null const approvalSigs = await Promise.all( - approvals.map(async ({ sig, alg, pubKey }) => { - const credential = await this.#verifySignature({ sig, alg, pubKey }, verificationMessage) + approvals.map(async (jwt) => { + const credential = await this.#verifySignature(jwt, verificationMessage) return credential }) ) diff --git a/apps/policy-engine/src/engine/evaluation-request.dto.ts b/apps/policy-engine/src/engine/evaluation-request.dto.ts index 0a09f55a9..7660f50a6 100644 --- a/apps/policy-engine/src/engine/evaluation-request.dto.ts +++ b/apps/policy-engine/src/engine/evaluation-request.dto.ts @@ -1,13 +1,4 @@ -import { - AccessList, - AccountId, - Action, - Address, - BaseActionDto, - FiatCurrency, - Hex, - SignatureDto -} from '@narval/policy-engine-shared' +import { AccessList, AccountId, Action, Address, BaseActionDto, FiatCurrency, Hex } from '@narval/policy-engine-shared' import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger' import { Transform, Type } from 'class-transformer' import { IsDefined, IsEthereumAddress, IsIn, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -131,17 +122,14 @@ export class HistoricalTransferDto { @ApiExtraModels(SignTransactionRequestDataDto, SignMessageRequestDataDto) export class EvaluationRequestDto { @IsDefined() - @ValidateNested() @ApiProperty() - authentication: SignatureDto + authentication: string @IsOptional() - @ValidateNested() @ApiProperty({ - type: () => SignatureDto, isArray: true }) - approvals?: SignatureDto[] + approvals?: string[] @ValidateNested() @Type((opts) => { diff --git a/apps/policy-engine/src/engine/persistence/repository/entity.repository.ts b/apps/policy-engine/src/engine/persistence/repository/entity.repository.ts index b48079999..cd132f3a4 100644 --- a/apps/policy-engine/src/engine/persistence/repository/entity.repository.ts +++ b/apps/policy-engine/src/engine/persistence/repository/entity.repository.ts @@ -17,4 +17,7 @@ export class EntityRepository { getCredentialForPubKey(pubKey: string): CredentialEntity | null { return FIXTURE.ENTITIES.credentials.find((cred) => cred.pubKey === pubKey) || null } + getCredential(id: string): CredentialEntity | null { + return FIXTURE.ENTITIES.credentials.find((cred) => cred.id === id) || null + } } diff --git a/apps/policy-engine/src/engine/persistence/repository/mock_data.ts b/apps/policy-engine/src/engine/persistence/repository/mock_data.ts index 9a626a3e5..b8b2aecce 100644 --- a/apps/policy-engine/src/engine/persistence/repository/mock_data.ts +++ b/apps/policy-engine/src/engine/persistence/repository/mock_data.ts @@ -1,5 +1,6 @@ import { Action, EvaluationRequest, FIXTURE, Request, TransactionRequest } from '@narval/policy-engine-shared' -import { Alg, hash } from '@narval/signature' +import { Payload, SigningAlg, buildSignerEip191, hash, privateKeyToJwk, signJwt } from '@narval/signature' +import { UNSAFE_PRIVATE_KEY } from 'packages/policy-engine-shared/src/lib/dev.fixture' import { toHex } from 'viem' export const ONE_ETH = BigInt('1000000000000000000') @@ -23,29 +24,32 @@ export const generateInboundRequest = async (): Promise => { } const message = hash(request) + const payload: Payload = { + requestHash: message + } - const aliceSignature = await FIXTURE.ACCOUNT.Alice.signMessage({ message }) - const bobSignature = await FIXTURE.ACCOUNT.Bob.signMessage({ message }) - const carolSignature = await FIXTURE.ACCOUNT.Carol.signMessage({ message }) - + // const aliceSignature = await FIXTURE.ACCOUNT.Alice.signMessage({ message }) + const aliceSignature = await signJwt( + payload, + privateKeyToJwk(UNSAFE_PRIVATE_KEY.Alice), + { alg: SigningAlg.EIP191 }, + buildSignerEip191(UNSAFE_PRIVATE_KEY.Alice) + ) + const bobSignature = await signJwt( + payload, + privateKeyToJwk(UNSAFE_PRIVATE_KEY.Bob), + { alg: SigningAlg.EIP191 }, + buildSignerEip191(UNSAFE_PRIVATE_KEY.Bob) + ) + const carolSignature = await signJwt( + payload, + privateKeyToJwk(UNSAFE_PRIVATE_KEY.Carol), + { alg: SigningAlg.EIP191 }, + buildSignerEip191(UNSAFE_PRIVATE_KEY.Carol) + ) return { - authentication: { - sig: aliceSignature, - alg: Alg.ES256K, - pubKey: FIXTURE.ACCOUNT.Alice.address - }, + authentication: aliceSignature, request, - approvals: [ - { - sig: bobSignature, - alg: Alg.ES256K, - pubKey: FIXTURE.ACCOUNT.Bob.address - }, - { - sig: carolSignature, - alg: Alg.ES256K, - pubKey: FIXTURE.ACCOUNT.Carol.address - } - ] + approvals: [bobSignature, carolSignature] } } diff --git a/packages/policy-engine-shared/src/lib/dev.fixture.ts b/packages/policy-engine-shared/src/lib/dev.fixture.ts index a3e595bff..327122656 100644 --- a/packages/policy-engine-shared/src/lib/dev.fixture.ts +++ b/packages/policy-engine-shared/src/lib/dev.fixture.ts @@ -1,5 +1,5 @@ -import { Alg } from '@narval/signature' -import { PrivateKeyAccount, sha256 } from 'viem' +import { Alg, addressToKid } from '@narval/signature' +import { PrivateKeyAccount } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { Action } from './type/action.type' import { EntityType, ValueOperators } from './type/domain.type' @@ -81,32 +81,37 @@ export const USER: Record = { export const CREDENTIAL: Record = { Root: { - id: sha256(ACCOUNT.Root.address).toLowerCase(), + id: addressToKid(ACCOUNT.Root.address), pubKey: ACCOUNT.Root.publicKey, + address: ACCOUNT.Root.address, alg: Alg.ES256K, userId: USER.Root.id }, Alice: { - id: sha256(ACCOUNT.Alice.address).toLowerCase(), + id: addressToKid(ACCOUNT.Alice.address), pubKey: ACCOUNT.Alice.publicKey, + address: ACCOUNT.Alice.address, alg: Alg.ES256K, userId: USER.Alice.id }, Bob: { - id: sha256(ACCOUNT.Bob.address).toLowerCase(), + id: addressToKid(ACCOUNT.Bob.address), pubKey: ACCOUNT.Bob.publicKey, + address: ACCOUNT.Bob.address, alg: Alg.ES256K, userId: USER.Bob.id }, Carol: { - id: sha256(ACCOUNT.Carol.address).toLowerCase(), + id: addressToKid(ACCOUNT.Carol.address), pubKey: ACCOUNT.Carol.publicKey, + address: ACCOUNT.Carol.address, alg: Alg.ES256K, userId: USER.Carol.id }, Dave: { - id: sha256(ACCOUNT.Dave.address).toLowerCase(), + id: addressToKid(ACCOUNT.Dave.address), pubKey: ACCOUNT.Dave.publicKey, + address: ACCOUNT.Dave.address, alg: Alg.ES256K, userId: USER.Dave.id } diff --git a/packages/policy-engine-shared/src/lib/dto/base-action-request.dto.ts b/packages/policy-engine-shared/src/lib/dto/base-action-request.dto.ts index 89a09c538..db8a03599 100644 --- a/packages/policy-engine-shared/src/lib/dto/base-action-request.dto.ts +++ b/packages/policy-engine-shared/src/lib/dto/base-action-request.dto.ts @@ -1,20 +1,16 @@ import { ApiProperty } from '@nestjs/swagger' -import { ArrayNotEmpty, IsDefined, ValidateNested } from 'class-validator' -import { SignatureDto } from './signature.dto' +import { ArrayNotEmpty, IsDefined, IsString } from 'class-validator' export class BaseActionRequestDto { @IsDefined() - @ApiProperty({ - type: () => SignatureDto - }) - authentication: SignatureDto + @IsString() + @ApiProperty() + authentication: string @IsDefined() @ArrayNotEmpty() - @ValidateNested() @ApiProperty({ - type: () => SignatureDto, isArray: true }) - approvals: SignatureDto[] + approvals: string[] } diff --git a/packages/policy-engine-shared/src/lib/schema/entity.schema.ts b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts index 5234f935a..1981f384f 100644 --- a/packages/policy-engine-shared/src/lib/schema/entity.schema.ts +++ b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts @@ -24,6 +24,7 @@ export const accountClassificationSchema = z.nativeEnum({ export const credentialEntitySchema = z.object({ id: z.string(), pubKey: z.string(), + address: z.string().optional(), alg: z.nativeEnum(Alg), userId: z.string() }) diff --git a/packages/policy-engine-shared/src/lib/type/action.type.ts b/packages/policy-engine-shared/src/lib/type/action.type.ts index 63b897011..6adcb3fee 100644 --- a/packages/policy-engine-shared/src/lib/type/action.type.ts +++ b/packages/policy-engine-shared/src/lib/type/action.type.ts @@ -1,5 +1,4 @@ -import { Alg } from '@narval/signature' -import { Address, TransactionRequest } from './domain.type' +import { Address, JwtString, TransactionRequest } from './domain.type' import { AccountClassification, AccountType, @@ -45,15 +44,6 @@ export const Action = { export type Action = (typeof Action)[keyof typeof Action] // DOMAIN -export type Signature = { - sig: string - alg: Alg - /** - * Depending on the alg, this may be necessary (e.g., RSA cannot recover the - * public key from the signature) - */ - pubKey: string -} /** * Action Types; these correspond to each Action @@ -68,12 +58,12 @@ export type BaseAdminRequest = { * The initiator signature of the request using `hashRequest` method to ensure * SHA256 format. */ - authentication: Signature + authentication: JwtString /** * Approval from the ENGINE; this is the attestation generated by an Evaluation of the action, and now the ENGINE is the consumer of the attestation to do a data change. */ - approvals: Signature[] + approvals: JwtString[] } export type SignTransactionAction = BaseAction & { diff --git a/packages/policy-engine-shared/src/lib/type/domain.type.ts b/packages/policy-engine-shared/src/lib/type/domain.type.ts index 2a5242b46..2d1aa46d6 100644 --- a/packages/policy-engine-shared/src/lib/type/domain.type.ts +++ b/packages/policy-engine-shared/src/lib/type/domain.type.ts @@ -1,11 +1,5 @@ import { AssetId } from '../util/caip.util' -import { - CreateOrganizationAction, - SignMessageAction, - SignTransactionAction, - SignTypedDataAction, - Signature -} from './action.type' +import { CreateOrganizationAction, SignMessageAction, SignTransactionAction, SignTypedDataAction } from './action.type' export enum Decision { PERMIT = 'Permit', @@ -49,6 +43,8 @@ export enum IdentityOperators { IN = 'in' } +export type JwtString = string + export type HistoricalTransfer = { /** * Amount in the smallest unit of the token (eg. wei for ETH). @@ -107,11 +103,12 @@ export type Feed = { source: string /** * The signature of the data acts as an attestation and prevents tampering. + * in the JWT format * * Null values are allowed because organizations have the option to not use * any trusted data sources if they prefer. */ - sig: Signature | null + sig: JwtString | null data: Data } @@ -122,19 +119,16 @@ export type Feed = { * being authorized. This is the data that will be hashed and signed. */ export type EvaluationRequest = { - /** - * The initiator signature of the request using `hashRequest` method to ensure - * SHA256 format. - */ - authentication: Signature + // JWT string signing the Request payload + authentication: JwtString /** * The authorization request of */ request: Request /** - * List of approvals required by the policy. + * JWT strings signing the Request payload */ - approvals?: Signature[] + approvals?: JwtString[] transfers?: HistoricalTransfer[] prices?: Prices /** @@ -173,7 +167,6 @@ export type EvaluationResponse = { missing: ApprovalRequirement[] satisfied: ApprovalRequirement[] } - attestation?: Signature // @deprecated, use AccessToken accessToken?: AccessToken transactionRequestIntent?: unknown } diff --git a/packages/signature/src/lib/sign.ts b/packages/signature/src/lib/sign.ts index 1f974ccac..d9bb8e149 100644 --- a/packages/signature/src/lib/sign.ts +++ b/packages/signature/src/lib/sign.ts @@ -2,8 +2,8 @@ import { secp256k1 } from '@noble/curves/secp256k1' import { sha256 as sha256Hash } from '@noble/hashes/sha256' import { keccak_256 as keccak256 } from '@noble/hashes/sha3' import { SignJWT, base64url, importJWK } from 'jose' -import { signatureToHex, toBytes, toHex } from 'viem' -import { EcdsaSignature, Header, JWK, Payload, SigningAlg } from './types' +import { isHex, signatureToHex, toBytes, toHex } from 'viem' +import { EcdsaSignature, Header, Hex, JWK, Payload, SigningAlg } from './types' import { hexToBase64Url } from './utils' // WIP to replace `sign` @@ -39,8 +39,9 @@ export async function signJwt( return jwt } -export const signSecp256k1 = (hash: Uint8Array, privateKey: string, isEth?: boolean): EcdsaSignature => { - const { r, s, recovery } = secp256k1.sign(hash, privateKey) +export const signSecp256k1 = (hash: Uint8Array, privateKey: Hex | string, isEth?: boolean): EcdsaSignature => { + const pk = isHex(privateKey) ? privateKey.slice(2) : privateKey + const { r, s, recovery } = secp256k1.sign(hash, pk) const rHex = toHex(r, { size: 32 }) const sHex = toHex(s, { size: 32 }) const recoveryBn = isEth ? 27n + BigInt(recovery) : BigInt(recovery) @@ -75,10 +76,9 @@ export const eip191Hash = (message: string): Uint8Array => { } export const buildSignerEip191 = - (privateKey: string) => + (privateKey: Hex | string) => async (messageToSign: string): Promise => { const hash = eip191Hash(messageToSign) - const signature = signSecp256k1(hash, privateKey, true) const hexSignature = signatureToHex(signature) return hexToBase64Url(hexSignature) diff --git a/packages/signature/src/lib/types.ts b/packages/signature/src/lib/types.ts index fe9f92be7..2e7c9fbc8 100644 --- a/packages/signature/src/lib/types.ts +++ b/packages/signature/src/lib/types.ts @@ -61,7 +61,11 @@ export type Hex = `0x${string}` // DOMAIN export type Header = { alg: SigningAlg kid: string // Key ID to identify the signing key - typ: 'JWT' + typ: 'JWT' | 'gnap-binding-jwsd' // see https://www.ietf.org/archive/id/draft-ietf-gnap-core-protocol-19.html#name-detached-jws + htm?: string | undefined // HTTP Method + uri?: string | undefined // The HTTP URI used for this request. This value MUST be an absolute URI, including all path and query components and no fragment component. + created?: number | undefined // The time the request was created. + ath?: string | undefined // The hash of the access token. The value MUST be the result of Base64url encoding (with no padding) the SHA-256 digest of the ASCII encoding of the associated access token's value. } /** @@ -78,10 +82,10 @@ export type Header = { * */ export type Payload = { - sub: string + sub?: string iat?: number exp?: number - iss: string + iss?: string aud?: string jti?: string cnf?: JWK // The client-bound key diff --git a/packages/signature/src/lib/utils.ts b/packages/signature/src/lib/utils.ts index 4887ae715..b5955e023 100644 --- a/packages/signature/src/lib/utils.ts +++ b/packages/signature/src/lib/utils.ts @@ -1,4 +1,5 @@ import { secp256k1 } from '@noble/curves/secp256k1' +import { sha256 as sha256Hash } from '@noble/hashes/sha256' import { toHex } from 'viem' import { getAddress, publicKeyToAddress } from 'viem/utils' import { Alg, Curves, Hex, JWK, KeyTypes } from './types' @@ -33,6 +34,10 @@ export const algToJwk = ( } } +export const addressToKid = (address: string): string => { + return toHex(sha256Hash(address.toLowerCase())) +} + // ES256k export const publicKeyToJwk = (publicKey: Hex, keyId?: string): JWK => { // remove the 0x04 prefix -- 04 means it's an uncompressed ECDSA key, 02 or 03 means compressed -- these need to be removed in a JWK! @@ -44,7 +49,7 @@ export const publicKeyToJwk = (publicKey: Hex, keyId?: string): JWK => { crv: Curves.SECP256K1, alg: Alg.ES256K, // use: Use.SIG, - kid: keyId || publicKeyToAddress(publicKey), // add an opaque prefix that indicates the key type + kid: keyId || addressToKid(publicKeyToAddress(publicKey)), // add an opaque prefix that indicates the key type x: hexToBase64Url(`0x${x}`), y: hexToBase64Url(`0x${y}`) } @@ -66,7 +71,7 @@ export const addressToJwk = (address: string, keyId?: string): JWK => { kty: KeyTypes.EC, crv: Curves.SECP256K1, alg: Alg.ES256K, - kid: keyId || getAddress(address), + kid: keyId || addressToKid(address), addr: getAddress(address) } }