@sphereon/oid4vci-client 0.8.2-next.6 → 0.8.2-next.88

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.
Files changed (72) hide show
  1. package/README.md +9 -8
  2. package/dist/AccessTokenClient.d.ts +0 -1
  3. package/dist/AccessTokenClient.d.ts.map +1 -1
  4. package/dist/AccessTokenClient.js +11 -18
  5. package/dist/AccessTokenClient.js.map +1 -1
  6. package/dist/AuthorizationCodeClient.d.ts +9 -0
  7. package/dist/AuthorizationCodeClient.d.ts.map +1 -0
  8. package/dist/AuthorizationCodeClient.js +132 -0
  9. package/dist/AuthorizationCodeClient.js.map +1 -0
  10. package/dist/AuthorizationDetailsBuilder.d.ts.map +1 -1
  11. package/dist/AuthorizationDetailsBuilder.js.map +1 -1
  12. package/dist/CredentialOfferClient.d.ts.map +1 -1
  13. package/dist/CredentialOfferClient.js +3 -1
  14. package/dist/CredentialOfferClient.js.map +1 -1
  15. package/dist/CredentialRequestClient.d.ts +15 -0
  16. package/dist/CredentialRequestClient.d.ts.map +1 -1
  17. package/dist/CredentialRequestClient.js +91 -43
  18. package/dist/CredentialRequestClient.js.map +1 -1
  19. package/dist/CredentialRequestClientBuilder.d.ts +19 -7
  20. package/dist/CredentialRequestClientBuilder.d.ts.map +1 -1
  21. package/dist/CredentialRequestClientBuilder.js +31 -1
  22. package/dist/CredentialRequestClientBuilder.js.map +1 -1
  23. package/dist/MetadataClient.d.ts.map +1 -1
  24. package/dist/MetadataClient.js +12 -1
  25. package/dist/MetadataClient.js.map +1 -1
  26. package/dist/OpenID4VCIClient.d.ts +62 -27
  27. package/dist/OpenID4VCIClient.d.ts.map +1 -1
  28. package/dist/OpenID4VCIClient.js +255 -176
  29. package/dist/OpenID4VCIClient.js.map +1 -1
  30. package/dist/ProofOfPossessionBuilder.d.ts +3 -1
  31. package/dist/ProofOfPossessionBuilder.d.ts.map +1 -1
  32. package/dist/ProofOfPossessionBuilder.js +5 -0
  33. package/dist/ProofOfPossessionBuilder.js.map +1 -1
  34. package/dist/functions/AuthorizationUtil.d.ts +3 -0
  35. package/dist/functions/AuthorizationUtil.d.ts.map +1 -0
  36. package/dist/functions/AuthorizationUtil.js +22 -0
  37. package/dist/functions/AuthorizationUtil.js.map +1 -0
  38. package/dist/functions/ProofUtil.d.ts +2 -1
  39. package/dist/functions/ProofUtil.d.ts.map +1 -1
  40. package/dist/functions/ProofUtil.js +6 -4
  41. package/dist/functions/ProofUtil.js.map +1 -1
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/types/index.js +2 -0
  49. package/dist/types/index.js.map +1 -0
  50. package/lib/AccessTokenClient.ts +16 -20
  51. package/lib/AuthorizationCodeClient.ts +163 -0
  52. package/lib/AuthorizationDetailsBuilder.ts +2 -2
  53. package/lib/CredentialOfferClient.ts +4 -1
  54. package/lib/CredentialRequestClient.ts +116 -45
  55. package/lib/CredentialRequestClientBuilder.ts +53 -8
  56. package/lib/MetadataClient.ts +13 -1
  57. package/lib/OpenID4VCIClient.ts +348 -216
  58. package/lib/ProofOfPossessionBuilder.ts +8 -0
  59. package/lib/__tests__/AccessTokenClient.spec.ts +2 -0
  60. package/lib/__tests__/CredentialRequestClient.spec.ts +28 -8
  61. package/lib/__tests__/EBSIE2E.spec.test.ts +145 -0
  62. package/lib/__tests__/MetadataClient.spec.ts +4 -1
  63. package/lib/__tests__/OpenID4VCIClient.spec.ts +117 -76
  64. package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +59 -49
  65. package/lib/__tests__/SdJwt.spec.ts +163 -0
  66. package/lib/__tests__/SphereonE2E.spec.test.ts +2 -2
  67. package/lib/__tests__/data/VciDataFixtures.ts +14 -13
  68. package/lib/functions/AuthorizationUtil.ts +18 -0
  69. package/lib/functions/ProofUtil.ts +18 -4
  70. package/lib/index.ts +1 -0
  71. package/lib/types/index.ts +0 -0
  72. package/package.json +8 -6
