@sphereon/oid4vci-client 0.2.0 → 0.4.1-unstable.247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +494 -371
- package/dist/AccessTokenClient.d.ts +30 -0
- package/dist/AccessTokenClient.d.ts.map +1 -0
- package/dist/AccessTokenClient.js +226 -0
- package/dist/AccessTokenClient.js.map +1 -0
- package/dist/AuthorizationDetailsBuilder.d.ts +11 -0
- package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -0
- package/dist/AuthorizationDetailsBuilder.js +44 -0
- package/dist/AuthorizationDetailsBuilder.js.map +1 -0
- package/dist/CredentialOffer.d.ts +6 -0
- package/dist/CredentialOffer.d.ts.map +1 -0
- package/dist/CredentialOffer.js +49 -0
- package/dist/CredentialOffer.js.map +1 -0
- package/dist/CredentialRequestClient.d.ts +29 -0
- package/dist/CredentialRequestClient.d.ts.map +1 -0
- package/dist/CredentialRequestClient.js +63 -0
- package/dist/CredentialRequestClient.js.map +1 -0
- package/dist/CredentialRequestClientBuilderV1_0_09.d.ts +29 -0
- package/dist/CredentialRequestClientBuilderV1_0_09.d.ts.map +1 -0
- package/dist/CredentialRequestClientBuilderV1_0_09.js +63 -0
- package/dist/CredentialRequestClientBuilderV1_0_09.js.map +1 -0
- package/dist/{main/lib/MetadataClient.d.ts → MetadataClient.d.ts} +39 -38
- package/dist/MetadataClient.d.ts.map +1 -0
- package/dist/MetadataClient.js +148 -0
- package/dist/MetadataClient.js.map +1 -0
- package/dist/OpenID4VCIClient.d.ts +72 -0
- package/dist/OpenID4VCIClient.d.ts.map +1 -0
- package/dist/OpenID4VCIClient.js +361 -0
- package/dist/OpenID4VCIClient.js.map +1 -0
- package/dist/ProofOfPossessionBuilder.d.ts +35 -0
- package/dist/ProofOfPossessionBuilder.d.ts.map +1 -0
- package/dist/ProofOfPossessionBuilder.js +120 -0
- package/dist/ProofOfPossessionBuilder.js.map +1 -0
- package/dist/{main/lib/functions → functions}/Encoding.d.ts +20 -17
- package/dist/functions/Encoding.d.ts.map +1 -0
- package/dist/functions/Encoding.js +144 -0
- package/dist/functions/Encoding.js.map +1 -0
- package/dist/functions/HttpUtils.d.ts +24 -0
- package/dist/functions/HttpUtils.d.ts.map +1 -0
- package/dist/functions/HttpUtils.js +93 -0
- package/dist/functions/HttpUtils.js.map +1 -0
- package/dist/functions/ProofUtil.d.ts +29 -0
- package/dist/functions/ProofUtil.d.ts.map +1 -0
- package/dist/functions/ProofUtil.js +103 -0
- package/dist/functions/ProofUtil.js.map +1 -0
- package/dist/functions/index.d.ts +4 -0
- package/dist/functions/index.d.ts.map +1 -0
- package/dist/{main/lib/functions → functions}/index.js +20 -20
- package/dist/functions/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/{main/lib/index.js → index.js} +25 -24
- package/dist/index.js.map +1 -0
- package/lib/AccessTokenClient.ts +270 -0
- package/lib/AuthorizationDetailsBuilder.ts +46 -0
- package/lib/CredentialOffer.ts +55 -0
- package/lib/CredentialRequestClient.ts +77 -0
- package/lib/CredentialRequestClientBuilderV1_0_09.ts +99 -0
- package/lib/MetadataClient.ts +147 -0
- package/lib/OpenID4VCIClient.ts +477 -0
- package/lib/ProofOfPossessionBuilder.ts +156 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +221 -0
- package/lib/__tests__/AuthorizationDetailsBuilder.spec.ts +65 -0
- package/lib/__tests__/AuthzFlowType.spec.ts +39 -0
- package/lib/__tests__/CredentialRequestClient.spec.ts +261 -0
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +103 -0
- package/lib/__tests__/HttpUtils.spec.ts +37 -0
- package/lib/__tests__/IT.spec.ts +155 -0
- package/lib/__tests__/IssuanceInitiation.spec.ts +37 -0
- package/lib/__tests__/JsonURIConversions.spec.ts +86 -0
- package/lib/__tests__/MetadataClient.spec.ts +198 -0
- package/lib/__tests__/MetadataMocks.ts +428 -0
- package/lib/__tests__/OpenID4VCIClient.spec.ts +166 -0
- package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +112 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +109 -0
- package/lib/__tests__/data/VciDataFixtures.ts +744 -0
- package/lib/functions/Encoding.ts +138 -0
- package/lib/functions/HttpUtils.ts +106 -0
- package/lib/functions/ProofUtil.ts +128 -0
- package/{dist/main/lib/functions/index.d.ts → lib/functions/index.ts} +3 -3
- package/lib/index.ts +8 -0
- package/package.json +68 -71
- package/CHANGELOG.md +0 -21
- package/dist/main/index.d.ts +0 -1
- package/dist/main/index.js +0 -18
- package/dist/main/lib/AccessTokenClient.d.ts +0 -20
- package/dist/main/lib/AccessTokenClient.js +0 -141
- package/dist/main/lib/CredentialRequestClient.d.ts +0 -31
- package/dist/main/lib/CredentialRequestClient.js +0 -66
- package/dist/main/lib/CredentialRequestClientBuilder.d.ts +0 -21
- package/dist/main/lib/CredentialRequestClientBuilder.js +0 -56
- package/dist/main/lib/IssuanceInitiation.d.ts +0 -5
- package/dist/main/lib/IssuanceInitiation.js +0 -29
- package/dist/main/lib/MetadataClient.js +0 -127
- package/dist/main/lib/functions/Encoding.js +0 -138
- package/dist/main/lib/functions/HttpUtils.d.ts +0 -17
- package/dist/main/lib/functions/HttpUtils.js +0 -133
- package/dist/main/lib/functions/ProofUtil.d.ts +0 -9
- package/dist/main/lib/functions/ProofUtil.js +0 -76
- package/dist/main/lib/index.d.ts +0 -7
- package/dist/main/lib/types/Authorization.types.d.ts +0 -66
- package/dist/main/lib/types/Authorization.types.js +0 -35
- package/dist/main/lib/types/CredentialIssuance.types.d.ts +0 -88
- package/dist/main/lib/types/CredentialIssuance.types.js +0 -8
- package/dist/main/lib/types/Generic.types.d.ts +0 -19
- package/dist/main/lib/types/Generic.types.js +0 -11
- package/dist/main/lib/types/OAuth2ASMetadata.d.ts +0 -37
- package/dist/main/lib/types/OAuth2ASMetadata.js +0 -3
- package/dist/main/lib/types/OID4VCIServerMetadata.d.ts +0 -65
- package/dist/main/lib/types/OID4VCIServerMetadata.js +0 -3
- package/dist/main/lib/types/Oidc4vciErrors.d.ts +0 -3
- package/dist/main/lib/types/Oidc4vciErrors.js +0 -7
- package/dist/main/lib/types/index.d.ts +0 -6
- package/dist/main/lib/types/index.js +0 -23
- package/dist/main/tsconfig.build.tsbuildinfo +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { KeyObject } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { Alg, CredentialRequest, IssuerMetadata, Jwt, JWTPayload, ProofOfPossession, Typ } from '@sphereon/oid4vci-common';
|
|
4
|
+
import * as jose from 'jose';
|
|
5
|
+
|
|
6
|
+
import { CredentialRequestClientBuilderV1_0_09, ProofOfPossessionBuilder } from '..';
|
|
7
|
+
|
|
8
|
+
import { IDENTIPROOF_ISSUER_URL, IDENTIPROOF_OID4VCI_METADATA, INITIATION_TEST_URI, WALT_ISSUER_URL, WALT_OID4VCI_METADATA } from './MetadataMocks';
|
|
9
|
+
|
|
10
|
+
const partialJWT = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmN';
|
|
11
|
+
|
|
12
|
+
const jwt: Jwt = {
|
|
13
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: Typ.JWT },
|
|
14
|
+
payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const kid = 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1';
|
|
18
|
+
|
|
19
|
+
let keypair: KeyPair;
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
const { privateKey, publicKey } = await jose.generateKeyPair('ES256');
|
|
23
|
+
keypair = { publicKey: publicKey as KeyObject, privateKey: privateKey as KeyObject };
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promise<string> {
|
|
27
|
+
if (!args.payload.aud) {
|
|
28
|
+
throw Error('aud required');
|
|
29
|
+
} else if (!kid) {
|
|
30
|
+
throw Error('kid required');
|
|
31
|
+
}
|
|
32
|
+
return await new jose.SignJWT({ ...args.payload })
|
|
33
|
+
.setProtectedHeader({ alg: 'ES256' })
|
|
34
|
+
.setIssuedAt()
|
|
35
|
+
.setIssuer(kid)
|
|
36
|
+
.setAudience(args.payload.aud)
|
|
37
|
+
.setExpirationTime('2h')
|
|
38
|
+
.sign(keypair.privateKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface KeyPair {
|
|
42
|
+
publicKey: KeyObject;
|
|
43
|
+
privateKey: KeyObject;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function proofOfPossessionVerifierCallbackFunction(args: { jwt: string; kid?: string }): Promise<Jwt> {
|
|
47
|
+
const result = await jose.jwtVerify(args.jwt, keypair.publicKey);
|
|
48
|
+
return { header: result.protectedHeader, payload: result.payload as unknown as JWTPayload };
|
|
49
|
+
}
|
|
50
|
+
describe('Credential Request Client Builder', () => {
|
|
51
|
+
it('should build correctly provided with correct params', function () {
|
|
52
|
+
const credReqClient = CredentialRequestClientBuilderV1_0_09.fromURI({ uri: INITIATION_TEST_URI })
|
|
53
|
+
.withCredentialEndpoint('https://oidc4vci.demo.spruceid.com/credential')
|
|
54
|
+
.withFormat('jwt_vc')
|
|
55
|
+
.withCredentialType('credentialType')
|
|
56
|
+
.withToken('token')
|
|
57
|
+
.build();
|
|
58
|
+
expect(credReqClient.credentialRequestOpts.credentialEndpoint).toBe('https://oidc4vci.demo.spruceid.com/credential');
|
|
59
|
+
expect(credReqClient.credentialRequestOpts.format).toBe('jwt_vc');
|
|
60
|
+
expect(credReqClient.credentialRequestOpts.credentialType).toBe('credentialType');
|
|
61
|
+
expect(credReqClient.credentialRequestOpts.token).toBe('token');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should build credential request correctly', async () => {
|
|
65
|
+
const credReqClient = CredentialRequestClientBuilderV1_0_09.fromURI({ uri: INITIATION_TEST_URI })
|
|
66
|
+
.withCredentialEndpoint('https://oidc4vci.demo.spruceid.com/credential')
|
|
67
|
+
.withFormat('jwt_vc')
|
|
68
|
+
.withCredentialType('https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#OpenBadgeCredential')
|
|
69
|
+
.build();
|
|
70
|
+
const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({
|
|
71
|
+
jwt,
|
|
72
|
+
callbacks: {
|
|
73
|
+
signCallback: proofOfPossessionCallbackFunction,
|
|
74
|
+
verifyCallback: proofOfPossessionVerifierCallbackFunction,
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
.withClientId('sphereon:wallet')
|
|
78
|
+
.withKid(kid)
|
|
79
|
+
.build();
|
|
80
|
+
await proofOfPossessionVerifierCallbackFunction({ ...proof, kid });
|
|
81
|
+
const credentialRequest: CredentialRequest = await credReqClient.createCredentialRequest({ proofInput: proof });
|
|
82
|
+
expect(credentialRequest.proof.jwt).toContain(partialJWT);
|
|
83
|
+
expect(credentialRequest.type).toBe('https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#OpenBadgeCredential');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should build correctly from metadata', async () => {
|
|
87
|
+
const credReqClient = CredentialRequestClientBuilderV1_0_09.fromURI({
|
|
88
|
+
uri: INITIATION_TEST_URI,
|
|
89
|
+
metadata: WALT_OID4VCI_METADATA,
|
|
90
|
+
})
|
|
91
|
+
.withFormat('jwt_vc')
|
|
92
|
+
.build();
|
|
93
|
+
expect(credReqClient.credentialRequestOpts.credentialEndpoint).toBe(`${WALT_ISSUER_URL}/credential`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should build correctly with endpoint from metadata', async () => {
|
|
97
|
+
const credReqClient = CredentialRequestClientBuilderV1_0_09.fromURI({ uri: INITIATION_TEST_URI })
|
|
98
|
+
.withFormat('jwt_vc')
|
|
99
|
+
.withCredentialEndpointFromMetadata(IDENTIPROOF_OID4VCI_METADATA as unknown as IssuerMetadata)
|
|
100
|
+
.build();
|
|
101
|
+
expect(credReqClient.credentialRequestOpts.credentialEndpoint).toBe(`${IDENTIPROOF_ISSUER_URL}/credential`);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isValidURL } from '../functions';
|
|
2
|
+
|
|
3
|
+
describe('httputils.isValidURL', () => {
|
|
4
|
+
it('Should return true for http://localhost', () => {
|
|
5
|
+
expect(isValidURL('http://localhost:4000/some/random/path')).toBeTruthy();
|
|
6
|
+
});
|
|
7
|
+
it('Should return false when no scheme is used', () => {
|
|
8
|
+
expect(isValidURL('sphereon.com/some/random/path')).toBeFalsy();
|
|
9
|
+
});
|
|
10
|
+
it('Should return false when a different scheme than http(s) is used', () => {
|
|
11
|
+
expect(isValidURL('ftp://sphereon.com/some/random/path')).toBeFalsy();
|
|
12
|
+
});
|
|
13
|
+
it('Should return false for invalid scheme http:xxx', () => {
|
|
14
|
+
expect(isValidURL('http:localhost:4000/some/random/path')).toBeFalsy();
|
|
15
|
+
});
|
|
16
|
+
it('Should return false for non-localhost hostname with no domain', () => {
|
|
17
|
+
expect(isValidURL('https://mydomain/some/random/path')).toBeFalsy();
|
|
18
|
+
});
|
|
19
|
+
it('Should return true for https://sphereon.com', () => {
|
|
20
|
+
expect(isValidURL('https://sphereon.com/some/random/path')).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
it('Should return true for https://sphereon.com:400', () => {
|
|
23
|
+
expect(isValidURL('https://sphereon.com:400/some/random/path')).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
it('Should return true when no path is supplied', () => {
|
|
26
|
+
expect(isValidURL('https://sphereon.com')).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
it('Should return true for https://sphereon.com:400/some/random/path?query=param', () => {
|
|
29
|
+
expect(isValidURL('https://sphereon.com:400/some/random/path?query=param')).toBeTruthy();
|
|
30
|
+
});
|
|
31
|
+
it('Should return true for https://sphereon.com:400/some/random/path#fragment', () => {
|
|
32
|
+
expect(isValidURL('https://sphereon.com:400/some/random/path#fragment')).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
it('Should return true for https://sphereon.com:400/some/random/path?query=param#fragment', () => {
|
|
35
|
+
expect(isValidURL('https://sphereon.com:400/some/random/path?query=param#fragment')).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { AccessTokenResponse, Alg, AuthzFlowType, CredentialOfferRequestWithBaseUrl, Jwt, ProofOfPossession, Typ } from '@sphereon/oid4vci-common';
|
|
2
|
+
import nock from 'nock';
|
|
3
|
+
|
|
4
|
+
import { AccessTokenClient, CredentialRequestClientBuilderV1_0_09, OpenID4VCIClient, ProofOfPossessionBuilder } from '..';
|
|
5
|
+
import { CredentialOffer } from '../CredentialOffer';
|
|
6
|
+
|
|
7
|
+
import { IDENTIPROOF_AS_METADATA, IDENTIPROOF_AS_URL, IDENTIPROOF_ISSUER_URL, IDENTIPROOF_OID4VCI_METADATA } from './MetadataMocks';
|
|
8
|
+
|
|
9
|
+
export const UNIT_TEST_TIMEOUT = 30000;
|
|
10
|
+
|
|
11
|
+
const ISSUER_URL = 'https://issuer.research.identiproof.io';
|
|
12
|
+
const jwt = {
|
|
13
|
+
header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: Typ.JWT },
|
|
14
|
+
payload: { iss: 'test-clientId', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: ISSUER_URL },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('OID4VCI-Client should', () => {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
|
+
async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> {
|
|
20
|
+
return 'ey.val.ue';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Access token mocks
|
|
24
|
+
const mockedAccessTokenResponse: AccessTokenResponse = {
|
|
25
|
+
access_token: 'ey6546.546654.64565',
|
|
26
|
+
authorization_pending: false,
|
|
27
|
+
c_nonce: 'c_nonce2022101300',
|
|
28
|
+
c_nonce_expires_in: 2025101300,
|
|
29
|
+
interval: 2025101300,
|
|
30
|
+
token_type: 'Bearer',
|
|
31
|
+
};
|
|
32
|
+
const mockedVC =
|
|
33
|
+
'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL2V4YW1wbGVzL3YxIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiaHR0cHM6Ly9leGFtcGxlLmVkdS9pc3N1ZXJzLzU2NTA0OSIsImlzc3VhbmNlRGF0ZSI6IjIwMTAtMDEtMDFUMDA6MDA6MDBaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifX19LCJpc3MiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwibmJmIjoxMjYyMzA0MDAwLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSJ9.z5vgMTK1nfizNCg5N-niCOL3WUIAL7nXy-nGhDZYO_-PNGeE-0djCpWAMH8fD8eWSID5PfkPBYkx_dfLJnQ7NA';
|
|
34
|
+
const INITIATE_QR =
|
|
35
|
+
'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';
|
|
36
|
+
const OFFER_QR =
|
|
37
|
+
'openid-credential-offer://credential_offer=%7B%22credential_issuer%22:%22https://credential-issuer.example.com%22,%22credentials%22:%5B%7B%22format%22:%22jwt_vc_json%22,%22types%22:%5B%22VerifiableCredential%22,%22UniversityDegreeCredential%22%5D%7D%5D,%22issuer_state%22:%22eyJhbGciOiJSU0Et...FYUaBy%22%7D';
|
|
38
|
+
|
|
39
|
+
function succeedWithAFullFlowWithClientSetup() {
|
|
40
|
+
nock(IDENTIPROOF_ISSUER_URL).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
41
|
+
nock(IDENTIPROOF_AS_URL).get('/.well-known/oauth-authorization-server').reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
42
|
+
nock(IDENTIPROOF_AS_URL)
|
|
43
|
+
.post(/oauth2\/token.*/)
|
|
44
|
+
.reply(200, JSON.stringify(mockedAccessTokenResponse));
|
|
45
|
+
nock(ISSUER_URL)
|
|
46
|
+
.post(/credential/)
|
|
47
|
+
.reply(200, {
|
|
48
|
+
format: 'jwt-vc',
|
|
49
|
+
credential: mockedVC,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
it('succeed with a full flow with the client using OpenID4VCI version 9', async () => {
|
|
54
|
+
succeedWithAFullFlowWithClientSetup();
|
|
55
|
+
const client = await OpenID4VCIClient.fromURI({
|
|
56
|
+
uri: INITIATE_QR,
|
|
57
|
+
flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW,
|
|
58
|
+
kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
|
|
59
|
+
alg: Alg.ES256,
|
|
60
|
+
clientId: 'test-clientId',
|
|
61
|
+
});
|
|
62
|
+
await assertionOfsucceedWithAFullFlowWithClient(client);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test.skip('succeed with a full flow wit the client using OpenID4VCI version 11', async () => {
|
|
66
|
+
succeedWithAFullFlowWithClientSetup();
|
|
67
|
+
const client = await OpenID4VCIClient.fromURI({
|
|
68
|
+
uri: OFFER_QR,
|
|
69
|
+
flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW,
|
|
70
|
+
kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
|
|
71
|
+
alg: Alg.ES256,
|
|
72
|
+
clientId: 'test-clientId',
|
|
73
|
+
});
|
|
74
|
+
await assertionOfsucceedWithAFullFlowWithClient(client);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
async function assertionOfsucceedWithAFullFlowWithClient(client: OpenID4VCIClient) {
|
|
78
|
+
expect(client.flowType).toEqual(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW);
|
|
79
|
+
expect(client.credentialOffer).toBeDefined();
|
|
80
|
+
expect(client.endpointMetadata).toBeDefined();
|
|
81
|
+
expect(client.getIssuer()).toEqual('https://issuer.research.identiproof.io');
|
|
82
|
+
expect(client.getCredentialEndpoint()).toEqual('https://issuer.research.identiproof.io/credential');
|
|
83
|
+
expect(client.getAccessTokenEndpoint()).toEqual('https://auth.research.identiproof.io/oauth2/token');
|
|
84
|
+
|
|
85
|
+
const accessToken = await client.acquireAccessToken({ pin: '1234' });
|
|
86
|
+
expect(accessToken).toEqual(mockedAccessTokenResponse);
|
|
87
|
+
|
|
88
|
+
const credentialResponse = await client.acquireCredentials({
|
|
89
|
+
credentialType: 'OpenBadgeCredential',
|
|
90
|
+
proofCallbacks: {
|
|
91
|
+
signCallback: proofOfPossessionCallbackFunction,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
expect(credentialResponse.credential).toEqual(mockedVC);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
it(
|
|
98
|
+
'succeed with a full flow without the client',
|
|
99
|
+
async () => {
|
|
100
|
+
/* Convert the URI into an object */
|
|
101
|
+
const credentialOffer: CredentialOfferRequestWithBaseUrl = CredentialOffer.fromURI(INITIATE_QR);
|
|
102
|
+
|
|
103
|
+
expect(credentialOffer.baseUrl).toEqual('openid-initiate-issuance://');
|
|
104
|
+
expect(credentialOffer.request).toEqual({
|
|
105
|
+
credential_type: 'OpenBadgeCredentialUrl',
|
|
106
|
+
issuer: ISSUER_URL,
|
|
107
|
+
'pre-authorized_code':
|
|
108
|
+
'4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp',
|
|
109
|
+
user_pin_required: 'true',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
nock(ISSUER_URL)
|
|
113
|
+
.post(/token.*/)
|
|
114
|
+
.reply(200, JSON.stringify(mockedAccessTokenResponse));
|
|
115
|
+
|
|
116
|
+
/* The actual access token calls */
|
|
117
|
+
const accessTokenClient: AccessTokenClient = new AccessTokenClient();
|
|
118
|
+
const accessTokenResponse = await accessTokenClient.acquireAccessToken({ credentialOffer: credentialOffer, pin: '1234' });
|
|
119
|
+
expect(accessTokenResponse.successBody).toEqual(mockedAccessTokenResponse);
|
|
120
|
+
// Get the credential
|
|
121
|
+
nock(ISSUER_URL)
|
|
122
|
+
.post(/credential/)
|
|
123
|
+
.reply(200, {
|
|
124
|
+
format: 'jwt-vc',
|
|
125
|
+
credential: mockedVC,
|
|
126
|
+
});
|
|
127
|
+
const credReqClient = CredentialRequestClientBuilderV1_0_09.fromCredentialOffer({ credentialOffer: credentialOffer })
|
|
128
|
+
.withFormat('jwt_vc')
|
|
129
|
+
|
|
130
|
+
.withTokenFromResponse(accessTokenResponse.successBody!)
|
|
131
|
+
.build();
|
|
132
|
+
|
|
133
|
+
//TS2322: Type '(args: ProofOfPossessionCallbackArgs) => Promise<string>'
|
|
134
|
+
// is not assignable to type 'ProofOfPossessionCallback'.
|
|
135
|
+
// Types of parameters 'args' and 'args' are incompatible.
|
|
136
|
+
// Property 'kid' is missing in type '{ header: unknown; payload: unknown; }' but required in type 'ProofOfPossessionCallbackArgs'.
|
|
137
|
+
const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({
|
|
138
|
+
jwt,
|
|
139
|
+
callbacks: {
|
|
140
|
+
signCallback: proofOfPossessionCallbackFunction,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
.withEndpointMetadata({
|
|
144
|
+
issuer: 'https://issuer.research.identiproof.io',
|
|
145
|
+
credential_endpoint: 'https://issuer.research.identiproof.io/credential',
|
|
146
|
+
token_endpoint: 'https://issuer.research.identiproof.io/token',
|
|
147
|
+
})
|
|
148
|
+
.withKid('did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1')
|
|
149
|
+
.build();
|
|
150
|
+
const credResponse = await credReqClient.acquireCredentialsUsingProof({ proofInput: proof });
|
|
151
|
+
expect(credResponse.successBody?.credential).toEqual(mockedVC);
|
|
152
|
+
},
|
|
153
|
+
UNIT_TEST_TIMEOUT
|
|
154
|
+
);
|
|
155
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OpenId4VCIVersion } from '@sphereon/oid4vci-common';
|
|
2
|
+
|
|
3
|
+
import { CredentialOffer } from '../CredentialOffer';
|
|
4
|
+
|
|
5
|
+
import { INITIATION_TEST, INITIATION_TEST_HTTPS_URI, INITIATION_TEST_URI } from './MetadataMocks';
|
|
6
|
+
|
|
7
|
+
describe('Issuance Initiation', () => {
|
|
8
|
+
it('Should return Issuance Initiation Request with base URL from https URI', () => {
|
|
9
|
+
expect(CredentialOffer.fromURI(INITIATION_TEST_HTTPS_URI)).toEqual({
|
|
10
|
+
baseUrl: 'https://server.example.com',
|
|
11
|
+
request: {
|
|
12
|
+
credential_type: ['https://did.example.org/healthCard', 'https://did.example.org/driverLicense'],
|
|
13
|
+
issuer: 'https://server.example.com',
|
|
14
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
15
|
+
},
|
|
16
|
+
version: OpenId4VCIVersion.VER_1_0_09,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('Should return Issuance Initiation Request with base URL from openid-initiate-issuance URI', () => {
|
|
21
|
+
expect(CredentialOffer.fromURI(INITIATION_TEST_URI)).toEqual(INITIATION_TEST);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('Should return Issuance Initiation URI from request', () => {
|
|
25
|
+
expect(CredentialOffer.toURI(INITIATION_TEST)).toEqual(INITIATION_TEST_URI);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('Should return URI from Issuance Initiation Request', () => {
|
|
29
|
+
const issuanceInitiationClient = CredentialOffer.fromURI(INITIATION_TEST_HTTPS_URI);
|
|
30
|
+
expect(CredentialOffer.toURI(issuanceInitiationClient)).toEqual(INITIATION_TEST_HTTPS_URI);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('Should throw error on invalid URI', () => {
|
|
34
|
+
const issuanceInitiationURI = INITIATION_TEST_HTTPS_URI.replace('?', '');
|
|
35
|
+
expect(() => CredentialOffer.fromURI(issuanceInitiationURI)).toThrowError('Invalid Issuance Initiation Request Payload');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { convertJsonToURI, convertURIToJsonObject } from '..';
|
|
2
|
+
|
|
3
|
+
describe('JSON To URI', () => {
|
|
4
|
+
it('should parse an object into open-id-URI with a single credential_type', () => {
|
|
5
|
+
expect(
|
|
6
|
+
convertJsonToURI(
|
|
7
|
+
{
|
|
8
|
+
issuer: 'https://server.example.com',
|
|
9
|
+
credential_type: 'https://did.example.org/healthCard',
|
|
10
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
uriTypeProperties: ['issuer', 'credential_type'],
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
).toEqual(
|
|
17
|
+
'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&op_state=eyJhbGciOiJSU0Et...FYUaBy'
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
it('should parse an object into open-id-URI with an array of credential_type', () => {
|
|
21
|
+
expect(
|
|
22
|
+
convertJsonToURI(
|
|
23
|
+
{
|
|
24
|
+
issuer: 'https://server.example.com',
|
|
25
|
+
credential_type: ['https://did.example.org/healthCard', 'https://did.example.org/driverLicense'],
|
|
26
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
arrayTypeProperties: ['credential_type'],
|
|
30
|
+
uriTypeProperties: ['issuer', 'credential_type'],
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
).toEqual(
|
|
34
|
+
'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy'
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
it('should parse an object into open-id-URI with an array of credential_type and json string', () => {
|
|
38
|
+
expect(
|
|
39
|
+
convertJsonToURI(
|
|
40
|
+
JSON.stringify({
|
|
41
|
+
issuer: 'https://server.example.com',
|
|
42
|
+
credential_type: ['https://did.example.org/healthCard', 'https://did.example.org/driverLicense'],
|
|
43
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
44
|
+
}),
|
|
45
|
+
{
|
|
46
|
+
arrayTypeProperties: ['credential_type'],
|
|
47
|
+
uriTypeProperties: ['issuer', 'credential_type'],
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
).toEqual(
|
|
51
|
+
'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy'
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('URI To Json Object', () => {
|
|
56
|
+
it('should parse open-id-URI as json object with a single credential_type', () => {
|
|
57
|
+
expect(
|
|
58
|
+
convertURIToJsonObject(
|
|
59
|
+
'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&op_state=eyJhbGciOiJSU0Et...FYUaBy',
|
|
60
|
+
{
|
|
61
|
+
arrayTypeProperties: ['credential_type'],
|
|
62
|
+
requiredProperties: ['issuer', 'credential_type'],
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
).toEqual({
|
|
66
|
+
issuer: 'https://server.example.com',
|
|
67
|
+
credential_type: 'https://did.example.org/healthCard',
|
|
68
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
it('should parse open-id-URI as json object with an array of credential_type', () => {
|
|
72
|
+
expect(
|
|
73
|
+
convertURIToJsonObject(
|
|
74
|
+
'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy',
|
|
75
|
+
{
|
|
76
|
+
arrayTypeProperties: ['credential_type'],
|
|
77
|
+
requiredProperties: ['issuer', 'credential_type'],
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
).toEqual({
|
|
81
|
+
issuer: 'https://server.example.com',
|
|
82
|
+
credential_type: ['https://did.example.org/healthCard', 'https://did.example.org/driverLicense'],
|
|
83
|
+
op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { getIssuerFromCredentialOfferPayload, WellKnownEndpoints } from '@sphereon/oid4vci-common';
|
|
2
|
+
import nock from 'nock';
|
|
3
|
+
|
|
4
|
+
import { CredentialOffer } from '../CredentialOffer';
|
|
5
|
+
import { MetadataClient } from '../MetadataClient';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
DANUBE_ISSUER_URL,
|
|
9
|
+
DANUBE_OIDC_METADATA,
|
|
10
|
+
IDENTIPROOF_AS_METADATA,
|
|
11
|
+
IDENTIPROOF_AS_URL,
|
|
12
|
+
IDENTIPROOF_ISSUER_URL,
|
|
13
|
+
IDENTIPROOF_OID4VCI_METADATA,
|
|
14
|
+
SPRUCE_ISSUER_URL,
|
|
15
|
+
SPRUCE_OID4VCI_METADATA,
|
|
16
|
+
WALT_ISSUER_URL,
|
|
17
|
+
WALT_OID4VCI_METADATA,
|
|
18
|
+
} from './MetadataMocks';
|
|
19
|
+
|
|
20
|
+
describe('MetadataClient with IdentiProof Issuer should', () => {
|
|
21
|
+
beforeAll(() => {
|
|
22
|
+
nock.cleanAll();
|
|
23
|
+
});
|
|
24
|
+
it('succeed with OID4VCI and separate AS metadata', async () => {
|
|
25
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
26
|
+
|
|
27
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
28
|
+
|
|
29
|
+
const metadata = await MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL);
|
|
30
|
+
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
|
|
31
|
+
expect(metadata.token_endpoint).toEqual('https://auth.research.identiproof.io/oauth2/token');
|
|
32
|
+
expect(metadata.issuerMetadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('succeed with OID4VCI and separate AS metadata from Initiation', async () => {
|
|
36
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
37
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
38
|
+
|
|
39
|
+
const INITIATE_URI =
|
|
40
|
+
'openid-initiate-issuance://?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredential&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhOTUyZjUxNi1jYWVmLTQ4YjMtODIxYy00OTRkYzgyNjljZjAiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.YE5DlalcLC2ChGEg47CQDaN1gTxbaQqSclIVqsSAUHE&user_pin_required=false';
|
|
41
|
+
const initiation = CredentialOffer.fromURI(INITIATE_URI);
|
|
42
|
+
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(initiation.request) as string);
|
|
43
|
+
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
|
|
44
|
+
expect(metadata.token_endpoint).toEqual('https://auth.research.identiproof.io/oauth2/token');
|
|
45
|
+
expect(metadata.issuerMetadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Fail without OID4VCI and only AS metadata (no credential endpoint)', async () => {
|
|
49
|
+
nock(IDENTIPROOF_ISSUER_URL)
|
|
50
|
+
.get(WellKnownEndpoints.OPENID4VCI_ISSUER)
|
|
51
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
52
|
+
|
|
53
|
+
nock(IDENTIPROOF_ISSUER_URL)
|
|
54
|
+
.get(WellKnownEndpoints.OPENID_CONFIGURATION)
|
|
55
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
56
|
+
|
|
57
|
+
nock(IDENTIPROOF_ISSUER_URL)
|
|
58
|
+
.get(WellKnownEndpoints.OAUTH_AS)
|
|
59
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
60
|
+
|
|
61
|
+
await expect(() => MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
62
|
+
'Could not deduce the token endpoint for https://issuer.research.identiproof.io'
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('Fail with OID4VCI and no AS metadata', async () => {
|
|
67
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
68
|
+
nock(IDENTIPROOF_ISSUER_URL)
|
|
69
|
+
.get(WellKnownEndpoints.OPENID_CONFIGURATION)
|
|
70
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
71
|
+
|
|
72
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, JSON.stringify({}));
|
|
73
|
+
await expect(() => MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL)).rejects.toThrowError('{"error": "not found"}');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('Fail if there is no token endpoint with errors enabled', async () => {
|
|
77
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
78
|
+
const meta = JSON.parse(JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
79
|
+
delete meta.token_endpoint;
|
|
80
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(meta));
|
|
81
|
+
|
|
82
|
+
await expect(() => MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
83
|
+
'Could not deduce the token endpoint for https://issuer.research.identiproof.io'
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('Fail if there is no credential endpoint with errors enabled', async () => {
|
|
88
|
+
const meta = JSON.parse(JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
89
|
+
delete meta.credential_endpoint;
|
|
90
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(meta));
|
|
91
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
92
|
+
|
|
93
|
+
await expect(() => MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
94
|
+
'Could not deduce the credential endpoint for https://issuer.research.identiproof.io'
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('Succeed with default value if there is no credential endpoint with errors disabled', async () => {
|
|
99
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(IDENTIPROOF_OID4VCI_METADATA));
|
|
100
|
+
nock(IDENTIPROOF_AS_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, JSON.stringify(IDENTIPROOF_AS_METADATA));
|
|
101
|
+
|
|
102
|
+
const metadata = await MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL);
|
|
103
|
+
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('Succeed with no well-known endpoints and errors disabled', async () => {
|
|
107
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(404, {});
|
|
108
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
|
|
109
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
|
|
110
|
+
|
|
111
|
+
const metadata = await MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL);
|
|
112
|
+
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('Fail when specific well-known is not found with errors enabled', async () => {
|
|
116
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(404, {});
|
|
117
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {});
|
|
118
|
+
nock(IDENTIPROOF_ISSUER_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {});
|
|
119
|
+
|
|
120
|
+
const metadata = MetadataClient.retrieveWellknown(IDENTIPROOF_ISSUER_URL, WellKnownEndpoints.OPENID4VCI_ISSUER, { errorOnNotFound: true });
|
|
121
|
+
await expect(metadata).rejects.toThrowError('{"error": "not found"}');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('Metadataclient with Spruce Issuer should', () => {
|
|
126
|
+
it('succeed with OID4VCI and separate AS metadata', async () => {
|
|
127
|
+
nock(SPRUCE_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(SPRUCE_OID4VCI_METADATA));
|
|
128
|
+
|
|
129
|
+
const metadata = await MetadataClient.retrieveAllMetadata(SPRUCE_ISSUER_URL);
|
|
130
|
+
expect(metadata.credential_endpoint).toEqual('https://ngi-oidc4vci-test.spruceid.xyz/credential');
|
|
131
|
+
expect(metadata.token_endpoint).toEqual('https://ngi-oidc4vci-test.spruceid.xyz/token');
|
|
132
|
+
expect(metadata.issuerMetadata).toEqual(SPRUCE_OID4VCI_METADATA);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('Fail without OID4VCI', async () => {
|
|
136
|
+
nock(SPRUCE_ISSUER_URL)
|
|
137
|
+
.get(/.*/)
|
|
138
|
+
.times(3)
|
|
139
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
140
|
+
|
|
141
|
+
await expect(() => MetadataClient.retrieveAllMetadata(SPRUCE_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
142
|
+
'Could not deduce the token endpoint for https://ngi-oidc4vci-test.spruceid.xyz'
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('Metadataclient with Danubetech should', () => {
|
|
148
|
+
it('succeed without OID4VCI and with OIDC metadata', async () => {
|
|
149
|
+
nock(DANUBE_ISSUER_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(200, JSON.stringify(DANUBE_OIDC_METADATA));
|
|
150
|
+
|
|
151
|
+
nock(DANUBE_ISSUER_URL)
|
|
152
|
+
.get(/.well-known\/.*/)
|
|
153
|
+
.times(2)
|
|
154
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
155
|
+
const metadata = await MetadataClient.retrieveAllMetadata(DANUBE_ISSUER_URL);
|
|
156
|
+
expect(metadata.credential_endpoint).toEqual('https://oidc4vc.uniissuer.io/credential');
|
|
157
|
+
expect(metadata.token_endpoint).toEqual('https://oidc4vc.uniissuer.io/token');
|
|
158
|
+
expect(metadata.issuerMetadata).toEqual(DANUBE_OIDC_METADATA);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('Fail without OID4VCI', async () => {
|
|
162
|
+
nock(SPRUCE_ISSUER_URL)
|
|
163
|
+
.get(/.*/)
|
|
164
|
+
.times(3)
|
|
165
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
166
|
+
|
|
167
|
+
await expect(() => MetadataClient.retrieveAllMetadata(SPRUCE_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
168
|
+
'Could not deduce the token endpoint for https://ngi-oidc4vci-test.spruceid.xyz'
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('Metadataclient with Walt-id should', () => {
|
|
174
|
+
it('succeed without OID4VCI and with OIDC metadata', async () => {
|
|
175
|
+
nock(WALT_ISSUER_URL).get(WellKnownEndpoints.OPENID4VCI_ISSUER).reply(200, JSON.stringify(WALT_OID4VCI_METADATA));
|
|
176
|
+
|
|
177
|
+
nock(WALT_ISSUER_URL)
|
|
178
|
+
.get(/.well-known\/.*/)
|
|
179
|
+
.times(2)
|
|
180
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
181
|
+
|
|
182
|
+
const metadata = await MetadataClient.retrieveAllMetadata(WALT_ISSUER_URL);
|
|
183
|
+
expect(metadata.credential_endpoint).toEqual('https://jff.walt.id/issuer-api/oidc/credential');
|
|
184
|
+
expect(metadata.token_endpoint).toEqual('https://jff.walt.id/issuer-api/oidc/token');
|
|
185
|
+
expect(metadata.issuerMetadata).toEqual(WALT_OID4VCI_METADATA);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('Fail without OID4VCI', async () => {
|
|
189
|
+
nock(WALT_ISSUER_URL)
|
|
190
|
+
.get(/.*/)
|
|
191
|
+
.times(4)
|
|
192
|
+
.reply(404, JSON.stringify({ error: 'does not exist' }));
|
|
193
|
+
|
|
194
|
+
await expect(() => MetadataClient.retrieveAllMetadata(WALT_ISSUER_URL, { errorOnNotFound: true })).rejects.toThrowError(
|
|
195
|
+
'Could not deduce the token endpoint for https://jff.walt.id/issuer-api/oidc'
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
});
|