@pagopa/io-react-native-wallet 0.7.4 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +49 -31
- package/lib/commonjs/credential/index.js +13 -0
- package/lib/commonjs/credential/index.js.map +1 -0
- package/lib/commonjs/credential/issuance/01-start-flow.js +2 -0
- package/lib/commonjs/credential/issuance/01-start-flow.js.map +1 -0
- package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js +26 -0
- package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js.map +1 -0
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js +119 -0
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -0
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +6 -0
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -0
- package/lib/commonjs/credential/issuance/05-authorize-access.js +63 -0
- package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -0
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +128 -0
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -0
- package/lib/commonjs/credential/issuance/07-confirm-credential.js +6 -0
- package/lib/commonjs/credential/issuance/07-confirm-credential.js.map +1 -0
- package/lib/commonjs/credential/issuance/const.js +9 -0
- package/lib/commonjs/credential/issuance/const.js.map +1 -0
- package/lib/commonjs/credential/issuance/index.js +34 -0
- package/lib/commonjs/credential/issuance/index.js.map +1 -0
- package/lib/commonjs/credential/presentation/01-start-flow.js +55 -0
- package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -0
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +32 -0
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -0
- package/lib/commonjs/credential/presentation/03-get-request-object.js +68 -0
- package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -0
- package/lib/commonjs/credential/presentation/04-send-authorization-response.js +139 -0
- package/lib/commonjs/credential/presentation/04-send-authorization-response.js.map +1 -0
- package/lib/commonjs/credential/presentation/index.js +34 -0
- package/lib/commonjs/credential/presentation/index.js.map +1 -0
- package/lib/commonjs/{rp → credential/presentation}/types.js +17 -34
- package/lib/commonjs/credential/presentation/types.js.map +1 -0
- package/lib/commonjs/index.js +10 -61
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/pid/index.js +1 -3
- package/lib/commonjs/pid/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/index.js +1 -1
- package/lib/commonjs/sd-jwt/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/types.js +1 -1
- package/lib/commonjs/sd-jwt/types.js.map +1 -1
- package/lib/commonjs/trust/chain.js +32 -4
- package/lib/commonjs/trust/chain.js.map +1 -1
- package/lib/commonjs/trust/index.js +105 -20
- package/lib/commonjs/trust/index.js.map +1 -1
- package/lib/commonjs/trust/types.js +54 -35
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/utils/crypto.js +5 -18
- package/lib/commonjs/utils/crypto.js.map +1 -1
- package/lib/commonjs/utils/errors.js +35 -4
- package/lib/commonjs/utils/errors.js.map +1 -1
- package/lib/commonjs/utils/misc.js +23 -0
- package/lib/commonjs/utils/misc.js.map +1 -0
- package/lib/commonjs/utils/par.js +86 -0
- package/lib/commonjs/utils/par.js.map +1 -0
- package/lib/module/credential/index.js +4 -0
- package/lib/module/credential/index.js.map +1 -0
- package/lib/module/credential/issuance/01-start-flow.js +2 -0
- package/lib/module/credential/issuance/01-start-flow.js.map +1 -0
- package/lib/module/credential/issuance/02-evaluate-issuer-trust.js +19 -0
- package/lib/module/credential/issuance/02-evaluate-issuer-trust.js.map +1 -0
- package/lib/module/credential/issuance/03-start-user-authorization.js +109 -0
- package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -0
- package/lib/module/credential/issuance/04-complete-user-authorization.js +2 -0
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -0
- package/lib/module/credential/issuance/05-authorize-access.js +55 -0
- package/lib/module/credential/issuance/05-authorize-access.js.map +1 -0
- package/lib/module/credential/issuance/06-obtain-credential.js +117 -0
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -0
- package/lib/module/credential/issuance/07-confirm-credential.js +2 -0
- package/lib/module/credential/issuance/07-confirm-credential.js.map +1 -0
- package/lib/module/credential/issuance/const.js +2 -0
- package/lib/module/credential/issuance/const.js.map +1 -0
- package/lib/module/credential/issuance/index.js +6 -0
- package/lib/module/credential/issuance/index.js.map +1 -0
- package/lib/module/credential/presentation/01-start-flow.js +46 -0
- package/lib/module/credential/presentation/01-start-flow.js.map +1 -0
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js +25 -0
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -0
- package/lib/module/credential/presentation/03-get-request-object.js +60 -0
- package/lib/module/credential/presentation/03-get-request-object.js.map +1 -0
- package/lib/module/credential/presentation/04-send-authorization-response.js +128 -0
- package/lib/module/credential/presentation/04-send-authorization-response.js.map +1 -0
- package/lib/module/credential/presentation/index.js +6 -0
- package/lib/module/credential/presentation/index.js.map +1 -0
- package/lib/module/credential/presentation/types.js +21 -0
- package/lib/module/credential/presentation/types.js.map +1 -0
- package/lib/module/index.js +4 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/pid/index.js +1 -2
- package/lib/module/pid/index.js.map +1 -1
- package/lib/module/sd-jwt/index.js +1 -1
- package/lib/module/sd-jwt/index.js.map +1 -1
- package/lib/module/sd-jwt/types.js +1 -1
- package/lib/module/sd-jwt/types.js.map +1 -1
- package/lib/module/trust/chain.js +30 -3
- package/lib/module/trust/chain.js.map +1 -1
- package/lib/module/trust/index.js +99 -16
- package/lib/module/trust/index.js.map +1 -1
- package/lib/module/trust/types.js +50 -31
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/utils/crypto.js +2 -15
- package/lib/module/utils/crypto.js.map +1 -1
- package/lib/module/utils/errors.js +35 -4
- package/lib/module/utils/errors.js.map +1 -1
- package/lib/module/utils/misc.js +17 -0
- package/lib/module/utils/misc.js.map +1 -0
- package/lib/module/utils/par.js +74 -0
- package/lib/module/utils/par.js.map +1 -0
- package/lib/typescript/credential/index.d.ts +4 -0
- package/lib/typescript/credential/index.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/01-start-flow.d.ts +11 -0
- package/lib/typescript/credential/issuance/01-start-flow.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts +18 -0
- package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +31 -0
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +16 -0
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts +26 -0
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +32 -0
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/07-confirm-credential.d.ts +11 -0
- package/lib/typescript/credential/issuance/07-confirm-credential.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/const.d.ts +2 -0
- package/lib/typescript/credential/issuance/const.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/index.d.ts +10 -0
- package/lib/typescript/credential/issuance/index.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/01-start-flow.d.ts +20 -0
- package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts +18 -0
- package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/03-get-request-object.d.ts +25 -0
- package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts +34 -0
- package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/index.d.ts +7 -0
- package/lib/typescript/credential/presentation/index.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/types.d.ts +49 -0
- package/lib/typescript/credential/presentation/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -5
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/pid/index.d.ts +1 -2
- package/lib/typescript/pid/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/index.d.ts +2 -2
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/types.d.ts +5 -5
- package/lib/typescript/trust/chain.d.ts +12 -3
- package/lib/typescript/trust/chain.d.ts.map +1 -1
- package/lib/typescript/trust/index.d.ts +198 -24
- package/lib/typescript/trust/index.d.ts.map +1 -1
- package/lib/typescript/trust/types.d.ts +1299 -623
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/utils/crypto.d.ts +1 -1
- package/lib/typescript/utils/crypto.d.ts.map +1 -1
- package/lib/typescript/utils/dpop.d.ts +2 -2
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/misc.d.ts +8 -0
- package/lib/typescript/utils/misc.d.ts.map +1 -0
- package/lib/typescript/utils/par.d.ts +68 -0
- package/lib/typescript/utils/par.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/credential/index.ts +4 -0
- package/src/credential/issuance/01-start-flow.ts +10 -0
- package/src/credential/issuance/02-evaluate-issuer-trust.ts +31 -0
- package/src/credential/issuance/03-start-user-authorization.ts +138 -0
- package/src/credential/issuance/04-complete-user-authorization.ts +17 -0
- package/src/credential/issuance/05-authorize-access.ts +92 -0
- package/src/credential/issuance/06-obtain-credential.ts +179 -0
- package/src/credential/issuance/07-confirm-credential.ts +14 -0
- package/src/credential/issuance/const.ts +2 -0
- package/src/credential/issuance/index.ts +32 -0
- package/src/credential/presentation/01-start-flow.ts +51 -0
- package/src/credential/presentation/02-evaluate-rp-trust.ts +33 -0
- package/src/credential/presentation/03-get-request-object.ts +85 -0
- package/src/credential/presentation/04-send-authorization-response.ts +168 -0
- package/src/credential/presentation/index.ts +26 -0
- package/src/credential/presentation/types.ts +27 -0
- package/src/index.ts +7 -28
- package/src/pid/index.ts +1 -2
- package/src/sd-jwt/index.ts +2 -2
- package/src/sd-jwt/types.ts +1 -1
- package/src/trust/chain.ts +45 -3
- package/src/trust/index.ts +136 -19
- package/src/trust/types.ts +57 -35
- package/src/utils/crypto.ts +2 -20
- package/src/utils/errors.ts +40 -8
- package/src/utils/misc.ts +23 -0
- package/src/utils/par.ts +103 -0
- package/lib/commonjs/pid/issuing.js +0 -276
- package/lib/commonjs/pid/issuing.js.map +0 -1
- package/lib/commonjs/rp/__test__/index.test.js +0 -172
- package/lib/commonjs/rp/__test__/index.test.js.map +0 -1
- package/lib/commonjs/rp/index.js +0 -239
- package/lib/commonjs/rp/index.js.map +0 -1
- package/lib/commonjs/rp/types.js.map +0 -1
- package/lib/module/pid/issuing.js +0 -266
- package/lib/module/pid/issuing.js.map +0 -1
- package/lib/module/rp/__test__/index.test.js +0 -168
- package/lib/module/rp/__test__/index.test.js.map +0 -1
- package/lib/module/rp/index.js +0 -228
- package/lib/module/rp/index.js.map +0 -1
- package/lib/module/rp/types.js +0 -36
- package/lib/module/rp/types.js.map +0 -1
- package/lib/typescript/pid/issuing.d.ts +0 -57
- package/lib/typescript/pid/issuing.d.ts.map +0 -1
- package/lib/typescript/rp/__test__/index.test.d.ts +0 -2
- package/lib/typescript/rp/__test__/index.test.d.ts.map +0 -1
- package/lib/typescript/rp/index.d.ts +0 -43
- package/lib/typescript/rp/index.d.ts.map +0 -1
- package/lib/typescript/rp/types.d.ts +0 -122
- package/lib/typescript/rp/types.d.ts.map +0 -1
- package/src/pid/issuing.ts +0 -405
- package/src/rp/__test__/index.test.ts +0 -250
- package/src/rp/index.ts +0 -287
- package/src/rp/types.ts +0 -42
package/src/sd-jwt/index.ts
CHANGED
@@ -135,7 +135,7 @@ export const disclose = async (
|
|
135
135
|
*
|
136
136
|
*
|
137
137
|
* @param token The encoded token that represents a valid sd-jwt for verifiable credentials
|
138
|
-
* @param publicKey The public key to validate the signature
|
138
|
+
* @param publicKey The single public key or an array of public keys to validate the signature.
|
139
139
|
* @param schema Schema to use to parse the SD-JWT
|
140
140
|
*
|
141
141
|
* @returns The parsed SD-JWT token and the parsed disclosures
|
@@ -143,7 +143,7 @@ export const disclose = async (
|
|
143
143
|
*/
|
144
144
|
export const verify = async <S extends z.AnyZodObject>(
|
145
145
|
token: string,
|
146
|
-
publicKey: JWK,
|
146
|
+
publicKey: JWK | JWK[],
|
147
147
|
schema: S
|
148
148
|
): Promise<{ sdJwt: z.infer<S>; disclosures: Disclosure[] }> => {
|
149
149
|
// get decoded data
|
package/src/sd-jwt/types.ts
CHANGED
package/src/trust/chain.ts
CHANGED
@@ -11,6 +11,7 @@ import { JWK } from "../utils/jwk";
|
|
11
11
|
import { IoWalletError } from "../utils/errors";
|
12
12
|
import * as z from "zod";
|
13
13
|
import type { JWTDecodeResult } from "@pagopa/io-react-native-jwt/lib/typescript/types";
|
14
|
+
import { getSignedEntityConfiguration, getSignedEntityStatement } from ".";
|
14
15
|
|
15
16
|
type ParsedToken = {
|
16
17
|
header: JWTDecodeResult["protectedHeader"];
|
@@ -51,12 +52,12 @@ const LastElementShape = z.union([
|
|
51
52
|
/**
|
52
53
|
* Validates a provided trust chain against a known trust
|
53
54
|
*
|
54
|
-
* @param trustAnchorEntity
|
55
|
-
* @param chain
|
55
|
+
* @param trustAnchorEntity The entity configuration of the known trust anchor
|
56
|
+
* @param chain The chain of statements to be validate
|
56
57
|
* @returns The list of parsed token representing the chain
|
57
58
|
* @throws {IoWalletError} If the chain is not valid
|
58
59
|
*/
|
59
|
-
export async function
|
60
|
+
export async function validateTrustChain(
|
60
61
|
trustAnchorEntity: TrustAnchorEntityConfiguration,
|
61
62
|
chain: string[]
|
62
63
|
): Promise<ParsedToken[]> {
|
@@ -107,3 +108,44 @@ export async function verifyTrustChain(
|
|
107
108
|
.map((args) => verify(...args))
|
108
109
|
);
|
109
110
|
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Given a trust chain, obtain a new trust chain by fetching each element's fresh version
|
114
|
+
*
|
115
|
+
* @param chain The original chain
|
116
|
+
* @param appFetch (optional) fetch api implementation
|
117
|
+
* @returns A list of signed token that reprensent the trust chain, in the same order of the provided chain
|
118
|
+
* @throws When an element of the chain fails to parse
|
119
|
+
*/
|
120
|
+
export function renewTrustChain(
|
121
|
+
chain: string[],
|
122
|
+
appFetch: GlobalFetch["fetch"] = fetch
|
123
|
+
) {
|
124
|
+
return Promise.all(
|
125
|
+
chain
|
126
|
+
// Decode each item to determine its shape
|
127
|
+
.map(decode)
|
128
|
+
.map(
|
129
|
+
(e) =>
|
130
|
+
[
|
131
|
+
EntityStatement.safeParse(e),
|
132
|
+
EntityConfiguration.safeParse(e),
|
133
|
+
] as const
|
134
|
+
)
|
135
|
+
// fetch the element according to its shape
|
136
|
+
.map(([es, ec], i) =>
|
137
|
+
ec.success
|
138
|
+
? getSignedEntityConfiguration(ec.data.payload.iss, { appFetch })
|
139
|
+
: es.success
|
140
|
+
? getSignedEntityStatement(es.data.payload.iss, es.data.payload.sub, {
|
141
|
+
appFetch,
|
142
|
+
})
|
143
|
+
: // if the element fail to parse in both EntityStatement and EntityConfiguration, raise an error
|
144
|
+
Promise.reject(
|
145
|
+
new IoWalletError(
|
146
|
+
`Cannot renew trust chain because the element #${i} failed to be parsed.`
|
147
|
+
)
|
148
|
+
)
|
149
|
+
)
|
150
|
+
);
|
151
|
+
}
|
package/src/trust/index.ts
CHANGED
@@ -5,11 +5,74 @@ import {
|
|
5
5
|
CredentialIssuerEntityConfiguration,
|
6
6
|
RelyingPartyEntityConfiguration,
|
7
7
|
EntityConfiguration,
|
8
|
+
EntityStatement,
|
8
9
|
} from "./types";
|
9
|
-
import {
|
10
|
-
import {
|
10
|
+
import { validateTrustChain, renewTrustChain } from "./chain";
|
11
|
+
import { hasStatus } from "../utils/misc";
|
11
12
|
|
12
|
-
export {
|
13
|
+
export type {
|
14
|
+
WalletProviderEntityConfiguration,
|
15
|
+
TrustAnchorEntityConfiguration,
|
16
|
+
CredentialIssuerEntityConfiguration,
|
17
|
+
RelyingPartyEntityConfiguration,
|
18
|
+
EntityConfiguration,
|
19
|
+
EntityStatement,
|
20
|
+
};
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Verify a given trust chain is actually valid.
|
24
|
+
* It can handle fast chain renewal, which means we try to fetch a fresh version of each statement.
|
25
|
+
*
|
26
|
+
* @param trustAnchorEntity The entity configuration of the known trust anchor
|
27
|
+
* @param chain The chain of statements to be validate
|
28
|
+
* @param options.renewOnFail Whether to renew the provided chain if the validation fails at first. Default: true
|
29
|
+
* @param options.appFetch Fetch api implementation. Default: the built-in implementation
|
30
|
+
* @returns The result of the chain validation
|
31
|
+
* @throws {IoWalletError} When either validation or renewal fail
|
32
|
+
*/
|
33
|
+
export async function verifyTrustChain(
|
34
|
+
trustAnchorEntity: TrustAnchorEntityConfiguration,
|
35
|
+
chain: string[],
|
36
|
+
{
|
37
|
+
appFetch = fetch,
|
38
|
+
renewOnFail = true,
|
39
|
+
}: { appFetch?: GlobalFetch["fetch"]; renewOnFail?: boolean } = {}
|
40
|
+
): Promise<ReturnType<typeof validateTrustChain>> {
|
41
|
+
try {
|
42
|
+
return validateTrustChain(trustAnchorEntity, chain);
|
43
|
+
} catch (error) {
|
44
|
+
if (renewOnFail) {
|
45
|
+
const renewedChain = await renewTrustChain(chain, appFetch);
|
46
|
+
return validateTrustChain(trustAnchorEntity, renewedChain);
|
47
|
+
} else {
|
48
|
+
throw error;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Fetch the signed entity configuration token for an entity
|
55
|
+
*
|
56
|
+
* @param entityBaseUrl The url of the entity to fetch
|
57
|
+
* @param param.appFetch (optional) fetch api implemention
|
58
|
+
* @returns The signed Entity Configuration token
|
59
|
+
*/
|
60
|
+
export async function getSignedEntityConfiguration(
|
61
|
+
entityBaseUrl: string,
|
62
|
+
{
|
63
|
+
appFetch = fetch,
|
64
|
+
}: {
|
65
|
+
appFetch?: GlobalFetch["fetch"];
|
66
|
+
} = {}
|
67
|
+
): Promise<string> {
|
68
|
+
const wellKnownUrl = `${entityBaseUrl}/.well-known/openid-federation`;
|
69
|
+
|
70
|
+
return await appFetch(wellKnownUrl, {
|
71
|
+
method: "GET",
|
72
|
+
})
|
73
|
+
.then(hasStatus(200))
|
74
|
+
.then((res) => res.text());
|
75
|
+
}
|
13
76
|
|
14
77
|
/**
|
15
78
|
* Fetch and parse the entity configuration document for a given federation entity.
|
@@ -77,24 +140,15 @@ async function fetchAndParseEntityConfiguration(
|
|
77
140
|
appFetch?: GlobalFetch["fetch"];
|
78
141
|
} = {}
|
79
142
|
) {
|
80
|
-
const
|
81
|
-
|
82
|
-
const response = await appFetch(wellKnownUrl, {
|
83
|
-
method: "GET",
|
143
|
+
const responseText = await getSignedEntityConfiguration(entityBaseUrl, {
|
144
|
+
appFetch,
|
84
145
|
});
|
85
146
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
payload: responseJwt.payload,
|
92
|
-
});
|
93
|
-
}
|
94
|
-
|
95
|
-
throw new IoWalletError(
|
96
|
-
`Unable to obtain Entity Configuration at ${wellKnownUrl}. Response code: ${response.status}`
|
97
|
-
);
|
147
|
+
const responseJwt = decodeJwt(responseText);
|
148
|
+
return schema.parse({
|
149
|
+
header: responseJwt.protectedHeader,
|
150
|
+
payload: responseJwt.payload,
|
151
|
+
});
|
98
152
|
}
|
99
153
|
|
100
154
|
export const getWalletProviderEntityConfiguration = (
|
@@ -142,3 +196,66 @@ export const getEntityConfiguration = (
|
|
142
196
|
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
143
197
|
) =>
|
144
198
|
fetchAndParseEntityConfiguration(entityBaseUrl, EntityConfiguration, options);
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Fetch and parse the entity statement document for a given federation entity.
|
202
|
+
*
|
203
|
+
* @param accreditationBodyBaseUrl The base url of the accreditaion body which holds and signs the required entity statement
|
204
|
+
* @param subordinatedEntityBaseUrl The url that identifies the subordinate entity
|
205
|
+
* @param options.appFetch An optional instance of the http client to be used.
|
206
|
+
* @returns The parsed entity configuration object
|
207
|
+
* @throws {IoWalletError} If the http request fails
|
208
|
+
* @throws Parse error if the document is not in the expected shape.
|
209
|
+
*/
|
210
|
+
export async function getEntityStatement(
|
211
|
+
accreditationBodyBaseUrl: string,
|
212
|
+
subordinatedEntityBaseUrl: string,
|
213
|
+
{
|
214
|
+
appFetch = fetch,
|
215
|
+
}: {
|
216
|
+
appFetch?: GlobalFetch["fetch"];
|
217
|
+
} = {}
|
218
|
+
) {
|
219
|
+
const responseText = await getSignedEntityStatement(
|
220
|
+
accreditationBodyBaseUrl,
|
221
|
+
subordinatedEntityBaseUrl,
|
222
|
+
{
|
223
|
+
appFetch,
|
224
|
+
}
|
225
|
+
);
|
226
|
+
|
227
|
+
const responseJwt = decodeJwt(responseText);
|
228
|
+
return EntityStatement.parse({
|
229
|
+
header: responseJwt.protectedHeader,
|
230
|
+
payload: responseJwt.payload,
|
231
|
+
});
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Fetch the entity statement document for a given federation entity.
|
236
|
+
*
|
237
|
+
* @param accreditationBodyBaseUrl The base url of the accreditaion body which holds and signs the required entity statement
|
238
|
+
* @param subordinatedEntityBaseUrl The url that identifies the subordinate entity
|
239
|
+
* @param options.appFetch An optional instance of the http client to be used.
|
240
|
+
* @returns The signed entity statement token
|
241
|
+
* @throws {IoWalletError} If the http request fails
|
242
|
+
*/
|
243
|
+
export async function getSignedEntityStatement(
|
244
|
+
accreditationBodyBaseUrl: string,
|
245
|
+
subordinatedEntityBaseUrl: string,
|
246
|
+
{
|
247
|
+
appFetch = fetch,
|
248
|
+
}: {
|
249
|
+
appFetch?: GlobalFetch["fetch"];
|
250
|
+
} = {}
|
251
|
+
) {
|
252
|
+
const url = `${accreditationBodyBaseUrl}/fetch?${new URLSearchParams({
|
253
|
+
sub: subordinatedEntityBaseUrl,
|
254
|
+
})}`;
|
255
|
+
|
256
|
+
return await appFetch(url, {
|
257
|
+
method: "GET",
|
258
|
+
})
|
259
|
+
.then(hasStatus(200))
|
260
|
+
.then((res) => res.text());
|
261
|
+
}
|
package/src/trust/types.ts
CHANGED
@@ -5,6 +5,15 @@ import * as z from "zod";
|
|
5
5
|
export const TrustMark = z.object({ id: z.string(), trust_mark: z.string() });
|
6
6
|
export type TrustMark = z.infer<typeof TrustMark>;
|
7
7
|
|
8
|
+
const RelyingPartyMetadata = z.object({
|
9
|
+
application_type: z.string().optional(),
|
10
|
+
client_id: z.string().optional(),
|
11
|
+
client_name: z.string().optional(),
|
12
|
+
jwks: z.object({ keys: z.array(JWK) }),
|
13
|
+
contacts: z.array(z.string()).optional(),
|
14
|
+
});
|
15
|
+
//.passthrough();
|
16
|
+
|
8
17
|
// Display metadata for a credential, used by the issuer to
|
9
18
|
// instruct the Wallet Solution on how to render the credential correctly
|
10
19
|
type CredentialDisplayMetadata = z.infer<typeof CredentialDisplayMetadata>;
|
@@ -19,13 +28,28 @@ const CredentialDisplayMetadata = z.object({
|
|
19
28
|
text_color: z.string(),
|
20
29
|
});
|
21
30
|
|
31
|
+
type CredentialDefinitionMetadata = z.infer<
|
32
|
+
typeof CredentialDefinitionMetadata
|
33
|
+
>;
|
34
|
+
const CredentialDefinitionMetadata = z.object({
|
35
|
+
type: z.array(z.string()),
|
36
|
+
credentialSubject: z.record(
|
37
|
+
z.object({
|
38
|
+
mandatory: z.boolean(),
|
39
|
+
display: z.array(z.object({ name: z.string(), locale: z.string() })),
|
40
|
+
})
|
41
|
+
),
|
42
|
+
});
|
43
|
+
|
22
44
|
// Metadata for a credentia which i supported by a Issuer
|
23
45
|
type SupportedCredentialMetadata = z.infer<typeof SupportedCredentialMetadata>;
|
24
46
|
const SupportedCredentialMetadata = z.object({
|
47
|
+
id: z.string(),
|
25
48
|
format: z.literal("vc+sd-jwt"),
|
26
49
|
cryptographic_binding_methods_supported: z.array(z.string()),
|
27
50
|
cryptographic_suites_supported: z.array(z.string()),
|
28
51
|
display: z.array(CredentialDisplayMetadata),
|
52
|
+
credential_definition: CredentialDefinitionMetadata,
|
29
53
|
});
|
30
54
|
|
31
55
|
export type EntityStatement = z.infer<typeof EntityStatement>;
|
@@ -54,6 +78,20 @@ export const EntityConfigurationHeader = z.object({
|
|
54
78
|
kid: z.string(),
|
55
79
|
});
|
56
80
|
|
81
|
+
const FederationEntityMetadata = z
|
82
|
+
.object({
|
83
|
+
federation_fetch_endpoint: z.string().optional(),
|
84
|
+
federation_list_endpoint: z.string().optional(),
|
85
|
+
federation_resolve_endpoint: z.string().optional(),
|
86
|
+
federation_trust_mark_status_endpoint: z.string().optional(),
|
87
|
+
federation_trust_mark_list_endpoint: z.string().optional(),
|
88
|
+
homepage_uri: z.string().optional(),
|
89
|
+
policy_uri: z.string().optional(),
|
90
|
+
logo_uri: z.string().optional(),
|
91
|
+
contacts: z.array(z.string()).optional(),
|
92
|
+
})
|
93
|
+
.passthrough();
|
94
|
+
|
57
95
|
// Structuire common to every Entity Configuration document
|
58
96
|
const BaseEntityConfiguration = z.object({
|
59
97
|
header: EntityConfigurationHeader,
|
@@ -68,19 +106,7 @@ const BaseEntityConfiguration = z.object({
|
|
68
106
|
}),
|
69
107
|
metadata: z
|
70
108
|
.object({
|
71
|
-
federation_entity:
|
72
|
-
.object({
|
73
|
-
federation_fetch_endpoint: z.string().optional(),
|
74
|
-
federation_list_endpoint: z.string().optional(),
|
75
|
-
federation_resolve_endpoint: z.string().optional(),
|
76
|
-
federation_trust_mark_status_endpoint: z.string().optional(),
|
77
|
-
federation_trust_mark_list_endpoint: z.string().optional(),
|
78
|
-
homepage_uri: z.string().optional(),
|
79
|
-
policy_uri: z.string().optional(),
|
80
|
-
logo_uri: z.string().optional(),
|
81
|
-
contacts: z.array(z.string()).optional(),
|
82
|
-
})
|
83
|
-
.passthrough(),
|
109
|
+
federation_entity: FederationEntityMetadata,
|
84
110
|
})
|
85
111
|
.passthrough(),
|
86
112
|
authority_hints: z.array(z.string()).optional(),
|
@@ -113,6 +139,24 @@ export const CredentialIssuerEntityConfiguration = BaseEntityConfiguration.and(
|
|
113
139
|
credentials_supported: z.array(SupportedCredentialMetadata),
|
114
140
|
jwks: z.object({ keys: z.array(JWK) }),
|
115
141
|
}),
|
142
|
+
/** Credential Issuers act as Relying Party
|
143
|
+
when they require the presentation of other credentials.
|
144
|
+
This does not apply for PID issuance, which requires CIE authz. */
|
145
|
+
wallet_relying_party: RelyingPartyMetadata.optional(),
|
146
|
+
}),
|
147
|
+
}),
|
148
|
+
})
|
149
|
+
);
|
150
|
+
|
151
|
+
// Entity configuration for a Relying Party
|
152
|
+
export type RelyingPartyEntityConfiguration = z.infer<
|
153
|
+
typeof RelyingPartyEntityConfiguration
|
154
|
+
>;
|
155
|
+
export const RelyingPartyEntityConfiguration = BaseEntityConfiguration.and(
|
156
|
+
z.object({
|
157
|
+
payload: z.object({
|
158
|
+
metadata: z.object({
|
159
|
+
wallet_relying_party: RelyingPartyMetadata,
|
116
160
|
}),
|
117
161
|
}),
|
118
162
|
})
|
@@ -145,28 +189,6 @@ export const WalletProviderEntityConfiguration = BaseEntityConfiguration.and(
|
|
145
189
|
})
|
146
190
|
);
|
147
191
|
|
148
|
-
// Entity configuration for a Relying Party
|
149
|
-
export type RelyingPartyEntityConfiguration = z.infer<
|
150
|
-
typeof RelyingPartyEntityConfiguration
|
151
|
-
>;
|
152
|
-
export const RelyingPartyEntityConfiguration = BaseEntityConfiguration.and(
|
153
|
-
z.object({
|
154
|
-
payload: z.object({
|
155
|
-
metadata: z.object({
|
156
|
-
wallet_relying_party: z
|
157
|
-
.object({
|
158
|
-
application_type: z.string().optional(),
|
159
|
-
client_id: z.string().optional(),
|
160
|
-
client_name: z.string().optional(),
|
161
|
-
jwks: z.object({ keys: z.array(JWK) }),
|
162
|
-
contacts: z.array(z.string()).optional(),
|
163
|
-
})
|
164
|
-
.passthrough(),
|
165
|
-
}),
|
166
|
-
}),
|
167
|
-
})
|
168
|
-
);
|
169
|
-
|
170
192
|
// Maps any entity configuration by the union of every possible shapes
|
171
193
|
export type EntityConfiguration = z.infer<typeof EntityConfiguration>;
|
172
194
|
export const EntityConfiguration = z.union(
|
package/src/utils/crypto.ts
CHANGED
@@ -46,24 +46,6 @@ export const createCryptoContextFor = (keytag: string): CryptoContext => {
|
|
46
46
|
};
|
47
47
|
};
|
48
48
|
|
49
|
-
// Wraps finally for async expressions
|
50
|
-
const asyncFinally =
|
51
|
-
<A extends Array<unknown>, R>(
|
52
|
-
fn: (...args: A) => Promise<R>,
|
53
|
-
onFinally: () => void | Promise<void>
|
54
|
-
) =>
|
55
|
-
async (...args: A): Promise<R> => {
|
56
|
-
try {
|
57
|
-
return await fn(...args);
|
58
|
-
// ^^^^^ return await is usually to be avoided,
|
59
|
-
// in this case is needed for the finally{} statement to be executed correctly
|
60
|
-
} catch (error) {
|
61
|
-
throw error;
|
62
|
-
} finally {
|
63
|
-
await onFinally();
|
64
|
-
}
|
65
|
-
};
|
66
|
-
|
67
49
|
/**
|
68
50
|
* Executes the input function injecting an ephemeral crypto context.
|
69
51
|
* An ephemeral crypto context is a context which is bound to a key
|
@@ -72,12 +54,12 @@ const asyncFinally =
|
|
72
54
|
* @param fn The procedure to be executed
|
73
55
|
* @returns The returned value of the input procedure.
|
74
56
|
*/
|
75
|
-
export const
|
57
|
+
export const withEphemeralKey = async <R>(
|
76
58
|
fn: (ephemeralContext: CryptoContext) => Promise<R>
|
77
59
|
): Promise<R> => {
|
78
60
|
// Use an ephemeral key to be destroyed after use
|
79
61
|
const keytag = `ephemeral-${uuid.v4()}`;
|
80
62
|
await generate(keytag);
|
81
63
|
const ephemeralContext = createCryptoContextFor(keytag);
|
82
|
-
return
|
64
|
+
return fn(ephemeralContext).finally(() => deleteKey(keytag));
|
83
65
|
};
|
package/src/utils/errors.ts
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
/**
|
2
|
+
* utility to format a set of attributes into an error message string
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
* // returns "foo=value bar=(list, item)"
|
6
|
+
* serializeAttrs({ foo: "value", bar: ["list", "item"] })
|
7
|
+
*
|
8
|
+
* @param attrs A key value record set
|
9
|
+
* @returns a human-readable serialization of the set
|
10
|
+
*/
|
11
|
+
const serializeAttrs = (attrs: Record<string, string | string>): string =>
|
12
|
+
Object.entries(attrs)
|
13
|
+
.map(([k, v]) => [k, Array.isArray(v) ? `(${v.join(", ")})` : v])
|
14
|
+
.map((_) => _.join("="))
|
15
|
+
.join(" ");
|
16
|
+
|
1
17
|
/**
|
2
18
|
* A generic Error that all other io-wallet specific Error subclasses extend.
|
3
19
|
*
|
@@ -42,8 +58,12 @@ export class ValidationFailed extends IoWalletError {
|
|
42
58
|
/** Reason code for the validation failure. */
|
43
59
|
reason: string;
|
44
60
|
|
45
|
-
constructor(
|
46
|
-
|
61
|
+
constructor(
|
62
|
+
message: string,
|
63
|
+
claim: string = "unspecified",
|
64
|
+
reason: string = "unspecified"
|
65
|
+
) {
|
66
|
+
super(serializeAttrs({ message, claim, reason }));
|
47
67
|
this.claim = claim;
|
48
68
|
this.reason = reason;
|
49
69
|
}
|
@@ -66,8 +86,12 @@ export class WalletInstanceAttestationIssuingError extends IoWalletError {
|
|
66
86
|
/** Reason code for the validation failure. */
|
67
87
|
reason: string;
|
68
88
|
|
69
|
-
constructor(
|
70
|
-
|
89
|
+
constructor(
|
90
|
+
message: string,
|
91
|
+
claim: string = "unspecified",
|
92
|
+
reason: string = "unspecified"
|
93
|
+
) {
|
94
|
+
super(serializeAttrs({ message, claim, reason }));
|
71
95
|
this.claim = claim;
|
72
96
|
this.reason = reason;
|
73
97
|
}
|
@@ -90,8 +114,12 @@ export class AuthRequestDecodeError extends IoWalletError {
|
|
90
114
|
/** Reason code for the validation failure. */
|
91
115
|
reason: string;
|
92
116
|
|
93
|
-
constructor(
|
94
|
-
|
117
|
+
constructor(
|
118
|
+
message: string,
|
119
|
+
claim: string = "unspecified",
|
120
|
+
reason: string = "unspecified"
|
121
|
+
) {
|
122
|
+
super(serializeAttrs({ message, claim, reason }));
|
95
123
|
this.claim = claim;
|
96
124
|
this.reason = reason;
|
97
125
|
}
|
@@ -114,8 +142,12 @@ export class PidIssuingError extends IoWalletError {
|
|
114
142
|
/** Reason code for the validation failure. */
|
115
143
|
reason: string;
|
116
144
|
|
117
|
-
constructor(
|
118
|
-
|
145
|
+
constructor(
|
146
|
+
message: string,
|
147
|
+
claim: string = "unspecified",
|
148
|
+
reason: string = "unspecified"
|
149
|
+
) {
|
150
|
+
super(serializeAttrs({ message, claim, reason }));
|
119
151
|
this.claim = claim;
|
120
152
|
this.reason = reason;
|
121
153
|
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { IoWalletError } from "./errors";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Check if a response is in the expected status, other
|
5
|
+
* @param status The expected status
|
6
|
+
* @returns The given response object
|
7
|
+
*/
|
8
|
+
export const hasStatus =
|
9
|
+
(status: number) =>
|
10
|
+
(res: Response): Response => {
|
11
|
+
if (res.status !== status) {
|
12
|
+
throw new IoWalletError(
|
13
|
+
`Http request failed. Expected ${status}, got ${res.status}, url: ${res.url}`
|
14
|
+
);
|
15
|
+
}
|
16
|
+
return res;
|
17
|
+
};
|
18
|
+
|
19
|
+
// extract a type from an async function output
|
20
|
+
// helpful to bind the input of a function to the output of another
|
21
|
+
export type Out<FN> = FN extends (...args: any[]) => Promise<any>
|
22
|
+
? Awaited<ReturnType<FN>>
|
23
|
+
: never;
|
package/src/utils/par.ts
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
import {
|
2
|
+
sha256ToBase64,
|
3
|
+
type CryptoContext,
|
4
|
+
SignJWT,
|
5
|
+
} from "@pagopa/io-react-native-jwt";
|
6
|
+
import uuid from "react-native-uuid";
|
7
|
+
import * as z from "zod";
|
8
|
+
import * as WalletInstanceAttestation from "../wallet-instance-attestation";
|
9
|
+
import { hasStatus } from "./misc";
|
10
|
+
|
11
|
+
export type AuthorizationDetail = z.infer<typeof AuthorizationDetail>;
|
12
|
+
export const AuthorizationDetail = z.object({
|
13
|
+
credential_definition: z.object({
|
14
|
+
type: z.string(),
|
15
|
+
}),
|
16
|
+
format: z.literal("vc+sd-jwt"),
|
17
|
+
type: z.literal("openid_credential"),
|
18
|
+
});
|
19
|
+
|
20
|
+
export type AuthorizationDetails = z.infer<typeof AuthorizationDetails>;
|
21
|
+
export const AuthorizationDetails = z.array(AuthorizationDetail);
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Make a PAR request to the issuer and return the response url
|
25
|
+
*/
|
26
|
+
export const makeParRequest =
|
27
|
+
({
|
28
|
+
wiaCryptoContext,
|
29
|
+
appFetch = fetch,
|
30
|
+
}: {
|
31
|
+
wiaCryptoContext: CryptoContext;
|
32
|
+
appFetch?: GlobalFetch["fetch"];
|
33
|
+
}) =>
|
34
|
+
async (
|
35
|
+
clientId: string,
|
36
|
+
codeVerifier: string,
|
37
|
+
walletProviderBaseUrl: string,
|
38
|
+
parEndpoint: string,
|
39
|
+
walletInstanceAttestation: string,
|
40
|
+
authorizationDetails: AuthorizationDetails,
|
41
|
+
assertionType: string
|
42
|
+
): Promise<string> => {
|
43
|
+
const wiaPublicKey = await wiaCryptoContext.getPublicKey();
|
44
|
+
|
45
|
+
const parUrl = new URL(parEndpoint);
|
46
|
+
const aud = `${parUrl.protocol}//${parUrl.hostname}`;
|
47
|
+
|
48
|
+
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
|
49
|
+
.payload.cnf.jwk.kid;
|
50
|
+
|
51
|
+
/** A code challenge is provided so that the PAR is bound
|
52
|
+
to the subsequent authorization code request
|
53
|
+
@see https://datatracker.ietf.org/doc/html/rfc9126#name-request */
|
54
|
+
const codeChallengeMethod = "s256";
|
55
|
+
const codeChallenge = await sha256ToBase64(codeVerifier);
|
56
|
+
|
57
|
+
/** The PAR request token is signed used the Wallet Instance Attestation key.
|
58
|
+
The signature can be verified by reading the public key from the key set shippet
|
59
|
+
with the it will ship the Wallet Instance Attestation.
|
60
|
+
The key is matched by its kid */
|
61
|
+
const signedJwtForPar = await new SignJWT(wiaCryptoContext)
|
62
|
+
.setProtectedHeader({
|
63
|
+
kid: wiaPublicKey.kid,
|
64
|
+
})
|
65
|
+
.setPayload({
|
66
|
+
iss,
|
67
|
+
aud,
|
68
|
+
jti: `${uuid.v4()}`,
|
69
|
+
client_assertion_type: assertionType,
|
70
|
+
authorization_details: authorizationDetails,
|
71
|
+
response_type: "code",
|
72
|
+
redirect_uri: walletProviderBaseUrl,
|
73
|
+
state: `${uuid.v4()}`,
|
74
|
+
client_id: clientId,
|
75
|
+
code_challenge_method: codeChallengeMethod,
|
76
|
+
code_challenge: codeChallenge,
|
77
|
+
})
|
78
|
+
.setIssuedAt()
|
79
|
+
.setExpirationTime("1h")
|
80
|
+
.sign();
|
81
|
+
|
82
|
+
/** The request body for the Pushed Authorization Request */
|
83
|
+
var formBody = new URLSearchParams({
|
84
|
+
response_type: "code",
|
85
|
+
client_id: clientId,
|
86
|
+
code_challenge: codeChallenge,
|
87
|
+
code_challenge_method: "S256",
|
88
|
+
client_assertion_type: assertionType,
|
89
|
+
client_assertion: walletInstanceAttestation,
|
90
|
+
request: signedJwtForPar,
|
91
|
+
});
|
92
|
+
|
93
|
+
return await appFetch(parEndpoint, {
|
94
|
+
method: "POST",
|
95
|
+
headers: {
|
96
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
97
|
+
},
|
98
|
+
body: formBody.toString(),
|
99
|
+
})
|
100
|
+
.then(hasStatus(201))
|
101
|
+
.then((res) => res.json())
|
102
|
+
.then((result) => result.request_uri);
|
103
|
+
};
|