@@ -1,6 +1,9 @@
1
1
  import {
2
- CredentialRequestV1_0_08,
2
+ acquireDeferredCredential,
3
3
  CredentialResponse,
4
+ getCredentialRequestForVersion,
5
+ getUniformFormat,
6
+ isDeferredCredentialResponse,
4
7
  OID4VCICredentialFormat,
5
8
  OpenId4VCIVersion,
6
9
  OpenIDResponse,
@@ -18,7 +21,10 @@ import { isValidURL, post } from './functions';
18
21
  const debug = Debug('sphereon:oid4vci:credential');
19
22
 
20
23
  export interface CredentialRequestOpts {
24
+ deferredCredentialAwait?: boolean;
25
+ deferredCredentialIntervalInMS?: number;
21
26
  credentialEndpoint: string;
27
+ deferredCredentialEndpoint?: string;
22
28
  credentialTypes: string[];
23
29
  format?: CredentialFormat | OID4VCICredentialFormat;
24
30
  proof: ProofOfPossession;
@@ -26,17 +32,45 @@ export interface CredentialRequestOpts {
26
32
  version: OpenId4VCIVersion;
27
33
  }
28
34
 
35
+ export async function buildProof<DIDDoc>(
36
+ proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession,
37
+ opts: {
38
+ version: OpenId4VCIVersion;
39
+ cNonce?: string;
40
+ },
41
+ ) {
42
+ if ('proof_type' in proofInput) {
43
+ if (opts.cNonce) {
44
+ throw Error(`Cnonce param is only supported when using a Proof of Posession builder`);
45
+ }
46
+ return await ProofOfPossessionBuilder.fromProof(proofInput as ProofOfPossession, opts.version).build();
47
+ }
48
+ if (opts.cNonce) {
49
+ proofInput.withAccessTokenNonce(opts.cNonce);
50
+ }
51
+ return await proofInput.build();
52
+ }
53
+
29
54
  export class CredentialRequestClient {
30
55
  private readonly _credentialRequestOpts: Partial<CredentialRequestOpts>;
56
+ private _isDeferred = false;
31
57
 
32
58
  get credentialRequestOpts(): CredentialRequestOpts {
33
59
  return this._credentialRequestOpts as CredentialRequestOpts;
34
60
  }
35
61
 
62
+ public isDeferred(): boolean {
63
+ return this._isDeferred;
64
+ }
65
+
36
66
  public getCredentialEndpoint(): string {
37
67
  return this.credentialRequestOpts.credentialEndpoint;
38
68
  }
39
69
 
70
+ public getDeferredCredentialEndpoint(): string | undefined {
71
+ return this.credentialRequestOpts.deferredCredentialEndpoint;
72
+ }
73
+
40
74
  public constructor(builder: CredentialRequestClientBuilder) {
41
75
  this._credentialRequestOpts = { ...builder };
42
76
  }
@@ -44,68 +78,73 @@ export class CredentialRequestClient {
44
78
  public async acquireCredentialsUsingProof<DIDDoc>(opts: {
45
79
  proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
46
80
  credentialTypes?: string | string[];
81
+ context?: string[];
47
82
  format?: CredentialFormat | OID4VCICredentialFormat;
48
83
  }): Promise<OpenIDResponse<CredentialResponse>> {
49
- const { credentialTypes, proofInput, format } = opts;
84
+ const { credentialTypes, proofInput, format, context } = opts;
50
85
 
51
- const request = await this.createCredentialRequest({ proofInput, credentialTypes, format, version: this.version() });
86
+ const request = await this.createCredentialRequest({ proofInput, credentialTypes, context, format, version: this.version() });
52
87
  return await this.acquireCredentialsUsingRequest(request);
53
88
  }
54
89
 
55
90
  public async acquireCredentialsUsingRequest(uniformRequest: UniformCredentialRequest): Promise<OpenIDResponse<CredentialResponse>> {
56
- let request: CredentialRequestV1_0_08 | UniformCredentialRequest = uniformRequest;
57
- if (!this.isV11OrHigher()) {
58
- let format: string = uniformRequest.format;
59
- if (format === 'jwt_vc_json') {
60
- format = 'jwt_vc';
61
- } else if (format === 'jwt_vc_json-ld') {
62
- format = 'ldp_vc';
63
- }
64
-
65
- request = {
66
- format,
67
- proof: uniformRequest.proof,
68
- type:
69
- 'types' in uniformRequest
70
- ? uniformRequest.types.filter((t) => t !== 'VerifiableCredential')[0]
71
- : uniformRequest.credential_definition.types[0],
72
- } as CredentialRequestV1_0_08;
73
- }
91
+ const request = getCredentialRequestForVersion(uniformRequest, this.version());
74
92
  const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
75
93
  if (!isValidURL(credentialEndpoint)) {
76
94
  debug(`Invalid credential endpoint: ${credentialEndpoint}`);
77
95
  throw new Error(URL_NOT_VALID);
78
96
  }
79
97
  debug(`Acquiring credential(s) from: ${credentialEndpoint}`);
98
+ debug(`request\n: ${JSON.stringify(request, null, 2)}`);
80
99
  const requestToken: string = this.credentialRequestOpts.token;
81
- const response: OpenIDResponse<CredentialResponse> = await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken });
82
- debug(`Credential endpoint ${credentialEndpoint} response:\r\n${response}`);
100
+ let response: OpenIDResponse<CredentialResponse> = await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken });
101
+ this._isDeferred = isDeferredCredentialResponse(response);
102
+ if (this.isDeferred() && this.credentialRequestOpts.deferredCredentialAwait && response.successBody) {
103
+ response = await this.acquireDeferredCredential(response.successBody, { bearerToken: this.credentialRequestOpts.token });
104
+ }
105
+
106
+ debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`);
83
107
  return response;
84
108
  }
85
109
 
110
+ public async acquireDeferredCredential(
111
+ response: Pick<CredentialResponse, 'transaction_id' | 'acceptance_token' | 'c_nonce'>,
112
+ opts?: {
113
+ bearerToken?: string;
114
+ },
115
+ ): Promise<OpenIDResponse<CredentialResponse>> {
116
+ const transactionId = response.transaction_id;
117
+ const bearerToken = response.acceptance_token ?? opts?.bearerToken;
118
+ const deferredCredentialEndpoint = this.getDeferredCredentialEndpoint();
119
+ if (!deferredCredentialEndpoint) {
120
+ throw Error(`No deferred credential endpoint supplied.`);
121
+ } else if (!bearerToken) {
122
+ throw Error(`No bearer token present and refresh for defered endpoint not supported yet`);
123
+ // todo updated bearer token with new c_nonce
124
+ }
125
+ return await acquireDeferredCredential({
126
+ bearerToken,
127
+ transactionId,
128
+ deferredCredentialEndpoint,
129
+ deferredCredentialAwait: this.credentialRequestOpts.deferredCredentialAwait,
130
+ deferredCredentialIntervalInMS: this.credentialRequestOpts.deferredCredentialIntervalInMS,
131
+ });
132
+ }
133
+
86
134
  public async createCredentialRequest<DIDDoc>(opts: {
87
135
  proofInput: ProofOfPossessionBuilder<DIDDoc> | ProofOfPossession;
88
136
  credentialTypes?: string | string[];
137
+ context?: string[];
89
138
  format?: CredentialFormat | OID4VCICredentialFormat;
90
139
  version: OpenId4VCIVersion;
91
140
  }): Promise<UniformCredentialRequest> {
92
141
  const { proofInput } = opts;
93
142
  const formatSelection = opts.format ?? this.credentialRequestOpts.format;
94
143
 
95
- let format: OID4VCICredentialFormat = formatSelection as OID4VCICredentialFormat;
96
- if (opts.version < OpenId4VCIVersion.VER_1_0_11) {
97
- if (formatSelection === 'jwt_vc' || formatSelection === 'jwt') {
98
- format = 'jwt_vc_json';
99
- } else if (formatSelection === 'ldp_vc' || formatSelection === 'ldp') {
100
- format = 'jwt_vc_json-ld';
101
- }
102
- }
103
-
104
- if (!format) {
144
+ if (!formatSelection) {
105
145
  throw Error(`Format of credential to be issued is missing`);
106
- } else if (format !== 'jwt_vc_json-ld' && format !== 'jwt_vc_json' && format !== 'ldp_vc') {
107
- throw Error(`Invalid format of credential to be issued: ${format}`);
108
146
  }
147
+ const format = getUniformFormat(formatSelection);
109
148
  const typesSelection =
110
149
  opts?.credentialTypes && (typeof opts.credentialTypes === 'string' || opts.credentialTypes.length > 0)
111
150
  ? opts.credentialTypes
@@ -113,24 +152,56 @@ export class CredentialRequestClient {
113
152
  const types = Array.isArray(typesSelection) ? typesSelection : [typesSelection];
114
153
  if (types.length === 0) {
115
154
  throw Error(`Credential type(s) need to be provided`);
116
- } else if (!this.isV11OrHigher() && types.length !== 1) {
155
+ }
156
+ // FIXME: this is mixing up the type (as id) from v8/v9 and the types (from the vc.type) from v11
157
+ else if (!this.isV11OrHigher() && types.length !== 1) {
117
158
  throw Error('Only a single credential type is supported for V8/V9');
118
159
  }
160
+ const proof = await buildProof(proofInput, opts);
161
+
162
+ // TODO: we should move format specific logic
163
+ if (format === 'jwt_vc_json' || format === 'jwt_vc') {
164
+ return {
165
+ types,
166
+ format,
167
+ proof,
168
+ };
169
+ } else if (format === 'jwt_vc_json-ld' || format === 'ldp_vc') {
170
+ if (this.version() >= OpenId4VCIVersion.VER_1_0_12 && !opts.context) {
171
+ throw Error('No @context value present, but it is required');
172
+ }
173
+
174
+ return {
175
+ format,
176
+ proof,
177
+
178
+ // Ignored because v11 does not have the context value, but it is required in v12
179
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
180
+ // @ts-ignore
181
+ credential_definition: {
182
+ types,
183
+ ...(opts.context && { '@context': opts.context }),
184
+ },
185
+ };
186
+ } else if (format === 'vc+sd-jwt') {
187
+ if (types.length > 1) {
188
+ throw Error(`Only a single credential type is supported for ${format}`);
189
+ }
190
+
191
+ return {
192
+ format,
193
+ proof,
194
+ vct: types[0],
195
+ };
196
+ }
119
197
 
120
- const proof =
121
- 'proof_type' in proofInput
122
- ? await ProofOfPossessionBuilder.fromProof(proofInput as ProofOfPossession, opts.version).build()
123
- : await proofInput.build();
124
- return {
125
- types,
126
- format,
127
- proof,
128
- } as UniformCredentialRequest;
198
+ throw new Error(`Unsupported format: ${format}`);
129
199
  }
130
200
 
131
201
  private version(): OpenId4VCIVersion {
132
202
  return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_11;
133
203
  }
204
+
134
205
  private isV11OrHigher(): boolean {
135
206
  return this.version() >= OpenId4VCIVersion.VER_1_0_11;
136
207
  }
@@ -6,6 +6,7 @@ import {
6
6
  determineSpecVersionFromOffer,
7
7
  EndpointMetadata,
8
8
  getIssuerFromCredentialOfferPayload,
9
+ getTypesFromOffer,
9
10
  OID4VCICredentialFormat,
10
11
  OpenId4VCIVersion,
11
12
  UniformCredentialOfferRequest,
@@ -17,11 +18,36 @@ import { CredentialRequestClient } from './CredentialRequestClient';
17
18
 
18
19
  export class CredentialRequestClientBuilder {
19
20
  credentialEndpoint?: string;
21
+ deferredCredentialEndpoint?: string;
22
+ deferredCredentialAwait = false;
23
+ deferredCredentialIntervalInMS = 5000;
20
24
  credentialTypes: string[] = [];
21
25
  format?: CredentialFormat | OID4VCICredentialFormat;
22
26
  token?: string;
23
27
  version?: OpenId4VCIVersion;
24
28
 
29
+ public static fromCredentialIssuer({
30
+ credentialIssuer,
31
+ metadata,
32
+ version,
33
+ credentialTypes,
34
+ }: {
35
+ credentialIssuer: string;
36
+ metadata?: EndpointMetadata;
37
+ version?: OpenId4VCIVersion;
38
+ credentialTypes: string | string[];
39
+ }): CredentialRequestClientBuilder {
40
+ const issuer = credentialIssuer;
41
+ const builder = new CredentialRequestClientBuilder();
42
+ builder.withVersion(version ?? OpenId4VCIVersion.VER_1_0_11);
43
+ builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
44
+ if (metadata?.deferred_credential_endpoint) {
45
+ builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
46
+ }
47
+ builder.withCredentialType(credentialTypes);
48
+ return builder;
49
+ }
50
+
25
51
  public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise<CredentialRequestClientBuilder> {
26
52
  const offer = await CredentialOfferClient.fromURI(uri);
27
53
  return CredentialRequestClientBuilder.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version });
@@ -40,13 +66,16 @@ export class CredentialRequestClientBuilder {
40
66
  const issuer = getIssuerFromCredentialOfferPayload(request.credential_offer) ?? (metadata?.issuer as string);
41
67
  builder.withVersion(version);
42
68
  builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
69
+ if (metadata?.deferred_credential_endpoint) {
70
+ builder.withDeferredCredentialEndpoint(metadata.deferred_credential_endpoint);
71
+ }
43
72
 
44
73
  if (version <= OpenId4VCIVersion.VER_1_0_08) {
45
74
  //todo: This basically sets all types available during initiation. Probably the user only wants a subset. So do we want to do this?
46
75
  builder.withCredentialType((request.original_credential_offer as CredentialOfferPayloadV1_0_08).credential_type);
47
76
  } else {
48
77
  // todo: look whether this is correct
49
- builder.withCredentialType(request.credential_offer.credentials.flatMap((c) => (typeof c === 'string' ? c : c.types)));
78
+ builder.withCredentialType(getTypesFromOffer(request.credential_offer));
50
79
  }
51
80
 
52
81
  return builder;
@@ -66,37 +95,53 @@ export class CredentialRequestClientBuilder {
66
95
  });
67
96
  }
68
97
 
69
- public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): CredentialRequestClientBuilder {
98
+ public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
70
99
  this.credentialEndpoint = metadata.credential_endpoint;
71
100
  return this;
72
101
  }
73
102
 
74
- public withCredentialEndpoint(credentialEndpoint: string): CredentialRequestClientBuilder {
103
+ public withCredentialEndpoint(credentialEndpoint: string): this {
75
104
  this.credentialEndpoint = credentialEndpoint;
76
105
  return this;
77
106
  }
78
107
 
79
- public withCredentialType(credentialTypes: string | string[]): CredentialRequestClientBuilder {
108
+ public withDeferredCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): this {
109
+ this.deferredCredentialEndpoint = metadata.deferred_credential_endpoint;
110
+ return this;
111
+ }
112
+
113
+ public withDeferredCredentialEndpoint(deferredCredentialEndpoint: string): this {
114
+ this.deferredCredentialEndpoint = deferredCredentialEndpoint;
115
+ return this;
116
+ }
117
+
118
+ public withDeferredCredentialAwait(deferredCredentialAwait: boolean, deferredCredentialIntervalInMS?: number): this {
119
+ this.deferredCredentialAwait = deferredCredentialAwait;
120
+ this.deferredCredentialIntervalInMS = deferredCredentialIntervalInMS ?? 5000;
121
+ return this;
122
+ }
123
+
124
+ public withCredentialType(credentialTypes: string | string[]): this {
80
125
  this.credentialTypes = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
81
126
  return this;
82
127
  }
83
128
 
84
- public withFormat(format: CredentialFormat | OID4VCICredentialFormat): CredentialRequestClientBuilder {
129
+ public withFormat(format: CredentialFormat | OID4VCICredentialFormat): this {
85
130
  this.format = format;
86
131
  return this;
87
132
  }
88
133
 
89
- public withToken(accessToken: string): CredentialRequestClientBuilder {
134
+ public withToken(accessToken: string): this {
90
135
  this.token = accessToken;
91
136
  return this;
92
137
  }
93
138
 
94
- public withTokenFromResponse(response: AccessTokenResponse): CredentialRequestClientBuilder {
139
+ public withTokenFromResponse(response: AccessTokenResponse): this {
95
140
  this.token = response.access_token;
96
141
  return this;
97
142
  }
98
143
 
99
- public withVersion(version: OpenId4VCIVersion): CredentialRequestClientBuilder {
144
+ public withVersion(version: OpenId4VCIVersion): this {
100
145
  this.version = version;
101
146
  return this;
102
147
  }
@@ -45,6 +45,7 @@ export class MetadataClient {
45
45
  public static async retrieveAllMetadata(issuer: string, opts?: { errorOnNotFound: boolean }): Promise<EndpointMetadataResult> {
46
46
  let token_endpoint: string | undefined;
47
47
  let credential_endpoint: string | undefined;
48
+ let deferred_credential_endpoint: string | undefined;
48
49
  let authorization_endpoint: string | undefined;
49
50
  let authorizationServerType: AuthorizationServerType = 'OID4VCI';
50
51
  let authorization_server: string = issuer;
@@ -53,6 +54,7 @@ export class MetadataClient {
53
54
  if (credentialIssuerMetadata) {
54
55
  debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${JSON.stringify(credentialIssuerMetadata)}`);
