@sphereon/oid4vci-client 0.10.3 → 0.10.4-next.119
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/README.md +24 -5
- package/dist/AccessTokenClient.d.ts +5 -5
- package/dist/AccessTokenClient.d.ts.map +1 -1
- package/dist/AccessTokenClient.js +51 -37
- package/dist/AccessTokenClient.js.map +1 -1
- package/dist/AccessTokenClientV1_0_11.d.ts +29 -0
- package/dist/AccessTokenClientV1_0_11.d.ts.map +1 -0
- package/dist/AccessTokenClientV1_0_11.js +209 -0
- package/dist/AccessTokenClientV1_0_11.js.map +1 -0
- package/dist/AuthorizationCodeClient.d.ts +9 -4
- package/dist/AuthorizationCodeClient.d.ts.map +1 -1
- package/dist/AuthorizationCodeClient.js +102 -18
- package/dist/AuthorizationCodeClient.js.map +1 -1
- package/dist/AuthorizationCodeClientV1_0_11.d.ts +9 -0
- package/dist/AuthorizationCodeClientV1_0_11.d.ts.map +1 -0
- package/dist/AuthorizationCodeClientV1_0_11.js +134 -0
- package/dist/AuthorizationCodeClientV1_0_11.js.map +1 -0
- package/dist/CredentialOfferClient.d.ts.map +1 -1
- package/dist/CredentialOfferClient.js +18 -13
- package/dist/CredentialOfferClient.js.map +1 -1
- package/dist/CredentialOfferClientV1_0_11.d.ts +10 -0
- package/dist/CredentialOfferClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialOfferClientV1_0_11.js +101 -0
- package/dist/CredentialOfferClientV1_0_11.js.map +1 -0
- package/dist/CredentialOfferClientV1_0_13.d.ts +10 -0
- package/dist/CredentialOfferClientV1_0_13.d.ts.map +1 -0
- package/dist/CredentialOfferClientV1_0_13.js +94 -0
- package/dist/CredentialOfferClientV1_0_13.js.map +1 -0
- package/dist/CredentialRequestClient.d.ts +20 -7
- package/dist/CredentialRequestClient.d.ts.map +1 -1
- package/dist/CredentialRequestClient.js +46 -30
- package/dist/CredentialRequestClient.js.map +1 -1
- package/dist/CredentialRequestClientBuilder.d.ts +11 -6
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
- package/dist/CredentialRequestClientBuilder.js +22 -9
- package/dist/CredentialRequestClientBuilder.js.map +1 -1
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts +48 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js +121 -0
- package/dist/CredentialRequestClientBuilderV1_0_11.js.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts +50 -0
- package/dist/CredentialRequestClientV1_0_11.d.ts.map +1 -0
- package/dist/CredentialRequestClientV1_0_11.js +151 -0
- package/dist/CredentialRequestClientV1_0_11.js.map +1 -0
- package/dist/MetadataClient.d.ts +5 -15
- package/dist/MetadataClient.d.ts.map +1 -1
- package/dist/MetadataClient.js +41 -44
- package/dist/MetadataClient.js.map +1 -1
- package/dist/MetadataClientV1_0_11.d.ts +31 -0
- package/dist/MetadataClientV1_0_11.d.ts.map +1 -0
- package/dist/MetadataClientV1_0_11.js +182 -0
- package/dist/MetadataClientV1_0_11.js.map +1 -0
- package/dist/MetadataClientV1_0_13.d.ts +31 -0
- package/dist/MetadataClientV1_0_13.d.ts.map +1 -0
- package/dist/MetadataClientV1_0_13.js +181 -0
- package/dist/MetadataClientV1_0_13.js.map +1 -0
- package/dist/OpenID4VCIClient.d.ts +14 -19
- package/dist/OpenID4VCIClient.d.ts.map +1 -1
- package/dist/OpenID4VCIClient.js +111 -61
- package/dist/OpenID4VCIClient.js.map +1 -1
- package/dist/OpenID4VCIClientV1_0_11.d.ts +108 -0
- package/dist/OpenID4VCIClientV1_0_11.d.ts.map +1 -0
- package/dist/OpenID4VCIClientV1_0_11.js +449 -0
- package/dist/OpenID4VCIClientV1_0_11.js.map +1 -0
- package/dist/OpenID4VCIClientV1_0_13.d.ts +112 -0
- package/dist/OpenID4VCIClientV1_0_13.d.ts.map +1 -0
- package/dist/OpenID4VCIClientV1_0_13.js +478 -0
- package/dist/OpenID4VCIClientV1_0_13.js.map +1 -0
- package/dist/ProofOfPossessionBuilder.d.ts +14 -3
- package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
- package/dist/ProofOfPossessionBuilder.js +20 -21
- package/dist/ProofOfPossessionBuilder.js.map +1 -1
- package/dist/functions/OpenIDUtils.d.ts +12 -0
- package/dist/functions/OpenIDUtils.d.ts.map +1 -0
- package/dist/functions/OpenIDUtils.js +37 -0
- package/dist/functions/OpenIDUtils.js.map +1 -0
- package/dist/functions/index.d.ts +2 -3
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +2 -3
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/notifications.d.ts +4 -0
- package/dist/functions/notifications.d.ts.map +1 -0
- package/dist/functions/notifications.js +39 -0
- package/dist/functions/notifications.js.map +1 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -1
- package/lib/AccessTokenClient.ts +59 -34
- package/lib/AccessTokenClientV1_0_11.ts +250 -0
- package/lib/AuthorizationCodeClient.ts +131 -28
- package/lib/AuthorizationCodeClientV1_0_11.ts +170 -0
- package/lib/CredentialOfferClient.ts +21 -8
- package/lib/CredentialOfferClientV1_0_11.ts +112 -0
- package/lib/CredentialOfferClientV1_0_13.ts +103 -0
- package/lib/CredentialRequestClient.ts +65 -26
- package/lib/CredentialRequestClientBuilder.ts +34 -16
- package/lib/CredentialRequestClientBuilderV1_0_11.ts +163 -0
- package/lib/CredentialRequestClientV1_0_11.ts +197 -0
- package/lib/MetadataClient.ts +64 -49
- package/lib/MetadataClientV1_0_11.ts +189 -0
- package/lib/MetadataClientV1_0_13.ts +188 -0
- package/lib/OpenID4VCIClient.ts +132 -68
- package/lib/OpenID4VCIClientV1_0_11.ts +635 -0
- package/lib/OpenID4VCIClientV1_0_13.ts +677 -0
- package/lib/ProofOfPossessionBuilder.ts +41 -11
- package/lib/__tests__/AccessTokenClient.spec.ts +40 -12
- package/lib/__tests__/AuthorizationDetailsBuilder.spec.ts +0 -12
- package/lib/__tests__/CredentialRequestClient.spec.ts +87 -50
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +18 -12
- package/lib/__tests__/CredentialRequestClientV1_0_11.spec.ts +317 -0
- package/lib/__tests__/EBSIE2E.spec.test.ts +2 -2
- package/lib/__tests__/HttpUtils.spec.ts +1 -1
- package/lib/__tests__/IT.spec.ts +264 -14
- package/lib/__tests__/IssuanceInitiation.spec.ts +59 -4
- package/lib/__tests__/IssuanceInitiationV1_0_11.spec.ts +62 -0
- package/lib/__tests__/MattrE2E.spec.test.ts +2 -2
- package/lib/__tests__/MetadataClient.spec.ts +53 -3
- package/lib/__tests__/MetadataMocks.ts +42 -2
- package/lib/__tests__/OpenID4VCIClient.spec.ts +58 -2
- package/lib/__tests__/{OpenID4VCIClientPAR.spec.ts → OpenID4VCIClientPARV1_0_11.spec.ts} +5 -5
- package/lib/__tests__/OpenID4VCIClientV1_0_11.spec.ts +226 -0
- package/lib/__tests__/OpenID4VCIClientV1_0_13.spec.ts +204 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +1 -1
- package/lib/__tests__/SdJwt.spec.ts +36 -30
- package/lib/__tests__/SphereonE2E.spec.test.ts +10 -7
- package/lib/__tests__/data/VciDataFixtures.ts +712 -27
- package/lib/functions/OpenIDUtils.ts +25 -0
- package/lib/functions/index.ts +2 -3
- package/lib/functions/notifications.ts +32 -0
- package/lib/index.ts +16 -1
- package/lib/types/index.ts +6 -0
- package/package.json +4 -4
- package/dist/functions/ProofUtil.d.ts +0 -30
- package/dist/functions/ProofUtil.d.ts.map +0 -1
- package/dist/functions/ProofUtil.js +0 -106
- package/dist/functions/ProofUtil.js.map +0 -1
- package/lib/functions/ProofUtil.ts +0 -128
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccessTokenResponse,
|
|
3
|
+
CredentialIssuerMetadata,
|
|
4
|
+
CredentialOfferPayloadV1_0_08,
|
|
5
|
+
CredentialOfferPayloadV1_0_11,
|
|
6
|
+
CredentialOfferRequestWithBaseUrl,
|
|
7
|
+
determineSpecVersionFromOffer,
|
|
8
|
+
EndpointMetadata,
|
|
9
|
+
ExperimentalSubjectIssuance,
|
|
10
|
+
getIssuerFromCredentialOfferPayload,
|
|
11
|
+
getTypesFromOfferV1_0_11,
|
|
12
|
+
OID4VCICredentialFormat,
|
|
13
|
+
OpenId4VCIVersion,
|
|
14
|
+
UniformCredentialOfferRequest,
|
|
15
|
+
} from '@sphereon/oid4vci-common';
|
|
16
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
17
|
+
|
|
18
|
+
import { CredentialOfferClientV1_0_11 } from './CredentialOfferClientV1_0_11';
|
|
19
|
+
import { CredentialRequestClientV1_0_11 } from './CredentialRequestClientV1_0_11';
|
|
20
|
+
|
|
21
|
+
export class CredentialRequestClientBuilderV1_0_11 {
|
|
22
|
+
credentialEndpoint?: string;
|
|
23
|
+
deferredCredentialEndpoint?: string;
|
|
24
|
+
deferredCredentialAwait = false;
|
|
25
|
+
deferredCredentialIntervalInMS = 5000;
|
|
26
|
+
credentialTypes: string[] = [];
|
|
27
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
28
|
+
token?: string;
|
|
29
|
+
version?: OpenId4VCIVersion;
|
|
30
|
+
subjectIssuance?: ExperimentalSubjectIssuance;
|
|
31
|
+
|
|
32
|
+
public static fromCredentialIssuer({
|
|
33
|
+
credentialIssuer,
|
|
34
|
+
metadata,
|
|
35
|
+
version,
|
|
36
|
+
credentialTypes,
|
|
37
|
+
}: {
|
|
38
|
+
credentialIssuer: string;
|
|
39
|
+
metadata?: EndpointMetadata;
|
|
40
|
+
version?: OpenId4VCIVersion;
|
|
41
|
+
credentialTypes: string | string[];
|
|
42
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
43
|
+
const issuer = credentialIssuer;
|
|
44
|
+
const builder = new CredentialRequestClientBuilderV1_0_11();
|
|
45
|
+
builder.withVersion(version ?? OpenId4VCIVersion.VER_1_0_11);
|
|
46
|
+
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
|
|
47
|
+
if (metadata?.deferred_credential_endpoint) {
|
|
48
|
+
builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
|
|
49
|
+
}
|
|
50
|
+
builder.withCredentialType(credentialTypes);
|
|
51
|
+
return builder;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise<CredentialRequestClientBuilderV1_0_11> {
|
|
55
|
+
const offer = await CredentialOfferClientV1_0_11.fromURI(uri);
|
|
56
|
+
return CredentialRequestClientBuilderV1_0_11.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public static fromCredentialOfferRequest(opts: {
|
|
60
|
+
request: UniformCredentialOfferRequest;
|
|
61
|
+
scheme?: string;
|
|
62
|
+
baseUrl?: string;
|
|
63
|
+
version?: OpenId4VCIVersion;
|
|
64
|
+
metadata?: EndpointMetadata;
|
|
65
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
66
|
+
const { request, metadata } = opts;
|
|
67
|
+
const version = opts.version ?? request.version ?? determineSpecVersionFromOffer(request.original_credential_offer);
|
|
68
|
+
const builder = new CredentialRequestClientBuilderV1_0_11();
|
|
69
|
+
const issuer = getIssuerFromCredentialOfferPayload(request.credential_offer) ?? (metadata?.issuer as string);
|
|
70
|
+
builder.withVersion(version);
|
|
71
|
+
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
|
|
72
|
+
if (metadata?.deferred_credential_endpoint) {
|
|
73
|
+
builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (version <= OpenId4VCIVersion.VER_1_0_08) {
|
|
77
|
+
//todo: This basically sets all types available during initiation. Probably the user only wants a subset. So do we want to do this?
|
|
78
|
+
builder.withCredentialType((request.original_credential_offer as CredentialOfferPayloadV1_0_08).credential_type);
|
|
79
|
+
} else if (version <= OpenId4VCIVersion.VER_1_0_11) {
|
|
80
|
+
// todo: look whether this is correct
|
|
81
|
+
builder.withCredentialType(getTypesFromOfferV1_0_11(request.credential_offer as CredentialOfferPayloadV1_0_11));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return builder;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public static fromCredentialOffer({
|
|
88
|
+
credentialOffer,
|
|
89
|
+
metadata,
|
|
90
|
+
}: {
|
|
91
|
+
credentialOffer: CredentialOfferRequestWithBaseUrl;
|
|
92
|
+
metadata?: EndpointMetadata;
|
|
93
|
+
}): CredentialRequestClientBuilderV1_0_11 {
|
|
94
|
+
return CredentialRequestClientBuilderV1_0_11.fromCredentialOfferRequest({
|
|
95
|
+
request: credentialOffer,
|
|
96
|
+
metadata,
|
|
97
|
+
version: credentialOffer.version,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
|
|
102
|
+
this.credentialEndpoint = metadata.credential_endpoint;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public withCredentialEndpoint(credentialEndpoint: string): this {
|
|
107
|
+
this.credentialEndpoint = credentialEndpoint;
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public withDeferredCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
|
|
112
|
+
this.deferredCredentialEndpoint = metadata.deferred_credential_endpoint;
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public withDeferredCredentialEndpoint(deferredCredentialEndpoint: string): this {
|
|
117
|
+
this.deferredCredentialEndpoint = deferredCredentialEndpoint;
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public withDeferredCredentialAwait(deferredCredentialAwait: boolean, deferredCredentialIntervalInMS?: number): this {
|
|
122
|
+
this.deferredCredentialAwait = deferredCredentialAwait;
|
|
123
|
+
this.deferredCredentialIntervalInMS = deferredCredentialIntervalInMS ?? 5000;
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public withCredentialType(credentialTypes: string | string[]): this {
|
|
128
|
+
this.credentialTypes = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public withFormat(format: CredentialFormat | OID4VCICredentialFormat): this {
|
|
133
|
+
this.format = format;
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public withSubjectIssuance(subjectIssuance: ExperimentalSubjectIssuance): this {
|
|
138
|
+
this.subjectIssuance = subjectIssuance;
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public withToken(accessToken: string): this {
|
|
143
|
+
this.token = accessToken;
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public withTokenFromResponse(response: AccessTokenResponse): this {
|
|
148
|
+
this.token = response.access_token;
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public withVersion(version: OpenId4VCIVersion): this {
|
|
153
|
+
this.version = version;
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public build(): CredentialRequestClientV1_0_11 {
|
|
158
|
+
if (!this.version) {
|
|
159
|
+
this.withVersion(OpenId4VCIVersion.VER_1_0_11);
|
|
160
|
+
}
|
|
161
|
+
return new CredentialRequestClientV1_0_11(this);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acquireDeferredCredential,
|
|
3
|
+
CredentialResponse,
|
|
4
|
+
getCredentialRequestForVersion,
|
|
5
|
+
getUniformFormat,
|
|
6
|
+
isDeferredCredentialResponse,
|
|
7
|
+
isValidURL,
|
|
8
|
+
JsonLdIssuerCredentialDefinition,
|
|
9
|
+
OID4VCICredentialFormat,
|
|
10
|
+
OpenId4VCIVersion,
|
|
11
|
+
OpenIDResponse,
|
|
12
|
+
post,
|
|
13
|
+
ProofOfPossession,
|
|
14
|
+
UniformCredentialRequest,
|
|
15
|
+
URL_NOT_VALID,
|
|
16
|
+
} from '@sphereon/oid4vci-common';
|
|
17
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
18
|
+
import Debug from 'debug';
|
|
19
|
+
|
|
20
|
+
import { buildProof } from './CredentialRequestClient';
|
|
21
|
+
import { CredentialRequestClientBuilderV1_0_11 } from './CredentialRequestClientBuilderV1_0_11';
|
|
22
|
+
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
23
|
+
|
|
24
|
+
const debug = Debug('sphereon:oid4vci:credential');
|
|
25
|
+
|
|
26
|
+
export interface CredentialRequestOptsV1_0_11 {
|
|
27
|
+
deferredCredentialAwait?: boolean;
|
|
28
|
+
deferredCredentialIntervalInMS?: number;
|
|
29
|
+
credentialEndpoint: string;
|
|
30
|
+
deferredCredentialEndpoint?: string;
|
|
31
|
+
credentialTypes: string[];
|
|
32
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
33
|
+
proof: ProofOfPossession;
|
|
34
|
+
token: string;
|
|
35
|
+
version: OpenId4VCIVersion;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class CredentialRequestClientV1_0_11 {
|
|
39
|
+
private readonly _credentialRequestOpts: Partial<CredentialRequestOptsV1_0_11>;
|
|
40
|
+
private _isDeferred = false;
|
|
41
|
+
|
|
42
|
+
get credentialRequestOpts(): CredentialRequestOptsV1_0_11 {
|
|
43
|
+
return this._credentialRequestOpts as CredentialRequestOptsV1_0_11;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public isDeferred(): boolean {
|
|
47
|
+
return this._isDeferred;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public getCredentialEndpoint(): string {
|
|
51
|
+
return this.credentialRequestOpts.credentialEndpoint;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public getDeferredCredentialEndpoint(): string | undefined {
|
|
55
|
+
return this.credentialRequestOpts.deferredCredentialEndpoint;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public constructor(builder: CredentialRequestClientBuilderV1_0_11) {
|
|
59
|
+
this._credentialRequestOpts = { ...builder };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async acquireCredentialsUsingProof<DIDDoc>(opts: {
|
|
63
|
+
proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
|
|
64
|
+
credentialTypes?: string | string[];
|
|
65
|
+
context?: string[];
|
|
66
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
67
|
+
}): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
|
|
68
|
+
const { credentialTypes, proofInput, format, context } = opts;
|
|
69
|
+
|
|
70
|
+
const request = await this.createCredentialRequest({ proofInput, credentialTypes, context, format, version: this.version() });
|
|
71
|
+
return await this.acquireCredentialsUsingRequest(request);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public async acquireCredentialsUsingRequest(
|
|
75
|
+
uniformRequest: UniformCredentialRequest,
|
|
76
|
+
): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
|
|
77
|
+
const request = getCredentialRequestForVersion(uniformRequest, this.version());
|
|
78
|
+
const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
|
|
79
|
+
if (!isValidURL(credentialEndpoint)) {
|
|
80
|
+
debug(`Invalid credential endpoint: ${credentialEndpoint}`);
|
|
81
|
+
throw new Error(URL_NOT_VALID);
|
|
82
|
+
}
|
|
83
|
+
debug(`Acquiring credential(s) from: ${credentialEndpoint}`);
|
|
84
|
+
debug(`request\n: ${JSON.stringify(request, null, 2)}`);
|
|
85
|
+
const requestToken: string = this.credentialRequestOpts.token;
|
|
86
|
+
let response = (await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken })) as OpenIDResponse<CredentialResponse> & {
|
|
87
|
+
access_token: string;
|
|
88
|
+
};
|
|
89
|
+
this._isDeferred = isDeferredCredentialResponse(response);
|
|
90
|
+
if (this.isDeferred() && this.credentialRequestOpts.deferredCredentialAwait && response.successBody) {
|
|
91
|
+
response = await this.acquireDeferredCredential(response.successBody, { bearerToken: this.credentialRequestOpts.token });
|
|
92
|
+
}
|
|
93
|
+
response.access_token = requestToken;
|
|
94
|
+
|
|
95
|
+
debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`);
|
|
96
|
+
return response;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public async acquireDeferredCredential(
|
|
100
|
+
response: Pick<CredentialResponse, 'transaction_id' | 'acceptance_token' | 'c_nonce'>,
|
|
101
|
+
opts?: {
|
|
102
|
+
bearerToken?: string;
|
|
103
|
+
},
|
|
104
|
+
): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
|
|
105
|
+
const transactionId = response.transaction_id;
|
|
106
|
+
const bearerToken = response.acceptance_token ?? opts?.bearerToken;
|
|
107
|
+
const deferredCredentialEndpoint = this.getDeferredCredentialEndpoint();
|
|
108
|
+
if (!deferredCredentialEndpoint) {
|
|
109
|
+
throw Error(`No deferred credential endpoint supplied.`);
|
|
110
|
+
} else if (!bearerToken) {
|
|
111
|
+
throw Error(`No bearer token present and refresh for defered endpoint not supported yet`);
|
|
112
|
+
// todo updated bearer token with new c_nonce
|
|
113
|
+
}
|
|
114
|
+
return await acquireDeferredCredential({
|
|
115
|
+
bearerToken,
|
|
116
|
+
transactionId,
|
|
117
|
+
deferredCredentialEndpoint,
|
|
118
|
+
deferredCredentialAwait: this.credentialRequestOpts.deferredCredentialAwait,
|
|
119
|
+
deferredCredentialIntervalInMS: this.credentialRequestOpts.deferredCredentialIntervalInMS,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public async createCredentialRequest<DIDDoc>(opts: {
|
|
124
|
+
proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
|
|
125
|
+
credentialTypes?: string | string[];
|
|
126
|
+
context?: string[];
|
|
127
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
128
|
+
version: OpenId4VCIVersion;
|
|
129
|
+
}): Promise<UniformCredentialRequest> {
|
|
130
|
+
const { proofInput } = opts;
|
|
131
|
+
const formatSelection = opts.format ?? this.credentialRequestOpts.format;
|
|
132
|
+
|
|
133
|
+
if (!formatSelection) {
|
|
134
|
+
throw Error(`Format of credential to be issued is missing`);
|
|
135
|
+
}
|
|
136
|
+
const format = getUniformFormat(formatSelection);
|
|
137
|
+
const typesSelection =
|
|
138
|
+
opts?.credentialTypes && (typeof opts.credentialTypes === 'string' || opts.credentialTypes.length > 0)
|
|
139
|
+
? opts.credentialTypes
|
|
140
|
+
: this.credentialRequestOpts.credentialTypes;
|
|
141
|
+
const types = Array.isArray(typesSelection) ? typesSelection : [typesSelection];
|
|
142
|
+
if (types.length === 0) {
|
|
143
|
+
throw Error(`Credential type(s) need to be provided`);
|
|
144
|
+
}
|
|
145
|
+
// FIXME: this is mixing up the type (as id) from v8/v9 and the types (from the vc.type) from v11
|
|
146
|
+
else if (!this.isV11OrHigher() && types.length !== 1) {
|
|
147
|
+
throw Error('Only a single credential type is supported for V8/V9');
|
|
148
|
+
}
|
|
149
|
+
const proof = await buildProof(proofInput, opts);
|
|
150
|
+
|
|
151
|
+
// TODO: we should move format specific logic
|
|
152
|
+
if (format === 'jwt_vc_json' || format === 'jwt_vc') {
|
|
153
|
+
return {
|
|
154
|
+
types,
|
|
155
|
+
format,
|
|
156
|
+
proof,
|
|
157
|
+
};
|
|
158
|
+
} else if (format === 'jwt_vc_json-ld' || format === 'ldp_vc') {
|
|
159
|
+
if (this.version() >= OpenId4VCIVersion.VER_1_0_12 && !opts.context) {
|
|
160
|
+
throw Error('No @context value present, but it is required');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
format,
|
|
165
|
+
proof,
|
|
166
|
+
|
|
167
|
+
// Ignored because v11 does not have the context value, but it is required in v12
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
credential_definition: {
|
|
171
|
+
types,
|
|
172
|
+
...(opts.context && { '@context': opts.context }),
|
|
173
|
+
} as JsonLdIssuerCredentialDefinition,
|
|
174
|
+
};
|
|
175
|
+
} else if (format === 'vc+sd-jwt') {
|
|
176
|
+
if (types.length > 1) {
|
|
177
|
+
throw Error(`Only a single credential type is supported for ${format}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
format,
|
|
182
|
+
proof,
|
|
183
|
+
vct: types[0],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private version(): OpenId4VCIVersion {
|
|
191
|
+
return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_11;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private isV11OrHigher(): boolean {
|
|
195
|
+
return this.version() >= OpenId4VCIVersion.VER_1_0_11;
|
|
196
|
+
}
|
|
197
|
+
}
|
package/lib/MetadataClient.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AuthorizationServerMetadata,
|
|
3
3
|
AuthorizationServerType,
|
|
4
|
-
|
|
4
|
+
CredentialIssuerMetadataV1_0_11,
|
|
5
|
+
CredentialIssuerMetadataV1_0_13,
|
|
5
6
|
CredentialOfferPayload,
|
|
7
|
+
CredentialOfferPayloadV1_0_13,
|
|
6
8
|
CredentialOfferRequestWithBaseUrl,
|
|
7
|
-
|
|
9
|
+
determineSpecVersionFromOffer,
|
|
10
|
+
EndpointMetadataResultV1_0_11,
|
|
11
|
+
EndpointMetadataResultV1_0_13,
|
|
8
12
|
getIssuerFromCredentialOfferPayload,
|
|
13
|
+
IssuerMetadataV1_0_08,
|
|
14
|
+
OpenId4VCIVersion,
|
|
9
15
|
OpenIDResponse,
|
|
10
16
|
WellKnownEndpoints,
|
|
11
17
|
} from '@sphereon/oid4vci-common';
|
|
12
18
|
import Debug from 'debug';
|
|
13
19
|
|
|
14
|
-
import {
|
|
20
|
+
import { MetadataClientV1_0_11 } from './MetadataClientV1_0_11';
|
|
21
|
+
import { MetadataClientV1_0_13 } from './MetadataClientV1_0_13';
|
|
22
|
+
import { retrieveWellknown } from './functions/OpenIDUtils';
|
|
15
23
|
|
|
16
24
|
const debug = Debug('sphereon:oid4vci:metadata');
|
|
17
25
|
|
|
@@ -21,18 +29,30 @@ export class MetadataClient {
|
|
|
21
29
|
*
|
|
22
30
|
* @param credentialOffer
|
|
23
31
|
*/
|
|
24
|
-
public static async retrieveAllMetadataFromCredentialOffer(
|
|
25
|
-
|
|
32
|
+
public static async retrieveAllMetadataFromCredentialOffer(
|
|
33
|
+
credentialOffer: CredentialOfferRequestWithBaseUrl,
|
|
34
|
+
): Promise<EndpointMetadataResultV1_0_13 | EndpointMetadataResultV1_0_11> {
|
|
35
|
+
if (determineSpecVersionFromOffer(credentialOffer.credential_offer) >= OpenId4VCIVersion.VER_1_0_13) {
|
|
36
|
+
return await MetadataClientV1_0_13.retrieveAllMetadataFromCredentialOffer(credentialOffer);
|
|
37
|
+
} else {
|
|
38
|
+
return await MetadataClientV1_0_11.retrieveAllMetadataFromCredentialOffer(credentialOffer);
|
|
39
|
+
}
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
/**
|
|
29
43
|
* Retrieve the metada using the initiation request obtained from a previous step
|
|
30
44
|
* @param request
|
|
31
45
|
*/
|
|
32
|
-
public static async retrieveAllMetadataFromCredentialOfferRequest(
|
|
46
|
+
public static async retrieveAllMetadataFromCredentialOfferRequest(
|
|
47
|
+
request: CredentialOfferPayload,
|
|
48
|
+
): Promise<EndpointMetadataResultV1_0_13 | EndpointMetadataResultV1_0_11> {
|
|
33
49
|
const issuer = getIssuerFromCredentialOfferPayload(request);
|
|
34
50
|
if (issuer) {
|
|
35
|
-
|
|
51
|
+
if (determineSpecVersionFromOffer(request) >= OpenId4VCIVersion.VER_1_0_13) {
|
|
52
|
+
return MetadataClientV1_0_13.retrieveAllMetadataFromCredentialOfferRequest(request as CredentialOfferPayloadV1_0_13);
|
|
53
|
+
} else {
|
|
54
|
+
return MetadataClientV1_0_11.retrieveAllMetadataFromCredentialOfferRequest(request);
|
|
55
|
+
}
|
|
36
56
|
}
|
|
37
57
|
throw new Error("can't retrieve metadata from CredentialOfferRequest. No issuer field is present");
|
|
38
58
|
}
|
|
@@ -42,32 +62,39 @@ export class MetadataClient {
|
|
|
42
62
|
* @param issuer The issuer URL
|
|
43
63
|
* @param opts
|
|
44
64
|
*/
|
|
45
|
-
public static async retrieveAllMetadata(
|
|
65
|
+
public static async retrieveAllMetadata(
|
|
66
|
+
issuer: string,
|
|
67
|
+
opts?: { errorOnNotFound: boolean },
|
|
68
|
+
): Promise<EndpointMetadataResultV1_0_13 | EndpointMetadataResultV1_0_11> {
|
|
46
69
|
let token_endpoint: string | undefined;
|
|
47
70
|
let credential_endpoint: string | undefined;
|
|
48
71
|
let deferred_credential_endpoint: string | undefined;
|
|
49
72
|
let authorization_endpoint: string | undefined;
|
|
50
73
|
let authorizationServerType: AuthorizationServerType = 'OID4VCI';
|
|
51
|
-
let
|
|
74
|
+
let authorization_servers: string[] | undefined = [issuer];
|
|
75
|
+
let authorization_server: string | undefined = undefined;
|
|
52
76
|
const oid4vciResponse = await MetadataClient.retrieveOpenID4VCIServerMetadata(issuer, { errorOnNotFound: false }); // We will handle errors later, given we will also try other metadata locations
|
|
53
77
|
let credentialIssuerMetadata = oid4vciResponse?.successBody;
|
|
54
78
|
if (credentialIssuerMetadata) {
|
|
55
79
|
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${JSON.stringify(credentialIssuerMetadata)}`);
|
|
56
80
|
credential_endpoint = credentialIssuerMetadata.credential_endpoint;
|
|
57
|
-
deferred_credential_endpoint = credentialIssuerMetadata.deferred_credential_endpoint
|
|
81
|
+
deferred_credential_endpoint = credentialIssuerMetadata.deferred_credential_endpoint
|
|
82
|
+
? (credentialIssuerMetadata.deferred_credential_endpoint as string)
|
|
83
|
+
: undefined;
|
|
58
84
|
if (credentialIssuerMetadata.token_endpoint) {
|
|
59
85
|
token_endpoint = credentialIssuerMetadata.token_endpoint;
|
|
60
86
|
}
|
|
61
|
-
if (credentialIssuerMetadata.
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
if (credentialIssuerMetadata.authorization_servers) {
|
|
88
|
+
authorization_servers = credentialIssuerMetadata.authorization_servers as string[];
|
|
89
|
+
} else if (credentialIssuerMetadata.authorization_server) {
|
|
90
|
+
authorization_server = credentialIssuerMetadata.authorization_server as string;
|
|
91
|
+
authorization_servers = [authorization_server];
|
|
66
92
|
}
|
|
67
93
|
}
|
|
68
94
|
// No specific OID4VCI endpoint. Either can be an OAuth2 AS or an OIDC IDP. Let's start with OIDC first
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
// TODO: for now we're taking just the first one
|
|
96
|
+
let response: OpenIDResponse<AuthorizationServerMetadata> = await retrieveWellknown(
|
|
97
|
+
authorization_servers[0],
|
|
71
98
|
WellKnownEndpoints.OPENID_CONFIGURATION,
|
|
72
99
|
{
|
|
73
100
|
errorOnNotFound: false,
|
|
@@ -79,13 +106,14 @@ export class MetadataClient {
|
|
|
79
106
|
authorizationServerType = 'OIDC';
|
|
80
107
|
} else {
|
|
81
108
|
// Now let's do OAuth2
|
|
82
|
-
|
|
109
|
+
// TODO: for now we're taking just the first one
|
|
110
|
+
response = await retrieveWellknown(authorization_servers[0], WellKnownEndpoints.OAUTH_AS, { errorOnNotFound: false });
|
|
83
111
|
authMetadata = response.successBody;
|
|
84
112
|
}
|
|
85
113
|
if (!authMetadata) {
|
|
86
114
|
// We will always throw an error, no matter whether the user provided the option not to, because this is bad.
|
|
87
|
-
if (issuer
|
|
88
|
-
throw Error(`Issuer ${issuer} provided a separate authorization server ${
|
|
115
|
+
if (!authorization_servers.includes(issuer)) {
|
|
116
|
+
throw Error(`Issuer ${issuer} provided a separate authorization server ${authorization_servers}, but that server did not provide metadata`);
|
|
89
117
|
}
|
|
90
118
|
} else {
|
|
91
119
|
if (!authorizationServerType) {
|
|
@@ -103,7 +131,7 @@ export class MetadataClient {
|
|
|
103
131
|
}
|
|
104
132
|
authorization_endpoint = authMetadata.authorization_endpoint;
|
|
105
133
|
if (!authMetadata.token_endpoint) {
|
|
106
|
-
throw Error(`Authorization Sever ${
|
|
134
|
+
throw Error(`Authorization Sever ${authorization_servers} did not provide a token_endpoint`);
|
|
107
135
|
} else if (token_endpoint && authMetadata.token_endpoint !== token_endpoint) {
|
|
108
136
|
throw Error(
|
|
109
137
|
`Credential issuer has a different token_endpoint (${token_endpoint}) from the Authorization Server (${authMetadata.token_endpoint})`,
|
|
@@ -152,7 +180,9 @@ export class MetadataClient {
|
|
|
152
180
|
|
|
153
181
|
if (!credentialIssuerMetadata && authMetadata) {
|
|
154
182
|
// Apparently everything worked out and the issuer is exposing everything in oAuth2/OIDC well-knowns. Spec is vague about this situation, but we can support it
|
|
155
|
-
credentialIssuerMetadata =
|
|
183
|
+
credentialIssuerMetadata = authorization_server
|
|
184
|
+
? (authMetadata as CredentialIssuerMetadataV1_0_11)
|
|
185
|
+
: (authMetadata as CredentialIssuerMetadataV1_0_13);
|
|
156
186
|
}
|
|
157
187
|
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);
|
|
158
188
|
return {
|
|
@@ -160,12 +190,14 @@ export class MetadataClient {
|
|
|
160
190
|
token_endpoint,
|
|
161
191
|
credential_endpoint,
|
|
162
192
|
deferred_credential_endpoint,
|
|
163
|
-
authorization_server,
|
|
193
|
+
...(authorization_server ? { authorization_server } : { authorization_servers: authorization_servers }),
|
|
164
194
|
authorization_endpoint,
|
|
165
195
|
authorizationServerType,
|
|
166
|
-
credentialIssuerMetadata:
|
|
196
|
+
credentialIssuerMetadata: authorization_server
|
|
197
|
+
? (credentialIssuerMetadata as IssuerMetadataV1_0_08 & Partial<AuthorizationServerMetadata>)
|
|
198
|
+
: (credentialIssuerMetadata as CredentialIssuerMetadataV1_0_13),
|
|
167
199
|
authorizationServerMetadata: authMetadata,
|
|
168
|
-
};
|
|
200
|
+
} as EndpointMetadataResultV1_0_13 | EndpointMetadataResultV1_0_11;
|
|
169
201
|
}
|
|
170
202
|
|
|
171
203
|
/**
|
|
@@ -178,31 +210,14 @@ export class MetadataClient {
|
|
|
178
210
|
opts?: {
|
|
179
211
|
errorOnNotFound?: boolean;
|
|
180
212
|
},
|
|
181
|
-
): Promise<
|
|
182
|
-
|
|
213
|
+
): Promise<
|
|
214
|
+
| OpenIDResponse<
|
|
215
|
+
CredentialIssuerMetadataV1_0_11 | CredentialIssuerMetadataV1_0_13 | (IssuerMetadataV1_0_08 & Partial<AuthorizationServerMetadata>)
|
|
216
|
+
>
|
|
217
|
+
| undefined
|
|
218
|
+
> {
|
|
219
|
+
return retrieveWellknown(issuerHost, WellKnownEndpoints.OPENID4VCI_ISSUER, {
|
|
183
220
|
errorOnNotFound: opts?.errorOnNotFound === undefined ? true : opts.errorOnNotFound,
|
|
184
221
|
});
|
|
185
222
|
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Allows to retrieve information from a well-known location
|
|
189
|
-
*
|
|
190
|
-
* @param host The host
|
|
191
|
-
* @param endpointType The endpoint type, currently supports OID4VCI, OIDC and OAuth2 endpoint types
|
|
192
|
-
* @param opts Options, like for instance whether an error should be thrown in case the endpoint doesn't exist
|
|
193
|
-
*/
|
|
194
|
-
public static async retrieveWellknown<T>(
|
|
195
|
-
host: string,
|
|
196
|
-
endpointType: WellKnownEndpoints,
|
|
197
|
-
opts?: { errorOnNotFound?: boolean },
|
|
198
|
-
): Promise<OpenIDResponse<T>> {
|
|
199
|
-
const result: OpenIDResponse<T> = await getJson(`${host.endsWith('/') ? host.slice(0, -1) : host}${endpointType}`, {
|
|
200
|
-
exceptionOnHttpErrorStatus: opts?.errorOnNotFound,
|
|
201
|
-
});
|
|
202
|
-
if (result.origResponse.status >= 400) {
|
|
203
|
-
// We only get here when error on not found is false
|
|
204
|
-
debug(`host ${host} with endpoint type ${endpointType} status: ${result.origResponse.status}, ${result.origResponse.statusText}`);
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
223
|
}
|