From 63044cea0d5421e23f46df77cde391acec2d7fc7 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Mon, 5 Feb 2024 21:16:30 +0100 Subject: [PATCH] chore: EBSI E2E tests --- test/e2e/EBSI.spec.ts | 117 +++++++++++++++++++++++++++++++++ test/interop/EBSI/EBSI.spec.ts | 69 +++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 test/e2e/EBSI.spec.ts create mode 100644 test/interop/EBSI/EBSI.spec.ts diff --git a/test/e2e/EBSI.spec.ts b/test/e2e/EBSI.spec.ts new file mode 100644 index 0000000..0d103f2 --- /dev/null +++ b/test/e2e/EBSI.spec.ts @@ -0,0 +1,117 @@ +import { getResolver as getKeyResolver } from '@cef-ebsi/key-did-resolver'; +// import { EbsiWallet } from '@cef-ebsi/wallet-lib'; +import EbsiWallet from '@cef-ebsi/wallet-lib'; +import { PresentationSignCallBackParams } from '@sphereon/pex'; +import { parseDid, W3CVerifiablePresentation } from '@sphereon/ssi-types'; +import { Resolver } from 'did-resolver'; +import { importJWK, JWK, SignJWT } from 'jose'; +import { v4 as uuidv4 } from 'uuid'; + +import { CheckLinkedDomain, OP, SigningAlgo } from '../../src'; + +const ID_TOKEN_REQUEST_URL = 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock/id_token_request'; + +export const UNIT_TEST_TIMEOUT = 30000; +export const jwk: JWK = { + alg: 'ES256', + kty: 'EC', + use: 'sig', + crv: 'P-256', + x: '9ggs4Cm4VXcKOePpjkL9iSyMCa22yOjbo-oUXpy-aw0', + y: 'lEXW7b_J7lceiVEtrfptvuPeENsOJl-fhzmu654GPR8', +}; +const hexPrivateKey = '47dc6ae067aa011f8574d2da7cf8c326538af08b85e6779d192a9893291c9a0a'; + +const nonce = uuidv4(); +export const generateDid = (_opts?: { seed?: Uint8Array }) => { + const did = EbsiWallet.createDid('NATURAL_PERSON', jwk); + return did; +}; + +const keyResolver = getKeyResolver(); + +const didStr = generateDid(); +const kid = `${didStr}#${parseDid(didStr).id}`; +console.log(kid); +describe('EBSI SIOPv2 should', () => { + async function testWithOp() { + const did = await generateDid(/*{ seed: u8a.fromString(hexPrivateKey, 'base16') }*/); + expect(did).toBeDefined(); + + const authRequestURL = await getAuthRequestURL({ nonce }); + expect(authRequestURL).toBeDefined(); + expect(authRequestURL).toContain('openid://?state='); + expect(authRequestURL).toContain(nonce); + + const correlationId = 'test'; + + const op: OP = OP.builder() + .addResolver('key', new Resolver(keyResolver)) + .withCheckLinkedDomain(CheckLinkedDomain.NEVER) + .withPresentationSignCallback(presentationSignCalback) + .withSignature({ alg: SigningAlgo.ES256, kid, did: didStr, hexPrivateKey }) + .build(); + + const verifiedAuthRequest = await op.verifyAuthorizationRequest(authRequestURL, { correlationId }); + expect(verifiedAuthRequest).toBeDefined(); + + const authResponse = await op.createAuthorizationResponse(verifiedAuthRequest, { + issuer: didStr, + correlationId, + }); + + expect(authResponse).toBeDefined(); + expect(authResponse.response.payload).toBeDefined(); + console.log(JSON.stringify(authResponse)); + + const result = await op.submitAuthorizationResponse(authResponse); + console.log(result.statusText); + console.log(await result.text()); + expect(result.status).toEqual(204); + } + + it( + 'succeed with an id token only', + async () => { + await testWithOp(); + }, + UNIT_TEST_TIMEOUT + ); + + async function getAuthRequestURL({ nonce }: { nonce: string }): Promise { + const credentialOffer = await fetch(ID_TOKEN_REQUEST_URL, { + method: 'post', + headers: { + Accept: 'text/plain', + 'Content-Type': 'application/json', + }, + + //make sure to serialize your JSON body + body: JSON.stringify({ + nonce, + }), + }); + + return await credentialOffer.text(); + } + + async function presentationSignCalback(args: PresentationSignCallBackParams): Promise { + const importedJwk = await importJWK(jwk, 'ES256'); + const jwt = await new SignJWT({ + vp: { ...args.presentation }, + nonce: args.options.proofOptions.nonce, + iss: args.options.holderDID, + }) + .setProtectedHeader({ + typ: 'JWT', + alg: 'ES256', + kid, + }) + .setIssuedAt() + .setExpirationTime('2h') + .sign(importedJwk); + + console.log(`VP: ${jwt}`); + return jwt; + } +}); diff --git a/test/interop/EBSI/EBSI.spec.ts b/test/interop/EBSI/EBSI.spec.ts new file mode 100644 index 0000000..079e5e5 --- /dev/null +++ b/test/interop/EBSI/EBSI.spec.ts @@ -0,0 +1,69 @@ +import { IVerifyCallbackArgs, IVerifyCredentialResult } from '@sphereon/wellknown-dids-client'; +import nock from 'nock'; + +import { AuthorizationResponseOpts, OP, SupportedVersion, VerificationMode, VerifyAuthorizationRequestOpts } from '../../../src'; +import { UNIT_TEST_TIMEOUT } from '../../data/mockedData'; + +const SIOP_URI = + 'openid://?state=3f3a673a-7835-42f1-a03e-b186fd042dcc&client_id=https%3A%2F%2Fconformance-test.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock&redirect_uri=https%3A%2F%2Fconformance-test.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock%2Fdirect_post&response_type=id_token&response_mode=direct_post&scope=openid&nonce=3a50effa-4505-42ce-8708-0c4ab32378dd&request_uri=https%3A%2F%2Fconformance-test.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock%2Frequest_uri%2F4cb2dc1f-61a4-46b7-9660-06d62dd99700'; +const JWT = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkZMeEkzTE04bUZDRkNEMUg0VmpacVd0MVBmaWQyaThBQ1lpRHZFelo5VU0ifQ.eyJzdGF0ZSI6IjNmM2E2NzNhLTc4MzUtNDJmMS1hMDNlLWIxODZmZDA0MmRjYyIsImNsaWVudF9pZCI6Imh0dHBzOi8vY29uZm9ybWFuY2UtdGVzdC5lYnNpLmV1L2NvbmZvcm1hbmNlL3YzL2F1dGgtbW9jayIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vY29uZm9ybWFuY2UtdGVzdC5lYnNpLmV1L2NvbmZvcm1hbmNlL3YzL2F1dGgtbW9jay9kaXJlY3RfcG9zdCIsInJlc3BvbnNlX3R5cGUiOiJpZF90b2tlbiIsInJlc3BvbnNlX21vZGUiOiJkaXJlY3RfcG9zdCIsInNjb3BlIjoib3BlbmlkIiwibm9uY2UiOiIzYTUwZWZmYS00NTA1LTQyY2UtODcwOC0wYzRhYjMyMzc4ZGQiLCJpc3MiOiJodHRwczovL2NvbmZvcm1hbmNlLXRlc3QuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2siLCJhdWQiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnFTWlpGakc0dFZnS2hFd0twcm9qcUxCM0MyWXBqNEg3M1N0Z2pNa1NYZzJtUXh1V0xmenVSMTJRc052Z1FXenJ6S1NmN1lSQk5yUlhLNzF2ZnExMkJieXhUTEZFWkJXZm5IcWV6QlZHUWlOTGZxZXV5d1pIZ3N0TUNjUzQ0VFhmYjIifQ.h0nQfHq2sck4PizIleqlTTPPjYPgEH8OPKK0ug7r_O7N4qEghfILnL07cs5y1gARIH7hJLNNvI7qXEerl-SdDw'; +describe('EBSI', () => { + const responseOpts: AuthorizationResponseOpts = { + /*checkLinkedDomain: CheckLinkedDomain.NEVER, + responseURI: EXAMPLE_REDIRECT_URL, + responseURIType: 'redirect_uri', + signature: { + hexPrivateKey: HEX_KEY, + did: DID, + kid: KID, + alg: SigningAlgo.ES256K, + }, + registration: { + authorizationEndpoint: 'www.myauthorizationendpoint.com', + responseTypesSupported: [ResponseType.ID_TOKEN], + subject_syntax_types_supported: ['did:web'], + vpFormats: { + ldp_vc: { + proof_type: [IProofType.EcdsaSecp256k1Signature2019, IProofType.EcdsaSecp256k1Signature2019], + }, + }, + logo_uri: VERIFIER_LOGO_FOR_CLIENT, + clientName: VERIFIER_NAME_FOR_CLIENT, + 'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100333', + clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY, + 'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL, + //TODO: fill it up with actual value + issuer: ResponseIss.SELF_ISSUED_V2, + passBy: PassBy.VALUE, + }, + responseMode: ResponseMode.POST, + expiresIn: 2000,*/ + }; + + const verifyOpts: VerifyAuthorizationRequestOpts = { + verification: { + mode: VerificationMode.INTERNAL, + resolveOpts: { + subjectSyntaxTypesSupported: ['did:ebsi'], + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + wellknownDIDVerifyCallback: async (_args: IVerifyCallbackArgs): Promise => ({ verified: true }), + }, + correlationId: '1234', + supportedVersions: [SupportedVersion.SIOPv2_D12_OID4VP_D18], + }; + it( + 'succeed from request opts when all params are set', + async () => { + nock('https://conformance-test.ebsi.eu/conformance/v3/auth-mock/request_uri/4cb2dc1f-61a4-46b7-9660-06d62dd99700').get('').reply(200, JWT); + + const op = OP.fromOpts(responseOpts, verifyOpts); + const verifiedRequest = await op.verifyAuthorizationRequest(SIOP_URI); + console.log(JSON.stringify(verifiedRequest, null, 2)); + expect(verifiedRequest.issuer).toMatch('https://conformance-test.ebsi.eu/conformance/v3/auth-mock'); + expect(verifiedRequest.jwt).toBeDefined(); + }, + UNIT_TEST_TIMEOUT + ); +});