@sphereon/oid4vci-client 0.2.0 → 0.4.1-next.285
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 +222 -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/CredentialOfferClient.d.ts +10 -0
- package/dist/CredentialOfferClient.d.ts.map +1 -0
- package/dist/CredentialOfferClient.js +101 -0
- package/dist/CredentialOfferClient.js.map +1 -0
- package/dist/CredentialRequestClient.d.ts +33 -0
- package/dist/CredentialRequestClient.d.ts.map +1 -0
- package/dist/CredentialRequestClient.js +118 -0
- package/dist/CredentialRequestClient.js.map +1 -0
- package/dist/CredentialRequestClientBuilder.d.ts +34 -0
- package/dist/CredentialRequestClientBuilder.d.ts.map +1 -0
- package/dist/CredentialRequestClientBuilder.js +87 -0
- package/dist/CredentialRequestClientBuilder.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 +75 -0
- package/dist/OpenID4VCIClient.d.ts.map +1 -0
- package/dist/OpenID4VCIClient.js +403 -0
- package/dist/OpenID4VCIClient.js.map +1 -0
- package/dist/ProofOfPossessionBuilder.d.ts +38 -0
- package/dist/ProofOfPossessionBuilder.d.ts.map +1 -0
- package/dist/ProofOfPossessionBuilder.js +129 -0
- package/dist/ProofOfPossessionBuilder.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 +104 -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 → functions}/index.js +20 -18
- 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 +249 -0
- package/lib/AuthorizationDetailsBuilder.ts +46 -0
- package/lib/CredentialOfferClient.ts +108 -0
- package/lib/CredentialRequestClient.ts +137 -0
- package/lib/CredentialRequestClientBuilder.ts +110 -0
- package/lib/MetadataClient.ts +147 -0
- package/lib/OpenID4VCIClient.ts +523 -0
- package/lib/ProofOfPossessionBuilder.ts +181 -0
- package/lib/__tests__/AccessTokenClient.spec.ts +225 -0
- package/lib/__tests__/AuthorizationDetailsBuilder.spec.ts +65 -0
- package/lib/__tests__/AuthzFlowType.spec.ts +39 -0
- package/lib/__tests__/CredentialRequestClient.spec.ts +291 -0
- package/lib/__tests__/CredentialRequestClientBuilder.spec.ts +121 -0
- package/lib/__tests__/HttpUtils.spec.ts +37 -0
- package/lib/__tests__/IT.spec.ts +173 -0
- package/lib/__tests__/IssuanceInitiation.spec.ts +48 -0
- package/lib/__tests__/JsonURIConversions.spec.ts +146 -0
- package/lib/__tests__/MetadataClient.spec.ts +203 -0
- package/lib/__tests__/MetadataMocks.ts +444 -0
- package/lib/__tests__/OpenID4VCIClient.spec.ts +166 -0
- package/lib/__tests__/OpenID4VCIClientPAR.spec.ts +112 -0
- package/lib/__tests__/ProofOfPossessionBuilder.spec.ts +110 -0
- package/lib/__tests__/data/VciDataFixtures.ts +744 -0
- package/lib/functions/ProofUtil.ts +120 -0
- package/lib/functions/index.ts +3 -0
- package/{dist/main/lib/index.d.ts → lib/index.ts} +8 -7
- package/package.json +68 -71
- package/CHANGELOG.md +0 -21
- package/dist/main/index.d.ts +0 -1
- 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.d.ts +0 -17
- 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/functions/index.d.ts +0 -3
- package/dist/main/lib/functions/index.js +0 -20
- 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,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialRequestV1_0_08,
|
|
3
|
+
CredentialResponse,
|
|
4
|
+
OID4VCICredentialFormat,
|
|
5
|
+
OpenId4VCIVersion,
|
|
6
|
+
OpenIDResponse,
|
|
7
|
+
ProofOfPossession,
|
|
8
|
+
UniformCredentialRequest,
|
|
9
|
+
URL_NOT_VALID,
|
|
10
|
+
} from '@sphereon/oid4vci-common';
|
|
11
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
12
|
+
import Debug from 'debug';
|
|
13
|
+
|
|
14
|
+
import { CredentialRequestClientBuilder } from './CredentialRequestClientBuilder';
|
|
15
|
+
import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder';
|
|
16
|
+
import { isValidURL, post } from './functions';
|
|
17
|
+
|
|
18
|
+
const debug = Debug('sphereon:oid4vci:credential');
|
|
19
|
+
|
|
20
|
+
export interface CredentialRequestOpts {
|
|
21
|
+
credentialEndpoint: string;
|
|
22
|
+
credentialTypes: string[];
|
|
23
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
24
|
+
proof: ProofOfPossession;
|
|
25
|
+
token: string;
|
|
26
|
+
version: OpenId4VCIVersion;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class CredentialRequestClient {
|
|
30
|
+
private readonly _credentialRequestOpts: Partial<CredentialRequestOpts>;
|
|
31
|
+
|
|
32
|
+
get credentialRequestOpts(): CredentialRequestOpts {
|
|
33
|
+
return this._credentialRequestOpts as CredentialRequestOpts;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public getCredentialEndpoint(): string {
|
|
37
|
+
return this.credentialRequestOpts.credentialEndpoint;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public constructor(builder: CredentialRequestClientBuilder) {
|
|
41
|
+
this._credentialRequestOpts = { ...builder };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public async acquireCredentialsUsingProof(opts: {
|
|
45
|
+
proofInput: ProofOfPossessionBuilder | ProofOfPossession;
|
|
46
|
+
credentialTypes?: string | string[];
|
|
47
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
48
|
+
}): Promise<OpenIDResponse<CredentialResponse>> {
|
|
49
|
+
const { credentialTypes, proofInput, format } = opts;
|
|
50
|
+
|
|
51
|
+
const request = await this.createCredentialRequest({ proofInput, credentialTypes, format, version: this.version() });
|
|
52
|
+
return await this.acquireCredentialsUsingRequest(request);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
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
|
+
}
|
|
74
|
+
const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
|
|
75
|
+
if (!isValidURL(credentialEndpoint)) {
|
|
76
|
+
debug(`Invalid credential endpoint: ${credentialEndpoint}`);
|
|
77
|
+
throw new Error(URL_NOT_VALID);
|
|
78
|
+
}
|
|
79
|
+
debug(`Acquiring credential(s) from: ${credentialEndpoint}`);
|
|
80
|
+
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}`);
|
|
83
|
+
return response;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public async createCredentialRequest(opts: {
|
|
87
|
+
proofInput: ProofOfPossessionBuilder | ProofOfPossession;
|
|
88
|
+
credentialTypes?: string | string[];
|
|
89
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
90
|
+
version: OpenId4VCIVersion;
|
|
91
|
+
}): Promise<UniformCredentialRequest> {
|
|
92
|
+
const { proofInput } = opts;
|
|
93
|
+
const formatSelection = opts.format ?? this.credentialRequestOpts.format;
|
|
94
|
+
|
|
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) {
|
|
105
|
+
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
|
+
}
|
|
109
|
+
const typesSelection =
|
|
110
|
+
opts?.credentialTypes && (typeof opts.credentialTypes === 'string' || opts.credentialTypes.length > 0)
|
|
111
|
+
? opts.credentialTypes
|
|
112
|
+
: this.credentialRequestOpts.credentialTypes;
|
|
113
|
+
const types = Array.isArray(typesSelection) ? typesSelection : [typesSelection];
|
|
114
|
+
if (types.length === 0) {
|
|
115
|
+
throw Error(`Credential type(s) need to be provided`);
|
|
116
|
+
} else if (!this.isV11OrHigher() && types.length !== 1) {
|
|
117
|
+
throw Error('Only a single credential type is supported for V8/V9');
|
|
118
|
+
}
|
|
119
|
+
|
|
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;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private version(): OpenId4VCIVersion {
|
|
132
|
+
return this.credentialRequestOpts?.version ?? OpenId4VCIVersion.VER_1_0_11;
|
|
133
|
+
}
|
|
134
|
+
private isV11OrHigher(): boolean {
|
|
135
|
+
return this.version() >= OpenId4VCIVersion.VER_1_0_11;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccessTokenResponse,
|
|
3
|
+
CredentialIssuerMetadata,
|
|
4
|
+
CredentialOfferPayloadV1_0_08,
|
|
5
|
+
CredentialOfferRequestWithBaseUrl,
|
|
6
|
+
determineSpecVersionFromOffer,
|
|
7
|
+
EndpointMetadata,
|
|
8
|
+
getIssuerFromCredentialOfferPayload,
|
|
9
|
+
OID4VCICredentialFormat,
|
|
10
|
+
OpenId4VCIVersion,
|
|
11
|
+
UniformCredentialOfferRequest,
|
|
12
|
+
} from '@sphereon/oid4vci-common';
|
|
13
|
+
import { CredentialFormat } from '@sphereon/ssi-types';
|
|
14
|
+
|
|
15
|
+
import { CredentialOfferClient } from './CredentialOfferClient';
|
|
16
|
+
import { CredentialRequestClient } from './CredentialRequestClient';
|
|
17
|
+
|
|
18
|
+
export class CredentialRequestClientBuilder {
|
|
19
|
+
credentialEndpoint?: string;
|
|
20
|
+
credentialTypes: string[] = [];
|
|
21
|
+
format?: CredentialFormat | OID4VCICredentialFormat;
|
|
22
|
+
token?: string;
|
|
23
|
+
version?: OpenId4VCIVersion;
|
|
24
|
+
|
|
25
|
+
public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise<CredentialRequestClientBuilder> {
|
|
26
|
+
const offer = await CredentialOfferClient.fromURI(uri);
|
|
27
|
+
return CredentialRequestClientBuilder.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public static fromCredentialOfferRequest(opts: {
|
|
31
|
+
request: UniformCredentialOfferRequest;
|
|
32
|
+
scheme?: string;
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
version?: OpenId4VCIVersion;
|
|
35
|
+
metadata?: EndpointMetadata;
|
|
36
|
+
}): CredentialRequestClientBuilder {
|
|
37
|
+
const { request, metadata } = opts;
|
|
38
|
+
const version = opts.version ?? request.version ?? determineSpecVersionFromOffer(request.original_credential_offer);
|
|
39
|
+
const builder = new CredentialRequestClientBuilder();
|
|
40
|
+
const issuer = getIssuerFromCredentialOfferPayload(request.credential_offer) ?? (metadata?.issuer as string);
|
|
41
|
+
builder.withVersion(version);
|
|
42
|
+
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
|
|
43
|
+
|
|
44
|
+
if (version <= OpenId4VCIVersion.VER_1_0_08) {
|
|
45
|
+
//todo: This basically sets all types available during initiation. Probably the user only wants a subset. So do we want to do this?
|
|
46
|
+
builder.withCredentialType((request.original_credential_offer as CredentialOfferPayloadV1_0_08).credential_type);
|
|
47
|
+
} else {
|
|
48
|
+
// todo: look whether this is correct
|
|
49
|
+
builder.withCredentialType(request.credential_offer.credentials.flatMap((c) => (typeof c === 'string' ? c : c.types)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return builder;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public static fromCredentialOffer({
|
|
56
|
+
credentialOffer,
|
|
57
|
+
metadata,
|
|
58
|
+
}: {
|
|
59
|
+
credentialOffer: CredentialOfferRequestWithBaseUrl;
|
|
60
|
+
metadata?: EndpointMetadata;
|
|
61
|
+
}): CredentialRequestClientBuilder {
|
|
62
|
+
return CredentialRequestClientBuilder.fromCredentialOfferRequest({
|
|
63
|
+
request: credentialOffer,
|
|
64
|
+
metadata,
|
|
65
|
+
version: credentialOffer.version,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public withCredentialEndpointFromMetadata(metadata: CredentialIssuerMetadata): CredentialRequestClientBuilder {
|
|
70
|
+
this.credentialEndpoint = metadata.credential_endpoint;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public withCredentialEndpoint(credentialEndpoint: string): CredentialRequestClientBuilder {
|
|
75
|
+
this.credentialEndpoint = credentialEndpoint;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public withCredentialType(credentialTypes: string | string[]): CredentialRequestClientBuilder {
|
|
80
|
+
this.credentialTypes = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes];
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public withFormat(format: CredentialFormat | OID4VCICredentialFormat): CredentialRequestClientBuilder {
|
|
85
|
+
this.format = format;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public withToken(accessToken: string): CredentialRequestClientBuilder {
|
|
90
|
+
this.token = accessToken;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public withTokenFromResponse(response: AccessTokenResponse): CredentialRequestClientBuilder {
|
|
95
|
+
this.token = response.access_token;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public withVersion(version: OpenId4VCIVersion): CredentialRequestClientBuilder {
|
|
100
|
+
this.version = version;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public build(): CredentialRequestClient {
|
|
105
|
+
if (!this.version) {
|
|
106
|
+
this.withVersion(OpenId4VCIVersion.VER_1_0_11);
|
|
107
|
+
}
|
|
108
|
+
return new CredentialRequestClient(this);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialIssuerMetadata,
|
|
3
|
+
CredentialOfferPayload,
|
|
4
|
+
CredentialOfferRequestWithBaseUrl,
|
|
5
|
+
EndpointMetadata,
|
|
6
|
+
getIssuerFromCredentialOfferPayload,
|
|
7
|
+
OAuth2ASMetadata,
|
|
8
|
+
Oauth2ASWithOID4VCIMetadata,
|
|
9
|
+
OpenIDResponse,
|
|
10
|
+
WellKnownEndpoints,
|
|
11
|
+
} from '@sphereon/oid4vci-common';
|
|
12
|
+
import Debug from 'debug';
|
|
13
|
+
|
|
14
|
+
import { getJson } from './functions';
|
|
15
|
+
|
|
16
|
+
const debug = Debug('sphereon:oid4vci:metadata');
|
|
17
|
+
|
|
18
|
+
export class MetadataClient {
|
|
19
|
+
/**
|
|
20
|
+
* Retrieve metadata using the Initiation obtained from a previous step
|
|
21
|
+
*
|
|
22
|
+
* @param credentialOffer
|
|
23
|
+
*/
|
|
24
|
+
public static async retrieveAllMetadataFromCredentialOffer(credentialOffer: CredentialOfferRequestWithBaseUrl): Promise<EndpointMetadata> {
|
|
25
|
+
return MetadataClient.retrieveAllMetadataFromCredentialOfferRequest(credentialOffer.credential_offer);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Retrieve the metada using the initiation request obtained from a previous step
|
|
30
|
+
* @param request
|
|
31
|
+
*/
|
|
32
|
+
public static async retrieveAllMetadataFromCredentialOfferRequest(request: CredentialOfferPayload): Promise<EndpointMetadata> {
|
|
33
|
+
if (getIssuerFromCredentialOfferPayload(request)) {
|
|
34
|
+
return MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(request) as string);
|
|
35
|
+
}
|
|
36
|
+
throw new Error("can't retrieve metadata from CredentialOfferRequest. No issuer field is present");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Retrieve all metadata from an issuer
|
|
41
|
+
* @param issuer The issuer URL
|
|
42
|
+
* @param opts
|
|
43
|
+
*/
|
|
44
|
+
public static async retrieveAllMetadata(issuer: string, opts?: { errorOnNotFound: boolean }): Promise<EndpointMetadata> {
|
|
45
|
+
let token_endpoint;
|
|
46
|
+
let credential_endpoint;
|
|
47
|
+
const response = await MetadataClient.retrieveOpenID4VCIServerMetadata(issuer);
|
|
48
|
+
let issuerMetadata = response?.successBody;
|
|
49
|
+
if (issuerMetadata) {
|
|
50
|
+
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${issuerMetadata}`);
|
|
51
|
+
credential_endpoint = issuerMetadata.credential_endpoint;
|
|
52
|
+
token_endpoint = issuerMetadata.token_endpoint;
|
|
53
|
+
if (!token_endpoint && issuerMetadata.authorization_server) {
|
|
54
|
+
debug(
|
|
55
|
+
`Issuer ${issuer} OID4VCI metadata has separate authorization_server ${issuerMetadata.authorization_server} that contains the token endpoint`
|
|
56
|
+
);
|
|
57
|
+
// Crossword uses this to separate the AS metadata. We fail when not found, since we now have no way of getting the token endpoint
|
|
58
|
+
const response: OpenIDResponse<OAuth2ASMetadata> = await this.retrieveWellknown(
|
|
59
|
+
issuerMetadata.authorization_server,
|
|
60
|
+
WellKnownEndpoints.OAUTH_AS,
|
|
61
|
+
{
|
|
62
|
+
errorOnNotFound: true,
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
token_endpoint = response.successBody?.token_endpoint;
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// No specific OID4VCI endpoint. Either can be an OAuth2 AS or an OpenID IDP. Let's start with OIDC first
|
|
69
|
+
let response: OpenIDResponse<Oauth2ASWithOID4VCIMetadata> = await MetadataClient.retrieveWellknown(
|
|
70
|
+
issuer,
|
|
71
|
+
WellKnownEndpoints.OPENID_CONFIGURATION,
|
|
72
|
+
{
|
|
73
|
+
errorOnNotFound: false,
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
let asConfig = response.successBody;
|
|
77
|
+
if (asConfig) {
|
|
78
|
+
debug(`Issuer ${issuer} has OpenID Connect Server metadata in well-known location`);
|
|
79
|
+
} else {
|
|
80
|
+
// Now oAuth2
|
|
81
|
+
response = await MetadataClient.retrieveWellknown(issuer, WellKnownEndpoints.OAUTH_AS, { errorOnNotFound: false });
|
|
82
|
+
asConfig = response.successBody;
|
|
83
|
+
}
|
|
84
|
+
if (asConfig) {
|
|
85
|
+
debug(`Issuer ${issuer} has oAuth2 Server metadata in well-known location`);
|
|
86
|
+
issuerMetadata = asConfig;
|
|
87
|
+
credential_endpoint = issuerMetadata.credential_endpoint;
|
|
88
|
+
token_endpoint = issuerMetadata.token_endpoint;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!token_endpoint) {
|
|
92
|
+
debug(`Issuer ${issuer} does not have a token_endpoint listed in well-known locations!`);
|
|
93
|
+
if (opts?.errorOnNotFound) {
|
|
94
|
+
throw new Error(`Could not deduce the token endpoint for ${issuer}`);
|
|
95
|
+
} else {
|
|
96
|
+
token_endpoint = `${issuer}${issuer.endsWith('/') ? '' : '/'}token`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!credential_endpoint) {
|
|
100
|
+
debug(`Issuer ${issuer} does not have a credential_endpoint listed in well-known locations!`);
|
|
101
|
+
if (opts?.errorOnNotFound) {
|
|
102
|
+
throw new Error(`Could not deduce the credential endpoint for ${issuer}`);
|
|
103
|
+
} else {
|
|
104
|
+
credential_endpoint = `${issuer}${issuer.endsWith('/') ? '' : '/'}credential`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);
|
|
108
|
+
return {
|
|
109
|
+
issuer,
|
|
110
|
+
token_endpoint,
|
|
111
|
+
credential_endpoint,
|
|
112
|
+
issuerMetadata,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Retrieve only the OID4VCI metadata for the issuer. So no OIDC/OAuth2 metadata
|
|
118
|
+
*
|
|
119
|
+
* @param issuerHost The issuer hostname
|
|
120
|
+
*/
|
|
121
|
+
public static async retrieveOpenID4VCIServerMetadata(issuerHost: string): Promise<OpenIDResponse<CredentialIssuerMetadata> | undefined> {
|
|
122
|
+
// Since the server metadata endpoint is optional we are not going to throw an error.
|
|
123
|
+
return MetadataClient.retrieveWellknown(issuerHost, WellKnownEndpoints.OPENID4VCI_ISSUER, { errorOnNotFound: false });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Allows to retrieve information from a well-known location
|
|
128
|
+
*
|
|
129
|
+
* @param host The host
|
|
130
|
+
* @param endpointType The endpoint type, currently supports OID4VCI, OIDC and OAuth2 endpoint types
|
|
131
|
+
* @param opts Options, like for instance whether an error should be thrown in case the endpoint doesn't exist
|
|
132
|
+
*/
|
|
133
|
+
public static async retrieveWellknown<T>(
|
|
134
|
+
host: string,
|
|
135
|
+
endpointType: WellKnownEndpoints,
|
|
136
|
+
opts?: { errorOnNotFound?: boolean }
|
|
137
|
+
): Promise<OpenIDResponse<T>> {
|
|
138
|
+
const result: OpenIDResponse<T> = await getJson(`${host.endsWith('/') ? host.slice(0, -1) : host}${endpointType}`, {
|
|
139
|
+
exceptionOnHttpErrorStatus: opts?.errorOnNotFound,
|
|
140
|
+
});
|
|
141
|
+
if (result.origResponse.status === 404) {
|
|
142
|
+
// We only get here when error on not found is false
|
|
143
|
+
debug(`host ${host} with endpoint type ${endpointType} was not found (404)`);
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
}
|