UNPKG

@sphereon/oid4vci-client

Version:

OpenID for Verifiable Credential Issuance (OpenID4VCI) client

456 lines (409 loc) • 22.3 kB
import { AccessTokenResponse, Alg, CredentialOfferPayloadV1_0_13, CredentialOfferRequestWithBaseUrl, Jwt, OpenId4VCIVersion, ProofOfPossession, resolveCredentialOfferURI, WellKnownEndpoints, } from '@sphereon/oid4vci-common'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import nock from 'nock'; import { AccessTokenClient, AccessTokenClientV1_0_11, OpenID4VCIClient, OpenID4VCIClientV1_0_13, ProofOfPossessionBuilder } from '..'; import { CredentialOfferClient } from '../CredentialOfferClient'; import { CredentialRequestClientBuilder } from '../CredentialRequestClientBuilder'; import { IDENTIPROOF_AS_METADATA, IDENTIPROOF_AS_URL, IDENTIPROOF_ISSUER_URL, IDENTIPROOF_OID4VCI_METADATA, IDENTIPROOF_OID4VCI_METADATA_v13, } from './MetadataMocks'; export const UNIT_TEST_TIMEOUT = 30000; const ISSUER_URL = 'https://issuer.research.identiproof.io'; const jwtDid = { header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'openid4vci-proof+jwt' }, payload: { iss: 'test-clientId', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: ISSUER_URL }, }; const jwtWithoutDid = { header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'openid4vci-proof+jwt' }, payload: { iss: 'test-clientId', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: ISSUER_URL }, }; const mockedVC = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL2V4YW1wbGVzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiaHR0cHM6Ly9leGFtcGxlLmVkdS9pc3N1ZXJzLzU2NTA0OSIsImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMDA6MDA6MDBaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifX19LCJpc3MiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwibmJmIjoxMjYyMzA0MDAwLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSJ9.z5vgMTK1nfizNCg5N-niCOL3WUIAL7nXy-nGhDZYO_-PNGeE-0djCpWAMH8fD8eWSID5PfkPBYkx_dfLJnQ7NA'; // Access token mocks const mockedAccessTokenResponse: AccessTokenResponse = { access_token: 'ey6546.546654.64565', c_nonce: 'c_nonce2022101300', c_nonce_expires_in: 2025101300, interval: 2025101300, token_type: 'Bearer', }; const INITIATE_QR_V1_0_13 = 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%22https://issuer.research.identiproof.io%22,%22credential_configuration_ids%22:%5B%22OpenBadgeCredentialUrl%22%5D,%22grants%22:%7B%22urn:ietf:params:oauth:grant-type:pre-authorized_code%22:%7B%22pre-authorized_code%22:%22oaKazRN8I0IbtZ0C7JuMn5%22,%22tx_code%22:%7B%22input_mode%22:%22text%22,%22length%22:22,%22description%22:%22Please%20enter%20the%20serial%20number%20of%20your%20physical%20drivers%20license%22%7D%7D%7D%7D'; // eslint-disable-next-line @typescript-eslint/no-unused-vars async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> { return 'ey.val.ue'; } describe('OID4VCI-Client should', () => { beforeEach(() => { nock.cleanAll(); }); afterEach(() => { nock.cleanAll(); }); // Access token mocks const mockedAccessTokenResponse: AccessTokenResponse = { access_token: 'ey6546.546654.64565', c_nonce: 'c_nonce2022101300', c_nonce_expires_in: 2025101300, interval: 2025101300, token_type: 'Bearer', }; const INITIATE_QR_V1_0_08 = 'openid-initiate-issuance://?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredentialUrl&pre-authorized_code=4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp&user_pin_required=true'; const OFFER_QR_V1_0_08 = 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22adhjhdjajkdkhjhdj%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D'; const HTTPS_INITIATE_QR = 'https://issuer.research.identiproof.io?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredentialUrl&pre-authorized_code=4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp&user_pin_required=true'; const HTTPS_OFFER_QR_AUTHORIZATION_CODE = 'https://issuer.research.identiproof.io?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22eyJhbGciOiJSU0Et...FYUaBy%22%7D%7D%7D'; const HTTPS_OFFER_QR_PRE_AUTHORIZED = 'https://issuer.research.identiproof.io?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22adhjhdjajkdkhjhdj%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D'; const HTTPS_OFFER_QR_PRE_AUTHORIZED_v13 = 'https://issuer.research.identiproof.io?credential_offer=%7B%0A%20%20%20%20%22credential_issuer%22%3A%20%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%0A%20%20%20%20%22credential_configuration_ids%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%22UniversityDegreeCredential%22%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22grants%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22pre-authorized_code%22%3A%20%22adhjhdjajkdkhjhdj%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22tx_code%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22length%22%3A%204%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22input_mode%22%3A%20%22numeric%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22description%22%3A%20%22Please%20provide%20the%20one-time%20code%20that%20was%20sent%20via%20e-mail%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D'; function succeedWithAFullFlowWithClientSetup() { nock(IDENTIPROOF_ISSUER_URL).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA)); nock(IDENTIPROOF_AS_URL).get('/.well-known/oauth-authorization-server').reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA)); nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {}); nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(200, {}); nock(IDENTIPROOF_AS_URL) .post(/oauth2\/token.*/) .reply(200, JSON.stringify(mockedAccessTokenResponse)); nock(ISSUER_URL) .post(/credential/) .reply(200, { format: 'jwt-vc', credential: mockedVC, }); } it('succeed with a full flow with the client using OpenID4VCI version 9', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: INITIATE_QR_V1_0_08, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); it('succeed with a full flow with the client using OpenID4VCI version 11 and deeplink', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: OFFER_QR_V1_0_08, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); it('succeed with a full flow with the client using OpenID4VCI draft < 9 and https', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: HTTPS_INITIATE_QR, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); it('should succeed with a full flow with the client using OpenID4VCI draft > 11, https and authorization_code flow', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: HTTPS_OFFER_QR_AUTHORIZATION_CODE, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); it('should succeed with a full flow with the client using OpenID4VCI draft > 11, https and preauthorized_code flow', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: HTTPS_OFFER_QR_PRE_AUTHORIZED, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); it('should succeed with a full flow with the client using OpenID4VCI draft >= 13, https and preauthorized_code flow without did', async () => { nock(IDENTIPROOF_ISSUER_URL) .get(WellKnownEndpoints.OPENID_CONFIGURATION) .reply(200, { token_endpoint: `${IDENTIPROOF_ISSUER_URL}/token`, authorization_endpoint: `${IDENTIPROOF_ISSUER_URL}/authorize`, }); nock(IDENTIPROOF_ISSUER_URL).post('/token').reply(200, { access_token: 'ey6546.546654.64565', c_nonce: 'c_nonce2022101300', c_nonce_expires_in: 2025101300, interval: 2025101300, token_type: 'Bearer', }); nock(IDENTIPROOF_ISSUER_URL).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA_v13)); nock(ISSUER_URL) .post(/credential/) .reply(200, { format: 'jwt-vc', credential: mockedVC, }); const client = await OpenID4VCIClientV1_0_13.fromURI({ uri: HTTPS_OFFER_QR_PRE_AUTHORIZED_v13, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); expect(client.getIssuer()).toEqual('https://issuer.research.identiproof.io'); expect(client.getCredentialEndpoint()).toEqual('https://issuer.research.identiproof.io/credential'); expect(client.getAccessTokenEndpoint()).toEqual('https://issuer.research.identiproof.io/token'); const accessToken = await client.acquireAccessToken({ pin: '1234', code: 'ABCD' }); expect(accessToken).toEqual(mockedAccessTokenResponse); const credentialResponse = await client.acquireCredentials({ credentialIdentifier: 'OpenBadgeCredential', // format: 'jwt_vc_json-ld', proofCallbacks: { signCallback: proofOfPossessionCallbackFunction, }, }); expect(credentialResponse.credential).toEqual(mockedVC); }); it('should succeed with a full flow with the client using OpenID4VCI draft > 11, https and preauthorized_code flow', async () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: HTTPS_OFFER_QR_PRE_AUTHORIZED, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', }); await assertionOfsucceedWithAFullFlowWithClient(client); }); async function assertionOfsucceedWithAFullFlowWithClient(client: OpenID4VCIClient) { expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); expect(client.getIssuer()).toEqual('https://issuer.research.identiproof.io'); expect(client.getCredentialEndpoint()).toEqual('https://issuer.research.identiproof.io/credential'); expect(client.getAccessTokenEndpoint()).toEqual('https://auth.research.identiproof.io/oauth2/token'); const accessToken = await client.acquireAccessToken({ pin: '1234', code: 'ABCD' }); expect(accessToken).toEqual(mockedAccessTokenResponse); const credentialResponse = await client.acquireCredentials({ credentialTypes: 'OpenBadgeCredential', format: 'jwt_vc_json-ld', proofCallbacks: { signCallback: proofOfPossessionCallbackFunction, }, }); expect(credentialResponse.credential).toEqual(mockedVC); } it( 'succeed with a full flow without the client v1_0_11', async () => { /* Convert the URI into an object */ const credentialOffer: CredentialOfferRequestWithBaseUrl = await CredentialOfferClient.fromURI(INITIATE_QR_V1_0_08); expect(credentialOffer.baseUrl).toEqual('openid-initiate-issuance://'); expect(credentialOffer.original_credential_offer).toEqual({ credential_type: ['OpenBadgeCredentialUrl'], issuer: ISSUER_URL, 'pre-authorized_code': '4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp', user_pin_required: 'true', }); nock(ISSUER_URL) .post(/token.*/) .reply(200, JSON.stringify(mockedAccessTokenResponse)); /* The actual access token calls */ const accessTokenClient: AccessTokenClientV1_0_11 = new AccessTokenClientV1_0_11(); const accessTokenResponse = await accessTokenClient.acquireAccessToken({ credentialOffer: credentialOffer, pin: '1234' }); expect(accessTokenResponse.successBody).toEqual(mockedAccessTokenResponse); // Get the credential nock(ISSUER_URL) .post(/credential/) .reply(200, { format: 'jwt-vc', credential: mockedVC, }); const credReqClient = CredentialRequestClientBuilder.fromCredentialOffer({ credentialOffer: credentialOffer }) .withFormat('jwt_vc') .withTokenFromResponse(accessTokenResponse.successBody!) .build(); const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({ jwt: jwtDid, callbacks: { signCallback: proofOfPossessionCallbackFunction, }, version: OpenId4VCIVersion.VER_1_0_11, }) .withEndpointMetadata({ issuer: 'https://issuer.research.identiproof.io', credential_endpoint: 'https://issuer.research.identiproof.io/credential', token_endpoint: 'https://issuer.research.identiproof.io/token', }) .withKid('did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1') .build(); const credResponse = await credReqClient.acquireCredentialsUsingProof({ proofInput: proof }); expect(credResponse.successBody?.credential).toEqual(mockedVC); }, UNIT_TEST_TIMEOUT, ); it( 'succeed with a full flow without the client v1_0_13', async () => { /* Convert the URI into an object */ const credentialOffer: CredentialOfferRequestWithBaseUrl = await CredentialOfferClient.fromURI(INITIATE_QR_V1_0_13); const preAuthorizedCode = 'oaKazRN8I0IbtZ0C7JuMn5'; expect(credentialOffer.baseUrl).toEqual('openid-credential-offer://'); expect((credentialOffer.credential_offer as CredentialOfferPayloadV1_0_13).credential_configuration_ids).toEqual(['OpenBadgeCredentialUrl']); expect(credentialOffer.original_credential_offer.grants).toEqual({ 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { 'pre-authorized_code': preAuthorizedCode, tx_code: { input_mode: 'text', description: 'Please enter the serial number of your physical drivers license', length: preAuthorizedCode.length, }, }, }); nock(ISSUER_URL) .post(/token.*/) .reply(200, JSON.stringify(mockedAccessTokenResponse)); /* The actual access token calls */ const accessTokenClient: AccessTokenClient = new AccessTokenClient(); const accessTokenResponse = await accessTokenClient.acquireAccessToken({ credentialOffer: credentialOffer, pin: '1234' }); expect(accessTokenResponse.successBody).toEqual(mockedAccessTokenResponse); // Get the credential nock(ISSUER_URL) .post(/credential/) .reply(200, { format: 'jwt-vc', credential: mockedVC, }); const credReqClient = CredentialRequestClientBuilder.fromCredentialOffer({ credentialOffer: credentialOffer }) .withFormat('jwt_vc') .withTokenFromResponse(accessTokenResponse.successBody!) .build(); const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({ jwt: jwtDid, callbacks: { signCallback: proofOfPossessionCallbackFunction, }, version: OpenId4VCIVersion.VER_1_0_11, }) .withEndpointMetadata({ issuer: 'https://issuer.research.identiproof.io', credential_endpoint: 'https://issuer.research.identiproof.io/credential', token_endpoint: 'https://issuer.research.identiproof.io/token', }) .withKid('did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1') .build(); const credResponse = await credReqClient.acquireCredentialsUsingProof({ proofInput: proof, credentialTypes: credentialOffer.original_credential_offer.credential_configuration_ids[0], }); expect(credResponse.successBody?.credential).toEqual(mockedVC); }, UNIT_TEST_TIMEOUT, ); }); describe('OIDVCI-Client for v1_0_13 should', () => { const mockedCredentialOffer = { credential_issuer: 'https://mijnkvk.acc.credenco.com', credential_configuration_ids: ['BevoegdheidUittreksel_jwt_vc_json'], grants: { authorization_code: { issuer_state: '32fc4ebf-9e31-4149-9877-e3c0b602d559', }, 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { 'pre-authorized_code': 'eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiIzMmZjNGViZi05ZTMxLTQxNDktOTg3Ny1lM2MwYjYwMmQ1NTkiLCJpc3MiOiJodHRwczovL21pam5rdmsuYWNjLmNyZWRlbmNvLmNvbSIsImF1ZCI6IlRPS0VOIn0.754aiQ87O0vHYSpRvPqAS9cLOgf-pewdeXbpLziRwsxEp9mENfaXpY62muYpzOaWcYmTOydkzhFul-NDYXJZCA', }, }, }; beforeEach(() => { // Mock the HTTP GET request to the credential offer URI nock('https://mijnkvk.acc.credenco.com') .get('/openid4vc/credentialOffer?id=32fc4ebf-9e31-4149-9877-e3c0b602d559') .reply(200, mockedCredentialOffer) .persist(); // Use .persist() if you want the mock to remain active for multiple tests }); afterEach(() => { // Clean up all mocks nock.cleanAll(); }); it('should successfully resolve the credential offer URI', async () => { const uri = 'https://mijnkvk.acc.credenco.com/openid4vc/credentialOffer?id=32fc4ebf-9e31-4149-9877-e3c0b602d559'; const credentialOffer = await resolveCredentialOfferURI(uri); expect(credentialOffer).toEqual(mockedCredentialOffer); }); it( 'succeed with a full flow without the client and without did', async () => { /* Convert the URI into an object */ const credentialOffer: CredentialOfferRequestWithBaseUrl = await CredentialOfferClient.fromURI(INITIATE_QR_V1_0_13); expect(credentialOffer.baseUrl).toEqual('openid-credential-offer://'); expect(credentialOffer.original_credential_offer).toEqual({ credential_configuration_ids: ['OpenBadgeCredentialUrl'], credential_issuer: ISSUER_URL, grants: { 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { 'pre-authorized_code': 'oaKazRN8I0IbtZ0C7JuMn5', tx_code: { description: 'Please enter the serial number of your physical drivers license', input_mode: 'text', length: 22, }, }, }, }); nock(ISSUER_URL) .post(/token.*/) .reply(200, JSON.stringify(mockedAccessTokenResponse)); /* The actual access token calls */ const accessTokenClient: AccessTokenClient = new AccessTokenClient(); const accessTokenResponse = await accessTokenClient.acquireAccessToken({ credentialOffer: credentialOffer, pin: '1234' }); expect(accessTokenResponse.successBody).toEqual(mockedAccessTokenResponse); // Get the credential nock(ISSUER_URL) .post(/credential/) .reply(200, { format: 'jwt-vc', credential: mockedVC, }); const credReqClient = CredentialRequestClientBuilder.fromCredentialOffer({ credentialOffer: credentialOffer }) .withFormat('jwt_vc') .withTokenFromResponse(accessTokenResponse.successBody!) .build(); const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({ jwt: jwtWithoutDid, callbacks: { signCallback: proofOfPossessionCallbackFunction, }, version: OpenId4VCIVersion.VER_1_0_13, }) .withEndpointMetadata({ issuer: 'https://issuer.research.identiproof.io', credential_endpoint: 'https://issuer.research.identiproof.io/credential', token_endpoint: 'https://issuer.research.identiproof.io/token', }) .withKid('ebfeb1f712ebc6f1c276e12ec21/keys/1') .build(); const credResponse = await credReqClient.acquireCredentialsUsingProof({ proofInput: proof, credentialIdentifier: 'OpenBadgeCredentialUrl' }); expect(credResponse.successBody?.credential).toEqual(mockedVC); }, UNIT_TEST_TIMEOUT, ); });