@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,138 @@
|
|
|
1
|
+
import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, SearchValue } from '@sphereon/oid4vci-common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @function encodeJsonAsURI encodes a Json object into a URI
|
|
5
|
+
* @param json object
|
|
6
|
+
* @param opts:
|
|
7
|
+
* - urlTypeProperties: a list of properties of which the value is a URL
|
|
8
|
+
* - arrayTypeProperties: a list of properties which are an array
|
|
9
|
+
*/
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
|
+
export function convertJsonToURI(json: { [s: string]: any } | ArrayLike<any> | string, opts?: EncodeJsonAsURIOpts): string {
|
|
12
|
+
if (typeof json === 'string') {
|
|
13
|
+
return convertJsonToURI(JSON.parse(json), opts);
|
|
14
|
+
}
|
|
15
|
+
const results = [];
|
|
16
|
+
|
|
17
|
+
function encodeAndStripWhitespace(key: string): string {
|
|
18
|
+
return encodeURIComponent(key.replace(' ', ''));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const [key, value] of Object.entries(json)) {
|
|
22
|
+
if (!value) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
//Skip properties that are not of URL type
|
|
26
|
+
if (!opts?.uriTypeProperties?.includes(key)) {
|
|
27
|
+
results.push(`${key}=${value}`);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (opts?.arrayTypeProperties?.includes(key) && Array.isArray(value)) {
|
|
31
|
+
results.push(value.map((v) => `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(v, /\./g)}`).join('&'));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const isBool = typeof value == 'boolean';
|
|
35
|
+
const isNumber = typeof value == 'number';
|
|
36
|
+
const isString = typeof value == 'string';
|
|
37
|
+
let encoded;
|
|
38
|
+
if (isBool || isNumber) {
|
|
39
|
+
encoded = `${encodeAndStripWhitespace(key)}=${value}`;
|
|
40
|
+
} else if (isString) {
|
|
41
|
+
encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(value, /\./g)}`;
|
|
42
|
+
} else {
|
|
43
|
+
encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(JSON.stringify(value), /\./g)}`;
|
|
44
|
+
}
|
|
45
|
+
results.push(encoded);
|
|
46
|
+
}
|
|
47
|
+
const components = results.join('&');
|
|
48
|
+
if (opts?.baseUrl) {
|
|
49
|
+
return `${opts.baseUrl}?${components}`;
|
|
50
|
+
}
|
|
51
|
+
return components;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @function decodeUriAsJson decodes a URI into a Json object
|
|
56
|
+
* @param uri string
|
|
57
|
+
* @param opts:
|
|
58
|
+
* - requiredProperties: the required properties
|
|
59
|
+
* - arrayTypeProperties: properties that can show up more that once
|
|
60
|
+
*/
|
|
61
|
+
export function convertURIToJsonObject(uri: string, opts?: DecodeURIAsJsonOpts): unknown {
|
|
62
|
+
if (!uri || !opts?.requiredProperties?.every((p) => uri.includes(p))) {
|
|
63
|
+
throw new Error(BAD_PARAMS);
|
|
64
|
+
}
|
|
65
|
+
const uriComponents = getURIComponentsAsArray(uri, opts?.arrayTypeProperties);
|
|
66
|
+
return decodeJsonProperties(uriComponents);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function decodeJsonProperties(parts: string[] | string[][]): unknown {
|
|
70
|
+
const json: { [s: string]: any } | ArrayLike<any> = {};
|
|
71
|
+
for (const key in parts) {
|
|
72
|
+
const value = parts[key];
|
|
73
|
+
if (!value) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (Array.isArray(value)) {
|
|
77
|
+
if (value.length > 1) {
|
|
78
|
+
json[decodeURIComponent(key)] = value.map((v) => decodeURIComponent(v));
|
|
79
|
+
} else {
|
|
80
|
+
json[decodeURIComponent(key)] = decodeURIComponent(value[0]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const isBool = typeof value == 'boolean';
|
|
84
|
+
const isNumber = typeof value == 'number';
|
|
85
|
+
const isString = typeof value == 'string';
|
|
86
|
+
if (isBool || isNumber) {
|
|
87
|
+
json[decodeURIComponent(key)] = value;
|
|
88
|
+
} else if (isString) {
|
|
89
|
+
const decoded = decodeURIComponent(value);
|
|
90
|
+
if (decoded.startsWith('{') && decoded.endsWith('}')) {
|
|
91
|
+
json[decodeURIComponent(key)] = JSON.parse(decoded);
|
|
92
|
+
} else {
|
|
93
|
+
json[decodeURIComponent(key)] = decoded;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return json;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @function get URI Components as Array
|
|
102
|
+
* @param uri string
|
|
103
|
+
* @param arrayTypes array of string containing array like keys
|
|
104
|
+
*/
|
|
105
|
+
function getURIComponentsAsArray(uri: string, arrayTypes?: string[]): string[] | string[][] {
|
|
106
|
+
const parts = uri.includes('?') ? uri.split('?')[1] : uri.includes('://') ? uri.split('://')[1] : uri;
|
|
107
|
+
const json: string[] | string[][] = [];
|
|
108
|
+
const dict: string[] = parts.split('&');
|
|
109
|
+
for (const entry of dict) {
|
|
110
|
+
const pair: string[] = entry.split('=');
|
|
111
|
+
const p0: any = pair[0];
|
|
112
|
+
const p1: any = pair[1];
|
|
113
|
+
if (arrayTypes?.includes(p0)) {
|
|
114
|
+
const key = json[p0];
|
|
115
|
+
if (Array.isArray(key)) {
|
|
116
|
+
key.push(p1);
|
|
117
|
+
} else {
|
|
118
|
+
json[p0] = [p1];
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
json[p0] = p1;
|
|
123
|
+
}
|
|
124
|
+
return json;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @function customEncodeURIComponent is used to encode chars that are not encoded by default
|
|
131
|
+
* @param searchValue The pattern/regexp to find the char(s) to be encoded
|
|
132
|
+
* @param uriComponent query string
|
|
133
|
+
*/
|
|
134
|
+
function customEncodeURIComponent(uriComponent: string, searchValue: SearchValue): string {
|
|
135
|
+
// -_.!~*'() are not escaped because they are considered safe.
|
|
136
|
+
// Add them to the regex as you need
|
|
137
|
+
return encodeURIComponent(uriComponent).replace(searchValue, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
138
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Encoding, OpenIDResponse } from '@sphereon/oid4vci-common';
|
|
2
|
+
import { fetch } from 'cross-fetch';
|
|
3
|
+
import Debug from 'debug';
|
|
4
|
+
|
|
5
|
+
const debug = Debug('sphereon:openid4vci:http');
|
|
6
|
+
|
|
7
|
+
export const getJson = async <T>(
|
|
8
|
+
URL: string,
|
|
9
|
+
opts?: { bearerToken?: string; contentType?: string; accept?: string; customHeaders?: Record<string, string>; exceptionOnHttpErrorStatus?: boolean }
|
|
10
|
+
): Promise<OpenIDResponse<T>> => {
|
|
11
|
+
return await openIdFetch(URL, undefined, { method: 'GET', ...opts });
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const formPost = async <T>(
|
|
15
|
+
url: string,
|
|
16
|
+
body: BodyInit,
|
|
17
|
+
opts?: { bearerToken?: string; contentType?: string; accept?: string; customHeaders?: Record<string, string>; exceptionOnHttpErrorStatus?: boolean }
|
|
18
|
+
): Promise<OpenIDResponse<T>> => {
|
|
19
|
+
return await post(url, body, opts?.contentType ? { ...opts } : { contentType: Encoding.FORM_URL_ENCODED, ...opts });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const post = async <T>(
|
|
23
|
+
url: string,
|
|
24
|
+
body?: BodyInit,
|
|
25
|
+
opts?: { bearerToken?: string; contentType?: string; accept?: string; customHeaders?: Record<string, string>; exceptionOnHttpErrorStatus?: boolean }
|
|
26
|
+
): Promise<OpenIDResponse<T>> => {
|
|
27
|
+
return await openIdFetch(url, body, { method: 'POST', ...opts });
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const openIdFetch = async <T>(
|
|
31
|
+
url: string,
|
|
32
|
+
body?: BodyInit,
|
|
33
|
+
opts?: {
|
|
34
|
+
method?: string;
|
|
35
|
+
bearerToken?: string;
|
|
36
|
+
contentType?: string;
|
|
37
|
+
accept?: string;
|
|
38
|
+
customHeaders?: Record<string, string>;
|
|
39
|
+
exceptionOnHttpErrorStatus?: boolean;
|
|
40
|
+
}
|
|
41
|
+
): Promise<OpenIDResponse<T>> => {
|
|
42
|
+
const headers: Record<string, string> = opts?.customHeaders ?? {};
|
|
43
|
+
if (opts?.bearerToken) {
|
|
44
|
+
headers['Authorization'] = `Bearer ${opts.bearerToken}`;
|
|
45
|
+
}
|
|
46
|
+
const method = opts?.method ? opts.method : body ? 'POST' : 'GET';
|
|
47
|
+
const accept = opts?.accept ? opts.accept : 'application/json';
|
|
48
|
+
headers['Accept'] = accept;
|
|
49
|
+
if (headers['Content-Type']) {
|
|
50
|
+
if (opts?.contentType && opts.contentType !== headers['Content-Type']) {
|
|
51
|
+
throw Error(
|
|
52
|
+
`Mismatch in content-types from custom headers (${headers['Content-Type']}) and supplied content type option (${opts.contentType})`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
if (opts?.contentType) {
|
|
57
|
+
headers['Content-Type'] = opts.contentType;
|
|
58
|
+
} else if (method !== 'GET') {
|
|
59
|
+
headers['Content-Type'] = 'application/json';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const payload: RequestInit = {
|
|
64
|
+
method,
|
|
65
|
+
headers,
|
|
66
|
+
body,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
debug(`START fetching url: ${url}`);
|
|
70
|
+
if (body) {
|
|
71
|
+
debug(`Body:\r\n${JSON.stringify(body)}`);
|
|
72
|
+
}
|
|
73
|
+
debug(`Headers:\r\n${JSON.stringify(payload.headers)}`);
|
|
74
|
+
const origResponse = await fetch(url, payload);
|
|
75
|
+
const isJSONResponse = accept === 'application/json' || origResponse.headers.get('Content-Type') === 'application/json';
|
|
76
|
+
const success = origResponse && origResponse.status >= 200 && origResponse.status < 400;
|
|
77
|
+
const responseText = await origResponse.text();
|
|
78
|
+
const responseBody = isJSONResponse ? JSON.parse(responseText) : responseText;
|
|
79
|
+
|
|
80
|
+
debug(`${success ? 'success' : 'error'} status: ${origResponse.status}, body:\r\n${JSON.stringify(responseBody)}`);
|
|
81
|
+
if (!success && opts?.exceptionOnHttpErrorStatus) {
|
|
82
|
+
const error = JSON.stringify(responseBody);
|
|
83
|
+
throw new Error(error === '{}' ? '{"error": "not found"}' : error);
|
|
84
|
+
}
|
|
85
|
+
debug(`END fetching url: ${url}`);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
origResponse,
|
|
89
|
+
successBody: success ? responseBody : undefined,
|
|
90
|
+
errorBody: !success ? responseBody : undefined,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const isValidURL = (url: string): boolean => {
|
|
95
|
+
const urlPattern = new RegExp(
|
|
96
|
+
'^(https?:\\/\\/)' + // validate protocol
|
|
97
|
+
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
|
|
98
|
+
'((localhost))|' + // validate OR localhost
|
|
99
|
+
'((\\d{1,3}\\.){3}\\d{1,3}))' + // validate OR ip (v4) address
|
|
100
|
+
'(\\:\\d+)?(\\/[-a-z\\d%_.~+:]*)*' + // validate port and path
|
|
101
|
+
'(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
|
|
102
|
+
'(\\#[-a-z\\d_]*)?$', // validate fragment locator
|
|
103
|
+
'i'
|
|
104
|
+
);
|
|
105
|
+
return !!urlPattern.test(url);
|
|
106
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BAD_PARAMS,
|
|
3
|
+
JWS_NOT_VALID,
|
|
4
|
+
Jwt,
|
|
5
|
+
JWTHeader,
|
|
6
|
+
JWTPayload,
|
|
7
|
+
ProofOfPossession,
|
|
8
|
+
ProofOfPossessionCallbacks,
|
|
9
|
+
ProofType,
|
|
10
|
+
Typ,
|
|
11
|
+
} from '@sphereon/oid4vci-common';
|
|
12
|
+
import Debug from 'debug';
|
|
13
|
+
|
|
14
|
+
const debug = Debug('sphereon:openid4vci:token');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* - proofOfPossessionCallback: JWTSignerCallback
|
|
19
|
+
* Mandatory if you want to create (sign) ProofOfPossession
|
|
20
|
+
* - proofOfPossessionVerifierCallback?: JWTVerifyCallback
|
|
21
|
+
* If exists, verifies the ProofOfPossession
|
|
22
|
+
* - proofOfPossessionCallbackArgs: ProofOfPossessionCallbackArgs
|
|
23
|
+
* arguments needed for signing ProofOfPossession
|
|
24
|
+
* @param callbacks:
|
|
25
|
+
* - proofOfPossessionCallback: JWTSignerCallback
|
|
26
|
+
* Mandatory to create (sign) ProofOfPossession
|
|
27
|
+
* - proofOfPossessionVerifierCallback?: JWTVerifyCallback
|
|
28
|
+
* If exists, verifies the ProofOfPossession
|
|
29
|
+
* @param jwtProps
|
|
30
|
+
* @param existingJwt
|
|
31
|
+
* - Optional, clientId of the party requesting the credential
|
|
32
|
+
*/
|
|
33
|
+
export const createProofOfPossession = async (
|
|
34
|
+
callbacks: ProofOfPossessionCallbacks,
|
|
35
|
+
jwtProps?: JwtProps,
|
|
36
|
+
existingJwt?: Jwt
|
|
37
|
+
): Promise<ProofOfPossession> => {
|
|
38
|
+
if (!callbacks.signCallback) {
|
|
39
|
+
debug(`no jwt signer callback or arguments supplied!`);
|
|
40
|
+
throw new Error(BAD_PARAMS);
|
|
41
|
+
}
|
|
42
|
+
const signerArgs = createJWT(jwtProps, existingJwt);
|
|
43
|
+
const jwt = await callbacks.signCallback(signerArgs, signerArgs.header.kid);
|
|
44
|
+
const proof = {
|
|
45
|
+
proof_type: ProofType.JWT,
|
|
46
|
+
jwt,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
partiallyValidateJWS(jwt);
|
|
51
|
+
if (callbacks.verifyCallback) {
|
|
52
|
+
debug(`Calling supplied verify callback....`);
|
|
53
|
+
await callbacks.verifyCallback({ jwt, kid: signerArgs.header.kid });
|
|
54
|
+
debug(`Supplied verify callback return success result`);
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
debug(`JWS was not valid`);
|
|
58
|
+
throw new Error(JWS_NOT_VALID);
|
|
59
|
+
}
|
|
60
|
+
debug(`Proof of Possession JWT:\r\n${jwt}`);
|
|
61
|
+
return proof;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const partiallyValidateJWS = (jws: string): void => {
|
|
65
|
+
if (jws.split('.').length !== 3 || !jws.startsWith('ey')) {
|
|
66
|
+
throw new Error(JWS_NOT_VALID);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export interface JwtProps {
|
|
71
|
+
typ?: string;
|
|
72
|
+
kid?: string;
|
|
73
|
+
issuer?: string;
|
|
74
|
+
clientId?: string;
|
|
75
|
+
alg?: string;
|
|
76
|
+
jti?: string;
|
|
77
|
+
nonce?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => {
|
|
81
|
+
const aud = getJwtProperty('aud', true, jwtProps?.issuer, existingJwt?.payload?.aud);
|
|
82
|
+
const iss = getJwtProperty('iss', false, jwtProps?.clientId, existingJwt?.payload?.iss);
|
|
83
|
+
const jti = getJwtProperty('jti', false, jwtProps?.jti, existingJwt?.payload?.jti);
|
|
84
|
+
const nonce = getJwtProperty('nonce', false, jwtProps?.nonce, existingJwt?.payload?.nonce); // Officially this is required, but some implementations don't have it
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
86
|
+
const alg = getJwtProperty('alg', false, jwtProps?.alg, existingJwt?.header?.alg, 'ES256')!;
|
|
87
|
+
const kid = getJwtProperty('kid', true, jwtProps?.kid, existingJwt?.header?.kid);
|
|
88
|
+
const jwt: Partial<Jwt> = existingJwt ? existingJwt : {};
|
|
89
|
+
const now = +new Date();
|
|
90
|
+
const jwtPayload: Partial<JWTPayload> = {
|
|
91
|
+
aud,
|
|
92
|
+
iat: jwt.payload?.iat ? jwt.payload.iat : now / 1000 - 60, // Let's ensure we subtract 60 seconds for potential time offsets
|
|
93
|
+
exp: jwt.payload?.exp ? jwt.payload.exp : now / 1000 + 10 * 60,
|
|
94
|
+
nonce,
|
|
95
|
+
...(iss ? { iss } : {}),
|
|
96
|
+
...(jti ? { jti } : {}),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const jwtHeader: JWTHeader = {
|
|
100
|
+
typ: Typ.JWT,
|
|
101
|
+
alg,
|
|
102
|
+
kid,
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
payload: { ...jwt.payload, ...jwtPayload },
|
|
106
|
+
header: { ...jwt.header, ...jwtHeader },
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getJwtProperty = (
|
|
111
|
+
propertyName: string,
|
|
112
|
+
required: boolean,
|
|
113
|
+
option?: string,
|
|
114
|
+
jwtProperty?: string,
|
|
115
|
+
defaultValue?: string
|
|
116
|
+
): string | undefined => {
|
|
117
|
+
if (option && jwtProperty && option !== jwtProperty) {
|
|
118
|
+
throw Error(`Cannot have a property '${propertyName}' with value '${option}' and different JWT value '${jwtProperty}' at the same time`);
|
|
119
|
+
}
|
|
120
|
+
let result = jwtProperty ? jwtProperty : option;
|
|
121
|
+
if (!result) {
|
|
122
|
+
if (required) {
|
|
123
|
+
throw Error(`No ${propertyName} property provided either in a JWT or as option`);
|
|
124
|
+
}
|
|
125
|
+
result = defaultValue;
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './Encoding';
|
|
2
|
-
export * from './HttpUtils';
|
|
3
|
-
export * from './ProofUtil';
|
|
1
|
+
export * from './Encoding';
|
|
2
|
+
export * from './HttpUtils';
|
|
3
|
+
export * from './ProofUtil';
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './AccessTokenClient';
|
|
2
|
+
export * from './CredentialOffer';
|
|
3
|
+
export * from './CredentialRequestClient';
|
|
4
|
+
export * from './CredentialRequestClientBuilderV1_0_09';
|
|
5
|
+
export * from './functions';
|
|
6
|
+
export * from './MetadataClient';
|
|
7
|
+
export * from './OpenID4VCIClient';
|
|
8
|
+
export * from './ProofOfPossessionBuilder';
|
package/package.json
CHANGED
|
@@ -1,71 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@sphereon/oid4vci-client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OpenID for Verifiable Credential Issuance (
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
},
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
},
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"OID4VCI"
|
|
70
|
-
]
|
|
71
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@sphereon/oid4vci-client",
|
|
3
|
+
"version": "0.4.1-unstable.247+0bbe17c",
|
|
4
|
+
"description": "OpenID for Verifiable Credential Issuance (OpenID4VCI) client",
|
|
5
|
+
"source": "lib/index.ts",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"author": "Sphereon",
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"private": false,
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@sphereon/oid4vci-common": "0.4.1-unstable.247+0bbe17c",
|
|
19
|
+
"@sphereon/ssi-types": "^0.9.0",
|
|
20
|
+
"cross-fetch": "^3.1.5",
|
|
21
|
+
"debug": "^4.3.4",
|
|
22
|
+
"uint8arrays": "^3.1.1"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/jest": "^29.5.0",
|
|
26
|
+
"@types/node": "^18.15.3",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
|
28
|
+
"@typescript-eslint/parser": "^5.36.1",
|
|
29
|
+
"codecov": "^3.8.3",
|
|
30
|
+
"dotenv": "^16.0.2",
|
|
31
|
+
"eslint": "^8.23.0",
|
|
32
|
+
"eslint-config-prettier": "^8.5.0",
|
|
33
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
34
|
+
"eslint-plugin-import": "^2.26.0",
|
|
35
|
+
"jest": "^29.1.2",
|
|
36
|
+
"jest-junit": "^14.0.1",
|
|
37
|
+
"jose": "^4.10.0",
|
|
38
|
+
"nock": "^13.2.9",
|
|
39
|
+
"npm-run-all": "^4.1.5",
|
|
40
|
+
"open-cli": "^7.0.1",
|
|
41
|
+
"ts-jest": "^29.0.5",
|
|
42
|
+
"ts-node": "^10.9.1",
|
|
43
|
+
"typescript": "4.9.5"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=16"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"lib/**/*",
|
|
50
|
+
"dist/**/*"
|
|
51
|
+
],
|
|
52
|
+
"prettier": {
|
|
53
|
+
"singleQuote": true,
|
|
54
|
+
"printWidth": 150
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"Sphereon",
|
|
58
|
+
"Verifiable Credentials",
|
|
59
|
+
"OpenID",
|
|
60
|
+
"OpenID for Verifiable Credential Issuance",
|
|
61
|
+
"OAuth2",
|
|
62
|
+
"SSI",
|
|
63
|
+
"OpenID4VCI",
|
|
64
|
+
"OIDC4VCI",
|
|
65
|
+
"OID4VCI"
|
|
66
|
+
],
|
|
67
|
+
"gitHead": "0bbe17c13de4df95e2fd79b3470a746cc7a5374a"
|
|
68
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Release Notes
|
|
2
|
-
|
|
3
|
-
## v0.2.0 - 2022-11-04
|
|
4
|
-
|
|
5
|
-
Release with support for the pre-authorized code flow only.
|
|
6
|
-
|
|
7
|
-
Expect breaking changes in the future, as this package still is undergoing heavy development.
|
|
8
|
-
|
|
9
|
-
- Added:
|
|
10
|
-
- Support for well-known OID4VCI, oAuth2 and OpenID metadata
|
|
11
|
-
|
|
12
|
-
- Fixes:
|
|
13
|
-
- Several fixes related to pincode handling
|
|
14
|
-
- Overall fixes
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## v0.1.0 - 2022-10-18
|
|
18
|
-
|
|
19
|
-
Initial release with support for the pre-authorized code flow only.
|
|
20
|
-
|
|
21
|
-
Expect breaking changes in the future, as this package still is undergoing heavy development.
|
package/dist/main/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './lib';
|
package/dist/main/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./lib"), exports);
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsd0NBQXFCIn0=
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { AccessTokenRequest, AccessTokenRequestOpts, AccessTokenResponse, AuthorizationServerOpts, EndpointMetadata, ErrorResponse, IssuanceInitiationRequestPayload, IssuanceInitiationWithBaseUrl, IssuerOpts } from './types';
|
|
2
|
-
export declare class AccessTokenClient {
|
|
3
|
-
acquireAccessTokenUsingIssuanceInitiation(issuanceInitiation: IssuanceInitiationWithBaseUrl, opts?: AccessTokenRequestOpts): Promise<AccessTokenResponse | ErrorResponse>;
|
|
4
|
-
acquireAccessTokenUsingRequest(accessTokenRequest: AccessTokenRequest, opts?: {
|
|
5
|
-
isPinRequired?: boolean;
|
|
6
|
-
metadata?: EndpointMetadata;
|
|
7
|
-
asOpts?: AuthorizationServerOpts;
|
|
8
|
-
issuerOpts?: IssuerOpts;
|
|
9
|
-
}): Promise<AccessTokenResponse | ErrorResponse>;
|
|
10
|
-
createAccessTokenRequest(issuanceInitiationRequest: IssuanceInitiationRequestPayload, opts?: AccessTokenRequestOpts): Promise<AccessTokenRequest>;
|
|
11
|
-
private assertPreAuthorizedGrantType;
|
|
12
|
-
private isPinRequiredValue;
|
|
13
|
-
private assertNumericPin;
|
|
14
|
-
private assertNonEmptyPreAuthorizedCode;
|
|
15
|
-
private validate;
|
|
16
|
-
private sendAuthCode;
|
|
17
|
-
private determineTokenURL;
|
|
18
|
-
private creatTokenURLFromURL;
|
|
19
|
-
private throwNotSupportedFlow;
|
|
20
|
-
}
|