55
56
  credential_endpoint = credentialIssuerMetadata.credential_endpoint;
57
+ deferred_credential_endpoint = credentialIssuerMetadata.deferred_credential_endpoint;
56
58
  if (credentialIssuerMetadata.token_endpoint) {
57
59
  token_endpoint = credentialIssuerMetadata.token_endpoint;
58
60
  }
@@ -111,12 +113,21 @@ export class MetadataClient {
111
113
  if (authMetadata.credential_endpoint) {
112
114
  if (credential_endpoint && authMetadata.credential_endpoint !== credential_endpoint) {
113
115
  debug(
114
- `Credential issuer has a different credential_endpoint (${credential_endpoint}) from the Authorization Server (${authMetadata.token_endpoint}). Will use the issuer value`,
116
+ `Credential issuer has a different credential_endpoint (${credential_endpoint}) from the Authorization Server (${authMetadata.credential_endpoint}). Will use the issuer value`,
115
117
  );
116
118
  } else {
117
119
  credential_endpoint = authMetadata.credential_endpoint;
118
120
  }
119
121
  }
122
+ if (authMetadata.deferred_credential_endpoint) {
123
+ if (deferred_credential_endpoint && authMetadata.deferred_credential_endpoint !== deferred_credential_endpoint) {
124
+ debug(
125
+ `Credential issuer has a different deferred_credential_endpoint (${deferred_credential_endpoint}) from the Authorization Server (${authMetadata.deferred_credential_endpoint}). Will use the issuer value`,
126
+ );
127
+ } else {
128
+ deferred_credential_endpoint = authMetadata.deferred_credential_endpoint;
129
+ }
130
+ }
120
131
  }
121
132
 
122
133
  if (!authorization_endpoint) {
@@ -148,6 +159,7 @@ export class MetadataClient {
148
159
  issuer,
149
160
  token_endpoint,
150
161
  credential_endpoint,
162
+ deferred_credential_endpoint,
151
163
  authorization_server,
152
164
  authorization_endpoint,
153
165
  authorizationServerType,