Skip to content

Commit

Permalink
feat: Added new tests for CredentialRequestClient plus fixed a proble…
Browse files Browse the repository at this point in the history
…m with CredentialOfferUtil. a CredentialRequest can have no issuer field

Signed-off-by: sksadjad <[email protected]>
  • Loading branch information
sksadjad committed Apr 20, 2023
1 parent a6b1eea commit 50f2292
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 17 deletions.
4 changes: 3 additions & 1 deletion packages/client/lib/AccessTokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export class AccessTokenClient {
const { request } = credentialOffer;

const isPinRequired = this.isPinRequiredValue(request);
const issuerOpts = { issuer: getIssuerFromCredentialOfferPayload(request) };
const issuerOpts = {
issuer: getIssuerFromCredentialOfferPayload(request) ? (getIssuerFromCredentialOfferPayload(request) as string) : (metadata?.issuer as string),
};

return await this.acquireAccessTokenUsingRequest({
accessTokenRequest: await this.createAccessTokenRequest({
Expand Down
4 changes: 3 additions & 1 deletion packages/client/lib/CredentialRequestClientBuilderV1_0_09.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export class CredentialRequestClientBuilderV1_0_09 {
metadata?: EndpointMetadata;
}): CredentialRequestClientBuilderV1_0_09 {
const builder = new CredentialRequestClientBuilderV1_0_09();
const issuer = getIssuerFromCredentialOfferPayload(request);
const issuer = getIssuerFromCredentialOfferPayload(request)
? (getIssuerFromCredentialOfferPayload(request) as string)
: (metadata?.issuer as string);
builder.withCredentialEndpoint(
metadata?.credential_endpoint ? metadata.credential_endpoint : issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`
);
Expand Down
5 changes: 4 additions & 1 deletion packages/client/lib/MetadataClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export class MetadataClient {
* @param request
*/
public static async retrieveAllMetadataFromCredentialOfferRequest(request: CredentialOfferPayload): Promise<EndpointMetadata> {
return MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request));
if (getIssuerFromCredentialOfferPayload(request)) {
return MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request) as string);
}
throw new Error("can't retrieve metadata from CredentialOfferRequest. No issuer field is present");
}

/**
Expand Down
105 changes: 102 additions & 3 deletions packages/client/lib/__tests__/CredentialRequestClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ import { KeyObject } from 'crypto';
import {
Alg,
CredentialRequest,
EndpointMetadata,
getIssuerFromCredentialOfferPayload,
Jwt,
ProofOfPossession,
ProofType,
Typ,
URL_NOT_VALID,
WellKnownEndpoints,
} from '@sphereon/openid4vci-common';
import * as jose from 'jose';
import nock from 'nock';

import { CredentialRequestClientBuilderV1_0_09, MetadataClient } from '..';
import { ProofOfPossessionBuilder } from '..';
import { CredentialRequestClientBuilderV1_0_09, MetadataClient, ProofOfPossessionBuilder } from '..';
import { CredentialOffer } from '../CredentialOffer';

import { IDENTIPROOF_ISSUER_URL, IDENTIPROOF_OID4VCI_METADATA, INITIATION_TEST, WALT_OID4VCI_METADATA } from './MetadataMocks';
import { getMockData } from './data/VciDataFixtures';

const partialJWT = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmN';

Expand Down Expand Up @@ -149,7 +151,7 @@ describe('Credential Request Client with Walt.id ', () => {
const credentialOffer = CredentialOffer.fromURI(WALT_IRR_URI);

const request = credentialOffer.request;
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request));
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request) as string);
expect(metadata.credential_endpoint).toEqual(WALT_OID4VCI_METADATA.credential_endpoint);
expect(metadata.token_endpoint).toEqual(WALT_OID4VCI_METADATA.token_endpoint);

Expand All @@ -160,3 +162,100 @@ describe('Credential Request Client with Walt.id ', () => {
expect(credReqClient.credentialRequestOpts.credentialEndpoint).toBe(WALT_OID4VCI_METADATA.credential_endpoint);
});
});

describe('Credential Request Client with different issuers ', () => {
it('should create correct CredentialRequest for Spruce', async () => {
const IRR_URI =
'openid-initiate-issuance://?issuer=https%3A%2F%2Fngi%2Doidc4vci%2Dtest%2Espruceid%2Exyz&credential_type=OpenBadgeCredential&pre-authorized_code=eyJhbGciOiJFUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOlsiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJleHAiOiIyMDIzLTA0LTIwVDA5OjA0OjM2WiIsIm5vbmNlIjoibWFibmVpT0VSZVB3V3BuRFFweEt3UnRsVVRFRlhGUEwifQ.qOZRPN8sTv_knhp7WaWte2-aDULaPZX--2i9unF6QDQNUllqDhvxgIHMDCYHCV8O2_Gj-T2x1J84fDMajE3asg&user_pin_required=false';
const credentialOffer = await CredentialRequestClientBuilderV1_0_09.fromURI({
uri: IRR_URI,
metadata: getMockData('spruce')?.metadata as unknown as EndpointMetadata,
})
.build()
.createCredentialRequest({
proofInput: {
proof_type: ProofType.JWT,
jwt: getMockData('spruce')?.credential.request.proof.jwt as string,
},
credentialType: 'OpenBadgeCredential',
format: 'jwt_vc',
});
expect(credentialOffer).toEqual(getMockData('spruce')?.credential.request);
});

it('should create correct CredentialRequest for Walt', async () => {
const IRR_URI =
'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&amp;credential_type=OpenBadgeCredential&amp;pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMTc4OTNjYy04ZTY3LTQxNzItYWZlOS1lODcyYmYxNDBlNWMiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.ODfq2AIhOcB61dAb3zMrXBJjPJaf53zkeHh_AssYyYA&amp;user_pin_required=false';
const credentialOffer = await CredentialRequestClientBuilderV1_0_09.fromURI({
uri: IRR_URI,
metadata: getMockData('walt')?.metadata as unknown as EndpointMetadata,
})
.build()
.createCredentialRequest({
proofInput: {
proof_type: ProofType.JWT,
jwt: getMockData('walt')?.credential.request.proof.jwt as string,
},
credentialType: 'OpenBadgeCredential',
format: 'jwt_vc',
});
expect(credentialOffer).toEqual(getMockData('walt')?.credential.request);
});

it('should create correct CredentialRequest for uniissuer', async () => {
const IRR_URI =
'https://oidc4vc.uniissuer.io/&credential_type=OpenBadgeCredential&pre-authorized_code=0ApoI8rxVmdQ44RIpuDbFIURIIkOhyek&user_pin_required=false';
const credentialOffer = await CredentialRequestClientBuilderV1_0_09.fromURI({
uri: IRR_URI,
metadata: getMockData('uniissuer')?.metadata as unknown as EndpointMetadata,
})
.build()
.createCredentialRequest({
proofInput: {
proof_type: ProofType.JWT,
jwt: getMockData('uniissuer')?.credential.request.proof.jwt as string,
},
credentialType: 'OpenBadgeCredential',
format: 'jwt_vc',
});
expect(credentialOffer).toEqual(getMockData('uniissuer')?.credential.request);
});

it('should create correct CredentialRequest for mattr', async () => {
const IRR_URI =
'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=g0UCOj6RAN5AwHU6gczm_GzB4_lH6GW39Z0Dl2DOOiO';
const credentialOffer = await CredentialRequestClientBuilderV1_0_09.fromURI({
uri: IRR_URI,
metadata: getMockData('mattr')?.metadata as unknown as EndpointMetadata,
})
.build()
.createCredentialRequest({
proofInput: {
proof_type: ProofType.JWT,
jwt: getMockData('mattr')?.credential.request.proof.jwt as string,
},
credentialType: 'OpenBadgeCredential',
format: 'ldp_vc',
});
expect(credentialOffer).toEqual(getMockData('mattr')?.credential.request);
});

it('should create correct CredentialRequest for diwala', async () => {
const IRR_URI =
'openid-initiate-issuance://?issuer=https://oidc4vc.diwala.io&amp;credential_type=OpenBadgeCredential&amp;pre-authorized_code=eyJhbGciOiJIUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZXhwIjoxNjgxOTg0NDY3fQ.fEAHKz2nuWfiYHw406iNxr-81pWkNkbi31bWsYSf6Ng';
const credentialOffer = await CredentialRequestClientBuilderV1_0_09.fromURI({
uri: IRR_URI,
metadata: getMockData('diwala')?.metadata as unknown as EndpointMetadata,
})
.build()
.createCredentialRequest({
proofInput: {
proof_type: ProofType.JWT,
jwt: getMockData('diwala')?.credential.request.proof.jwt as string,
},
credentialType: 'OpenBadgeCredential',
format: 'ldp_vc',
});
expect(credentialOffer).toEqual(getMockData('diwala')?.credential.request);
});
});
2 changes: 1 addition & 1 deletion packages/client/lib/__tests__/MetadataClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('MetadataClient with IdentiProof Issuer should', () => {
const INITIATE_URI =
'openid-initiate-issuance://?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredential&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhOTUyZjUxNi1jYWVmLTQ4YjMtODIxYy00OTRkYzgyNjljZjAiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.YE5DlalcLC2ChGEg47CQDaN1gTxbaQqSclIVqsSAUHE&user_pin_required=false';
const initiation = CredentialOffer.fromURI(INITIATE_URI);
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(initiation.request));
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(initiation.request) as string);
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
expect(metadata.token_endpoint).toEqual('https://auth.research.identiproof.io/oauth2/token');
expect(metadata.issuerMetadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
Expand Down
21 changes: 14 additions & 7 deletions packages/client/lib/__tests__/data/VciDataFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import {
CredentialSupportedBrief,
IssuerCredentialSubjectDisplay,
IssuerMetadataV1_0_08
} from '@sphereon/openid4vci-common';
import { CredentialSupportedBrief, IssuerCredentialSubjectDisplay, IssuerMetadataV1_0_08 } from '@sphereon/openid4vci-common';
import { ICredentialStatus, W3CVerifiableCredential } from '@sphereon/ssi-types';

export function getMockData(issuerName: string): IssuerMockData|null {
export function getMockData(issuerName: string): IssuerMockData | null {
if (issuerName in mockData) {
return mockData[issuerName];
}
return null
return null;
}

export interface VciMockDataStructure {
Expand Down Expand Up @@ -44,6 +40,7 @@ export interface IssuerMockData {
};
credential: {
url: string;
deeplink: string;
request: {
type: string;
format: 'jwt_vc' | 'ldp_vc' | string;
Expand Down Expand Up @@ -110,6 +107,8 @@ const mockData: VciMockDataStructure = {
},
credential: {
url: 'https://ngi-oidc4vci-test.spruceid.xyz/credential',
deeplink:
'openid-initiate-issuance://?issuer=https%3A%2F%2Fngi%2Doidc4vci%2Dtest%2Espruceid%2Exyz&credential_type=OpenBadgeCredential&pre-authorized_code=eyJhbGciOiJFUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOlsiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJleHAiOiIyMDIzLTA0LTIwVDA5OjA0OjM2WiIsIm5vbmNlIjoibWFibmVpT0VSZVB3V3BuRFFweEt3UnRsVVRFRlhGUEwifQ.qOZRPN8sTv_knhp7WaWte2-aDULaPZX--2i9unF6QDQNUllqDhvxgIHMDCYHCV8O2_Gj-T2x1J84fDMajE3asg&user_pin_required=false',
request: {
type: 'OpenBadgeCredential',
format: 'jwt_vc',
Expand Down Expand Up @@ -353,6 +352,8 @@ const mockData: VciMockDataStructure = {
},
},
credential: {
deeplink:
'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&amp;credential_type=OpenBadgeCredential&amp;pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMTc4OTNjYy04ZTY3LTQxNzItYWZlOS1lODcyYmYxNDBlNWMiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.ODfq2AIhOcB61dAb3zMrXBJjPJaf53zkeHh_AssYyYA&amp;user_pin_required=false',
url: 'https://jff.walt.id/issuer-api/default/oidc/credential',
request: {
type: 'OpenBadgeCredential',
Expand Down Expand Up @@ -469,6 +470,8 @@ const mockData: VciMockDataStructure = {
},
},
credential: {
deeplink:
'https://oidc4vc.uniissuer.io/&credential_type=OpenBadgeCredential&pre-authorized_code=0ApoI8rxVmdQ44RIpuDbFIURIIkOhyek&user_pin_required=false',
url: 'https://oidc4vc.uniissuer.io/1.0/credential',
request: {
type: 'OpenBadgeCredential',
Expand Down Expand Up @@ -566,6 +569,8 @@ const mockData: VciMockDataStructure = {
},
},
credential: {
deeplink:
'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=g0UCOj6RAN5AwHU6gczm_GzB4_lH6GW39Z0Dl2DOOiO',
url: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential',
request: {
type: 'OpenBadgeCredential',
Expand Down Expand Up @@ -678,6 +683,8 @@ const mockData: VciMockDataStructure = {
},
},
credential: {
deeplink:
'openid-initiate-issuance://?issuer=https://oidc4vc.diwala.io&amp;credential_type=OpenBadgeCredential&amp;pre-authorized_code=eyJhbGciOiJIUzI1NiJ9.eyJjcmVkZW50aWFsX3R5cGUiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZXhwIjoxNjgxOTg0NDY3fQ.fEAHKz2nuWfiYHw406iNxr-81pWkNkbi31bWsYSf6Ng',
url: 'https://oidc4vc.diwala.io/credential',
request: {
type: 'OpenBadgeCredential',
Expand Down
6 changes: 3 additions & 3 deletions packages/common/lib/functions/CredentialOfferUtil.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CredentialOfferPayload, DefaultURISchemes, OpenId4VCIVersion, TokenErrorResponse } from '../types';
import { CredentialOfferPayload, DefaultURISchemes, OpenId4VCIVersion } from '../types';

export function determineSpecVersionFromURI(uri: string): OpenId4VCIVersion {
let version: OpenId4VCIVersion = OpenId4VCIVersion.VER_UNKNOWN;
Expand Down Expand Up @@ -50,9 +50,9 @@ function recordVersion(determinedVersion: OpenId4VCIVersion, potentialVersion: O
);
}

export function getIssuerFromCredentialOfferPayload(request: CredentialOfferPayload): string {
export function getIssuerFromCredentialOfferPayload(request: CredentialOfferPayload): string | undefined {
if (!request || !('issuer' in request) || 'credential_issuer' in request) {
throw new Error(TokenErrorResponse.invalid_request);
return undefined;
}
return 'issuer' in request ? request.issuer : request['credential_issuer'];
}

0 comments on commit 50f2292

Please sign in to comment.