UNPKG

@sphereon/oid4vci-client

Version:

OpenID for Verifiable Credential Issuance (OpenID4VCI) client

146 lines (126 loc) 6.01 kB
import { Alg, Jwt } from '@sphereon/oid4vci-common'; import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils'; import { CredentialMapper } from '@sphereon/ssi-types'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore import { from } from '@trust/keyto'; import { fetch } from 'cross-fetch'; import debug from 'debug'; import { base64url, importJWK, JWK, SignJWT } from 'jose'; import * as u8a from 'uint8arrays'; import { OpenID4VCIClientV1_0_11 } from '..'; export const UNIT_TEST_TIMEOUT = 30000; const ISSUER_URL = 'https://conformance-test.ebsi.eu/conformance/v3/issuer-mock'; const AUTH_URL = 'https://conformance-test.ebsi.eu/conformance/v3/auth-mock'; const jwk: JWK = { alg: 'ES256', use: 'sig', kty: 'EC', crv: 'P-256', x: 'hUWYK06qFvdudydiqnEhVJhZ-73jcLtuzH8kIyNOSHE', y: 'UZf7oUkJdo65SQekMD5ssiRclEimG2SmlsjXf3QwQJo', d: 'zDeeo3K0Pk8dofeKcajvJYxMZ1vijx_cVDJQl1IpbAM', }; console.log(`JWK (private/orig): ${JSON.stringify(jwk, null, 2)}`); const privateKey = from(jwk, 'jwk').toString('blk', 'private'); const publicKey = from(jwk, 'jwk').toString('blk', 'public'); console.log(`Private key: ${privateKey}`); console.log(`Public key: ${publicKey}`); console.log(`Private key (b64): ${base64url.encode(u8a.fromString(privateKey, 'base16'))}`); console.log(`JWK (private 2) ${JSON.stringify(toJwk(privateKey, 'Secp256r1', { isPrivateKey: true }))}`); console.log(`JWK (public 2) ${JSON.stringify(toJwk(publicKey, 'Secp256r1', { isPrivateKey: false }))}`); // const DID_METHOD = 'did:key' const DID = 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE'; const DID_URL_ENCODED = 'did%3Akey%3Az2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE'; // const PRIVATE_KEY_HEX = '7dd923e40f4615ac496119f7e793cc2899e99b64b88ca8603db986700089532b' // const PUBLIC_KEY_HEX = // '04a23cb4c83901acc2eb0f852599610de0caeac260bf8ed05e7f902eaac0f9c8d74dd4841b94d13424d32af8ec0e9976db9abfa7e3a59e10d565c5d4d901b4be63' // pub hex: 35e03477cb29f3ac518770dccd4e26e703cd21b9741c24b038170c377b0d99d9 // priv hex: 913466d1a38d1d8c0d3c0fb0fc3b633075085a31372bbd2a8022215a88d9d1e5 // const did = `did:key:z6Mki5ZwZKN1dBQprfJTikUvkDxrHijiiQngkWviMF5gw2Hv`; const kid = `${DID}#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE`; // const jw = jose.importKey() // EBSI returning a 500 in credential endpoint all of a sudden describe.skip('OID4VCI-Client using Sphereon issuer should', () => { async function test(credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossPreAuthorisedDeferred' | 'CTWalletCrossAuthorisedInTime') { debug.enable('*'); const offer = await getCredentialOffer(credentialType); const client = await OpenID4VCIClientV1_0_11.fromURI({ uri: offer, kid, alg: Alg.ES256, clientId: DID_URL_ENCODED, }); expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); expect(client.getCredentialEndpoint()).toEqual(`${ISSUER_URL}/credential`); expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`); if (credentialType !== 'CTWalletCrossPreAuthorisedInTime') { const url = await client.createAuthorizationRequestUrl({ authorizationRequest: { redirectUri: 'openid4vc%3A', }, }); const result = await fetch(url); console.log(result.text()); } const accessToken = await client.acquireAccessToken({ pin: '0891' }); // console.log(accessToken); expect(accessToken).toMatchObject({ expires_in: 86400, // scope: 'GuestCredential', token_type: 'Bearer', }); const format = 'jwt_vc'; const credentialResponse = await client.acquireCredentials({ credentialTypes: client.getCredentialOfferTypes()[0], format, proofCallbacks: { signCallback: proofOfPossessionCallbackFunction, }, kid, deferredCredentialAwait: true, deferredCredentialIntervalInMS: 5000, }); console.log(JSON.stringify(credentialResponse, null, 2)); expect(credentialResponse.credential).toBeDefined(); const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credentialResponse.credential!); expect(format.startsWith(wrappedVC.format)).toEqual(true); } // Current conformance tests is not stable as changes are being applied it seems it( 'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json', async () => { await test('CTWalletCrossPreAuthorisedInTime'); await test('CTWalletCrossPreAuthorisedDeferred'); // await test('CTWalletCrossAuthorisedInTime'); }, UNIT_TEST_TIMEOUT, ); }); async function getCredentialOffer( credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossAuthorisedInTime' | 'CTWalletCrossPreAuthorisedDeferred', ): Promise<string> { const credentialOffer = await fetch( `https://conformance-test.ebsi.eu/conformance/v3/issuer-mock/initiate-credential-offer?credential_type=${credentialType}&client_id=${DID_URL_ENCODED}&credential_offer_endpoint=openid-credential-offer%3A%2F%2F`, { method: 'GET', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }, ); return await credentialOffer.text(); } async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promise<string> { const importedJwk = await importJWK(jwk); return await new SignJWT({ ...args.payload }) .setProtectedHeader({ ...args.header, kid: kid! }) .setIssuer(DID) .setIssuedAt() .setExpirationTime('2m') .sign(importedJwk); }