@pagopa/io-react-native-wallet 0.4.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -19
- package/lib/commonjs/index.js +16 -23
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/pid/index.js +3 -8
- package/lib/commonjs/pid/index.js.map +1 -1
- package/lib/commonjs/pid/issuing.js +153 -176
- package/lib/commonjs/pid/issuing.js.map +1 -1
- package/lib/commonjs/rp/__test__/index.test.js +7 -5
- package/lib/commonjs/rp/__test__/index.test.js.map +1 -1
- package/lib/commonjs/rp/index.js +145 -155
- package/lib/commonjs/rp/index.js.map +1 -1
- package/lib/commonjs/rp/types.js +1 -21
- package/lib/commonjs/rp/types.js.map +1 -1
- package/lib/commonjs/trust/index.js +24 -5
- package/lib/commonjs/trust/index.js.map +1 -1
- package/lib/commonjs/trust/types.js +102 -9
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/utils/crypto.js +46 -0
- package/lib/commonjs/utils/crypto.js.map +1 -0
- package/lib/commonjs/utils/dpop.js +14 -7
- package/lib/commonjs/utils/dpop.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/index.js +3 -3
- package/lib/commonjs/wallet-instance-attestation/issuing.js +42 -60
- package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/module/index.js +4 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/pid/index.js +1 -1
- package/lib/module/pid/index.js.map +1 -1
- package/lib/module/pid/issuing.js +152 -180
- package/lib/module/pid/issuing.js.map +1 -1
- package/lib/module/rp/__test__/index.test.js +3 -3
- package/lib/module/rp/__test__/index.test.js.map +1 -1
- package/lib/module/rp/index.js +141 -154
- package/lib/module/rp/index.js.map +1 -1
- package/lib/module/rp/types.js +0 -20
- package/lib/module/rp/types.js.map +1 -1
- package/lib/module/trust/index.js +19 -5
- package/lib/module/trust/index.js.map +1 -1
- package/lib/module/trust/types.js +100 -7
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/utils/crypto.js +40 -0
- package/lib/module/utils/crypto.js.map +1 -0
- package/lib/module/utils/dpop.js +13 -5
- package/lib/module/utils/dpop.js.map +1 -1
- package/lib/module/wallet-instance-attestation/index.js +2 -2
- package/lib/module/wallet-instance-attestation/index.js.map +1 -1
- package/lib/module/wallet-instance-attestation/issuing.js +40 -58
- package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/typescript/index.d.ts +4 -6
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/pid/index.d.ts +1 -1
- package/lib/typescript/pid/index.d.ts.map +1 -1
- package/lib/typescript/pid/issuing.d.ts +43 -88
- package/lib/typescript/pid/issuing.d.ts.map +1 -1
- package/lib/typescript/rp/index.d.ts +41 -87
- package/lib/typescript/rp/index.d.ts.map +1 -1
- package/lib/typescript/rp/types.d.ts +10 -906
- package/lib/typescript/rp/types.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/index.d.ts +1 -1
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/trust/index.d.ts +806 -3
- package/lib/typescript/trust/index.d.ts.map +1 -1
- package/lib/typescript/trust/types.d.ts +9655 -297
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/utils/crypto.d.ts +10 -0
- package/lib/typescript/utils/crypto.d.ts.map +1 -0
- package/lib/typescript/utils/dpop.d.ts +10 -2
- package/lib/typescript/utils/dpop.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/index.d.ts +2 -2
- package/lib/typescript/wallet-instance-attestation/index.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/issuing.d.ts +18 -31
- package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/types.d.ts +4 -4
- package/package.json +2 -2
- package/src/index.ts +14 -13
- package/src/pid/index.ts +1 -1
- package/src/pid/issuing.ts +233 -232
- package/src/rp/__test__/index.test.ts +3 -3
- package/src/rp/index.ts +172 -194
- package/src/rp/types.ts +0 -24
- package/src/sd-jwt/index.ts +1 -1
- package/src/trust/index.ts +106 -5
- package/src/trust/types.ts +152 -34
- package/src/utils/crypto.ts +41 -0
- package/src/utils/dpop.ts +17 -7
- package/src/wallet-instance-attestation/index.ts +2 -2
- package/src/wallet-instance-attestation/issuing.ts +51 -63
- package/lib/commonjs/pid/metadata.js +0 -49
- package/lib/commonjs/pid/metadata.js.map +0 -1
- package/lib/module/pid/metadata.js +0 -41
- package/lib/module/pid/metadata.js.map +0 -1
- package/lib/typescript/pid/metadata.d.ts +0 -482
- package/lib/typescript/pid/metadata.d.ts.map +0 -1
- package/src/pid/metadata.ts +0 -46
package/src/pid/issuing.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
decode as decodeJwt,
|
|
3
|
-
verify as verifyJwt,
|
|
4
2
|
sha256ToBase64,
|
|
3
|
+
type CryptoContext,
|
|
4
|
+
SignJWT,
|
|
5
|
+
thumbprint,
|
|
5
6
|
} from "@pagopa/io-react-native-jwt";
|
|
6
|
-
|
|
7
|
-
import { SignJWT, thumbprint } from "@pagopa/io-react-native-jwt";
|
|
8
7
|
import { JWK } from "../utils/jwk";
|
|
9
8
|
import uuid from "react-native-uuid";
|
|
10
|
-
import { PidIssuingError
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
|
|
9
|
+
import { PidIssuingError } from "../utils/errors";
|
|
10
|
+
import { createDPopToken } from "../utils/dpop";
|
|
11
|
+
import { CredentialIssuerEntityConfiguration } from "../trust/types";
|
|
12
|
+
import * as WalletInstanceAttestation from "../wallet-instance-attestation";
|
|
13
|
+
import { generate, deleteKey } from "@pagopa/io-react-native-crypto";
|
|
14
|
+
import { SdJwt } from ".";
|
|
15
|
+
import { createCryptoContextFor } from "../utils/crypto";
|
|
16
16
|
// This is a temporary type that will be used for demo purposes only
|
|
17
17
|
export type CieData = {
|
|
18
18
|
birthDate: string;
|
|
@@ -21,7 +21,15 @@ export type CieData = {
|
|
|
21
21
|
surname: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export type
|
|
24
|
+
export type AuthorizationConf = {
|
|
25
|
+
accessToken: string;
|
|
26
|
+
nonce: string;
|
|
27
|
+
clientId: string;
|
|
28
|
+
authorizationCode: string;
|
|
29
|
+
codeVerifier: string;
|
|
30
|
+
walletProviderBaseUrl: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
25
33
|
export type PidResponse = {
|
|
26
34
|
credential: string;
|
|
27
35
|
c_nonce: string;
|
|
@@ -29,111 +37,87 @@ export type PidResponse = {
|
|
|
29
37
|
format: string;
|
|
30
38
|
};
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
walletProviderBaseUrl: string;
|
|
35
|
-
walletInstanceAttestation: string;
|
|
36
|
-
codeVerifier: string;
|
|
37
|
-
clientId: string;
|
|
38
|
-
state: string;
|
|
39
|
-
authorizationCode: string;
|
|
40
|
-
appFetch: GlobalFetch["fetch"];
|
|
40
|
+
const assertionType =
|
|
41
|
+
"urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation";
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Make a PAR request to the PID issuer and return the response url
|
|
45
|
+
*/
|
|
46
|
+
const getPar =
|
|
47
|
+
({
|
|
48
|
+
wiaCryptoContext,
|
|
49
|
+
appFetch = fetch,
|
|
50
|
+
}: {
|
|
51
|
+
wiaCryptoContext: CryptoContext;
|
|
52
|
+
appFetch?: GlobalFetch["fetch"];
|
|
53
|
+
}) =>
|
|
54
|
+
async (
|
|
46
55
|
clientId: string,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
codeVerifier: string,
|
|
57
|
+
walletProviderBaseUrl: string,
|
|
58
|
+
pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration,
|
|
59
|
+
walletInstanceAttestation: string
|
|
60
|
+
): Promise<string> => {
|
|
61
|
+
// Calculate the thumbprint of the public key of the Wallet Instance Attestation.
|
|
62
|
+
// The PAR request token is signed used the Wallet Instance Attestation key.
|
|
63
|
+
// The signature can be verified by reading the public key from the key set shippet with the it will ship the Wallet Instance Attestation;
|
|
64
|
+
// key is matched by its kid, which is supposed to be the thumbprint of its public key.
|
|
65
|
+
const keyThumbprint = await wiaCryptoContext
|
|
66
|
+
.getPublicKey()
|
|
67
|
+
.then(JWK.parse)
|
|
68
|
+
.then(thumbprint);
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
* @function
|
|
63
|
-
* @param jwk The wallet instance attestation public JWK
|
|
64
|
-
*
|
|
65
|
-
* @returns Unsigned jwt
|
|
66
|
-
*
|
|
67
|
-
*/
|
|
68
|
-
async getUnsignedJwtForPar(jwk: JWK): Promise<string> {
|
|
69
|
-
const parsedJwk = JWK.parse(jwk);
|
|
70
|
-
const keyThumbprint = await thumbprint(parsedJwk);
|
|
71
|
-
const publicKey = { ...parsedJwk, kid: keyThumbprint };
|
|
72
|
-
const codeChallenge = await sha256ToBase64(this.codeVerifier);
|
|
70
|
+
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
|
|
71
|
+
.payload.cnf.jwk.kid;
|
|
73
72
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
authorization_details: [
|
|
78
|
-
{
|
|
79
|
-
credentialDefinition: {
|
|
80
|
-
type: ["eu.eudiw.pid.it"],
|
|
81
|
-
},
|
|
82
|
-
format: "vc+sd-jwt",
|
|
83
|
-
type: "type",
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
response_type: "code",
|
|
87
|
-
code_challenge_method: "s256",
|
|
88
|
-
redirect_uri: this.walletProviderBaseUrl,
|
|
89
|
-
state: this.state,
|
|
90
|
-
client_id: this.clientId,
|
|
91
|
-
code_challenge: codeChallenge,
|
|
92
|
-
})
|
|
73
|
+
const codeChallenge = await sha256ToBase64(codeVerifier);
|
|
74
|
+
|
|
75
|
+
const signedJwtForPar = await new SignJWT(wiaCryptoContext)
|
|
93
76
|
.setProtectedHeader({
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
kid: keyThumbprint,
|
|
78
|
+
})
|
|
79
|
+
.setPayload({
|
|
80
|
+
iss,
|
|
81
|
+
aud: pidProviderEntityConfiguration.payload.iss,
|
|
82
|
+
jti: `${uuid.v4()}`,
|
|
83
|
+
client_assertion_type: assertionType,
|
|
84
|
+
authorization_details: [
|
|
85
|
+
{
|
|
86
|
+
credential_definition: {
|
|
87
|
+
type: "PersonIdentificationData",
|
|
88
|
+
},
|
|
89
|
+
format: "vc+sd-jwt",
|
|
90
|
+
type: "openid_credential",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
response_type: "code",
|
|
94
|
+
code_challenge_method: "s256",
|
|
95
|
+
redirect_uri: walletProviderBaseUrl,
|
|
96
|
+
state: `${uuid.v4()}`,
|
|
97
|
+
client_id: clientId,
|
|
98
|
+
code_challenge: codeChallenge,
|
|
96
99
|
})
|
|
97
100
|
.setIssuedAt()
|
|
98
101
|
.setExpirationTime("1h")
|
|
99
|
-
.
|
|
102
|
+
.sign();
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Make a PAR request to the PID issuer and return the response url
|
|
106
|
-
*
|
|
107
|
-
* @function
|
|
108
|
-
* @param unsignedJwtForPar The unsigned JWT for PAR
|
|
109
|
-
* @param signature The JWT for PAR signature
|
|
110
|
-
*
|
|
111
|
-
* @returns Unsigned PAR url
|
|
112
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
async getPar(unsignedJwtForPar: string, signature: string): Promise<string> {
|
|
115
|
-
const codeChallenge = await sha256ToBase64(this.codeVerifier);
|
|
116
|
-
const signedJwtForPar = await SignJWT.appendSignature(
|
|
117
|
-
unsignedJwtForPar,
|
|
118
|
-
signature
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
const parUrl = new URL("/as/par", this.pidProviderBaseUrl).href;
|
|
104
|
+
const parUrl =
|
|
105
|
+
pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
|
|
106
|
+
.pushed_authorization_request_endpoint;
|
|
122
107
|
|
|
123
108
|
const requestBody = {
|
|
124
109
|
response_type: "code",
|
|
125
|
-
client_id:
|
|
110
|
+
client_id: clientId,
|
|
126
111
|
code_challenge: codeChallenge,
|
|
127
112
|
code_challenge_method: "S256",
|
|
128
|
-
client_assertion_type:
|
|
129
|
-
|
|
130
|
-
client_assertion: this.walletInstanceAttestation,
|
|
113
|
+
client_assertion_type: assertionType,
|
|
114
|
+
client_assertion: walletInstanceAttestation,
|
|
131
115
|
request: signedJwtForPar,
|
|
132
116
|
};
|
|
133
117
|
|
|
134
118
|
var formBody = new URLSearchParams(requestBody);
|
|
135
119
|
|
|
136
|
-
const response = await
|
|
120
|
+
const response = await appFetch(parUrl, {
|
|
137
121
|
method: "POST",
|
|
138
122
|
headers: {
|
|
139
123
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -149,61 +133,75 @@ export class Issuing {
|
|
|
149
133
|
throw new PidIssuingError(
|
|
150
134
|
`Unable to obtain PAR. Response code: ${await response.text()}`
|
|
151
135
|
);
|
|
152
|
-
}
|
|
136
|
+
};
|
|
153
137
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
138
|
+
/**
|
|
139
|
+
* Start the issuing flow by generating an authorization request to the PID Provider. Obtain from the PID Provider an access token to be used to complete the issuing flow.
|
|
140
|
+
*
|
|
141
|
+
* @param params.wiaCryptoContext The key pair associated with the WIA. Will be use to prove the ownership of the attestation.
|
|
142
|
+
* @param params.appFetch (optional) Http client
|
|
143
|
+
* @param walletInstanceAttestation Wallet Instance Attestation token.
|
|
144
|
+
* @param walletProviderBaseUrl Base url for the Wallet Provider
|
|
145
|
+
* @param pidProviderEntityConfiguration The Entity Configuration of the PID Provider, from which discover public endooints.
|
|
146
|
+
* @returns The access token along with the values that identify the issuing session.
|
|
147
|
+
*/
|
|
148
|
+
export const authorizeIssuing =
|
|
149
|
+
({
|
|
150
|
+
wiaCryptoContext,
|
|
151
|
+
appFetch = fetch,
|
|
152
|
+
}: {
|
|
153
|
+
wiaCryptoContext: CryptoContext;
|
|
154
|
+
appFetch?: GlobalFetch["fetch"];
|
|
155
|
+
}) =>
|
|
156
|
+
async (
|
|
157
|
+
walletInstanceAttestation: string,
|
|
158
|
+
walletProviderBaseUrl: string,
|
|
159
|
+
pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration
|
|
160
|
+
): Promise<AuthorizationConf> => {
|
|
161
|
+
// FIXME: do better
|
|
162
|
+
const clientId = await wiaCryptoContext.getPublicKey().then((_) => _.kid);
|
|
163
|
+
const codeVerifier = `${uuid.v4()}`;
|
|
164
|
+
const authorizationCode = `${uuid.v4()}`;
|
|
165
|
+
const tokenUrl =
|
|
166
|
+
pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
|
|
167
|
+
.token_endpoint;
|
|
168
|
+
|
|
169
|
+
await getPar({ wiaCryptoContext, appFetch })(
|
|
170
|
+
clientId,
|
|
171
|
+
codeVerifier,
|
|
172
|
+
walletProviderBaseUrl,
|
|
173
|
+
pidProviderEntityConfiguration,
|
|
174
|
+
walletInstanceAttestation
|
|
175
|
+
);
|
|
172
176
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
* @returns a token response
|
|
178
|
-
*
|
|
179
|
-
*/
|
|
180
|
-
async getAuthToken(): Promise<TokenResponse> {
|
|
181
|
-
//Generate fresh keys for DPoP
|
|
182
|
-
const dPopKeyTag = `${uuid.v4()}`;
|
|
183
|
-
const dPopKey = await generate(dPopKeyTag);
|
|
184
|
-
const unsignedDPopForToken = await this.getUnsignedDPoP(dPopKey);
|
|
185
|
-
const dPopTokenSignature = await sign(unsignedDPopForToken, dPopKeyTag);
|
|
186
|
-
await deleteKey(dPopKeyTag);
|
|
177
|
+
// Use an ephemeral key to be destroyed after use
|
|
178
|
+
const keytag = `ephemeral-${uuid.v4()}`;
|
|
179
|
+
await generate(keytag);
|
|
180
|
+
const ephemeralContext = createCryptoContextFor(keytag);
|
|
187
181
|
|
|
188
|
-
const signedDPop = await
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
const signedDPop = await createDPopToken(
|
|
183
|
+
{
|
|
184
|
+
htm: "POST",
|
|
185
|
+
htu: tokenUrl,
|
|
186
|
+
jti: `${uuid.v4()}`,
|
|
187
|
+
},
|
|
188
|
+
ephemeralContext
|
|
191
189
|
);
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
|
|
191
|
+
await deleteKey(keytag);
|
|
192
|
+
|
|
194
193
|
const requestBody = {
|
|
195
194
|
grant_type: "authorization code",
|
|
196
|
-
client_id:
|
|
197
|
-
code:
|
|
198
|
-
code_verifier:
|
|
199
|
-
client_assertion_type:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
redirect_uri: this.walletProviderBaseUrl,
|
|
195
|
+
client_id: clientId,
|
|
196
|
+
code: authorizationCode,
|
|
197
|
+
code_verifier: codeVerifier,
|
|
198
|
+
client_assertion_type: assertionType,
|
|
199
|
+
client_assertion: walletInstanceAttestation,
|
|
200
|
+
redirect_uri: walletProviderBaseUrl,
|
|
203
201
|
};
|
|
204
202
|
var formBody = new URLSearchParams(requestBody);
|
|
205
203
|
|
|
206
|
-
const response = await
|
|
204
|
+
const response = await appFetch(tokenUrl, {
|
|
207
205
|
method: "POST",
|
|
208
206
|
headers: {
|
|
209
207
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -213,73 +211,92 @@ export class Issuing {
|
|
|
213
211
|
});
|
|
214
212
|
|
|
215
213
|
if (response.status === 200) {
|
|
216
|
-
|
|
214
|
+
const { c_nonce, access_token } = await response.json();
|
|
215
|
+
return {
|
|
216
|
+
accessToken: access_token,
|
|
217
|
+
nonce: c_nonce,
|
|
218
|
+
clientId,
|
|
219
|
+
codeVerifier,
|
|
220
|
+
authorizationCode,
|
|
221
|
+
walletProviderBaseUrl,
|
|
222
|
+
};
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
throw new PidIssuingError(
|
|
220
226
|
`Unable to obtain token. Response code: ${await response.text()}`
|
|
221
227
|
);
|
|
222
|
-
}
|
|
228
|
+
};
|
|
223
229
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Return the signed jwt for nonce proof of possession
|
|
232
|
+
*/
|
|
233
|
+
const createNonceProof = async (
|
|
234
|
+
nonce: string,
|
|
235
|
+
issuer: string,
|
|
236
|
+
audience: string,
|
|
237
|
+
ctx: CryptoContext
|
|
238
|
+
): Promise<string> => {
|
|
239
|
+
return new SignJWT(ctx)
|
|
240
|
+
.setPayload({
|
|
235
241
|
nonce,
|
|
242
|
+
jwk: await ctx.getPublicKey(),
|
|
236
243
|
})
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return unsignedProof;
|
|
247
|
-
}
|
|
244
|
+
.setProtectedHeader({
|
|
245
|
+
type: "openid4vci-proof+jwt",
|
|
246
|
+
})
|
|
247
|
+
.setAudience(audience)
|
|
248
|
+
.setIssuer(issuer)
|
|
249
|
+
.setIssuedAt()
|
|
250
|
+
.setExpirationTime("1h")
|
|
251
|
+
.sign();
|
|
252
|
+
};
|
|
248
253
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
254
|
+
/**
|
|
255
|
+
* Complete the issuing flow and get the PID credential.
|
|
256
|
+
*
|
|
257
|
+
* @param params.pidCryptoContext The key pair associated with the PID. Will be use to prove the ownership of the credential.
|
|
258
|
+
* @param params.appFetch (optional) Http client
|
|
259
|
+
* @param authConf The authorization configuration retrieved with the access token
|
|
260
|
+
* @param cieData Data red from the CIE login process
|
|
261
|
+
* @returns The PID credential token
|
|
262
|
+
*/
|
|
263
|
+
export const getCredential =
|
|
264
|
+
({
|
|
265
|
+
pidCryptoContext,
|
|
266
|
+
appFetch = fetch,
|
|
267
|
+
}: {
|
|
268
|
+
pidCryptoContext: CryptoContext;
|
|
269
|
+
appFetch?: GlobalFetch["fetch"];
|
|
270
|
+
}) =>
|
|
271
|
+
async (
|
|
272
|
+
{ nonce, accessToken, clientId, walletProviderBaseUrl }: AuthorizationConf,
|
|
273
|
+
pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration,
|
|
269
274
|
cieData: CieData
|
|
270
|
-
): Promise<PidResponse> {
|
|
271
|
-
const signedDPopForPid = await
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
): Promise<PidResponse> => {
|
|
276
|
+
const signedDPopForPid = await createDPopToken(
|
|
277
|
+
{
|
|
278
|
+
htm: "POST",
|
|
279
|
+
htu: pidProviderEntityConfiguration.payload.metadata
|
|
280
|
+
.openid_credential_issuer.token_endpoint,
|
|
281
|
+
jti: `${uuid.v4()}`,
|
|
282
|
+
},
|
|
283
|
+
pidCryptoContext
|
|
274
284
|
);
|
|
275
|
-
const signedNonceProof = await
|
|
276
|
-
|
|
277
|
-
|
|
285
|
+
const signedNonceProof = await createNonceProof(
|
|
286
|
+
nonce,
|
|
287
|
+
clientId,
|
|
288
|
+
walletProviderBaseUrl,
|
|
289
|
+
pidCryptoContext
|
|
278
290
|
);
|
|
279
|
-
|
|
291
|
+
|
|
292
|
+
const credentialUrl =
|
|
293
|
+
pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
|
|
294
|
+
.credential_endpoint;
|
|
280
295
|
|
|
281
296
|
const requestBody = {
|
|
282
|
-
credential_definition: JSON.stringify({
|
|
297
|
+
credential_definition: JSON.stringify({
|
|
298
|
+
type: ["PersonIdentificationData"],
|
|
299
|
+
}),
|
|
283
300
|
format: "vc+sd-jwt",
|
|
284
301
|
proof: JSON.stringify({
|
|
285
302
|
jwt: signedNonceProof,
|
|
@@ -289,7 +306,7 @@ export class Issuing {
|
|
|
289
306
|
};
|
|
290
307
|
const formBody = new URLSearchParams(requestBody);
|
|
291
308
|
|
|
292
|
-
const response = await
|
|
309
|
+
const response = await appFetch(credentialUrl, {
|
|
293
310
|
method: "POST",
|
|
294
311
|
headers: {
|
|
295
312
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -300,44 +317,28 @@ export class Issuing {
|
|
|
300
317
|
});
|
|
301
318
|
|
|
302
319
|
if (response.status === 200) {
|
|
303
|
-
|
|
320
|
+
const pidResponse = (await response.json()) as PidResponse;
|
|
321
|
+
await validatePid(pidResponse.credential, pidCryptoContext);
|
|
322
|
+
return pidResponse;
|
|
304
323
|
}
|
|
305
324
|
|
|
306
|
-
throw new PidIssuingError(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
* @function
|
|
313
|
-
* @returns PID issuer metadata
|
|
314
|
-
*
|
|
315
|
-
*/
|
|
316
|
-
async getEntityConfiguration(): Promise<PidIssuerEntityConfiguration> {
|
|
317
|
-
const metadataUrl = new URL(
|
|
318
|
-
"ci/.well-known/openid-federation",
|
|
319
|
-
this.pidProviderBaseUrl
|
|
320
|
-
).href;
|
|
321
|
-
|
|
322
|
-
const response = await this.appFetch(metadataUrl);
|
|
325
|
+
throw new PidIssuingError(
|
|
326
|
+
`Unable to obtain credential! url=${credentialUrl} status=${
|
|
327
|
+
response.status
|
|
328
|
+
} body=${await response.text()}`
|
|
329
|
+
);
|
|
330
|
+
};
|
|
323
331
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (result.success) {
|
|
329
|
-
const parsedMetadata = result.data;
|
|
330
|
-
await verifyJwt(jwtMetadata, parsedMetadata.jwks.keys);
|
|
331
|
-
return parsedMetadata;
|
|
332
|
-
} else {
|
|
333
|
-
throw new PidMetadataError(result.error.message);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
332
|
+
const validatePid = async (pidJwt: string, pidCryptoContext: CryptoContext) => {
|
|
333
|
+
const decoded = SdJwt.decode(pidJwt);
|
|
334
|
+
const pidKey = await pidCryptoContext.getPublicKey();
|
|
335
|
+
const holderBindedKey = decoded.sdJwt.payload.cnf.jwk;
|
|
336
336
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
337
|
+
if ((await thumbprint(pidKey)) !== (await thumbprint(holderBindedKey))) {
|
|
338
|
+
throw new PidIssuingError(
|
|
339
|
+
`The obtained pid does not seem to be valid according to your configuration. Your PID public key is: ${JSON.stringify(
|
|
340
|
+
pidKey
|
|
341
|
+
)} but PID holder binded key is: ${JSON.stringify(holderBindedKey)}`
|
|
341
342
|
);
|
|
342
343
|
}
|
|
343
|
-
}
|
|
344
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RelyingPartyEntityConfiguration } from "../../trust/types";
|
|
2
|
+
import * as RelyingPartySolution from "..";
|
|
2
3
|
import { AuthRequestDecodeError } from "../../utils/errors";
|
|
3
|
-
import { RpEntityConfiguration } from "../types";
|
|
4
4
|
|
|
5
5
|
describe("decodeAuthRequestQR", () => {
|
|
6
6
|
it("should return authentication request URL", async () => {
|
|
@@ -239,7 +239,7 @@ describe("RpEntityConfiguration", () => {
|
|
|
239
239
|
],
|
|
240
240
|
},
|
|
241
241
|
};
|
|
242
|
-
const result =
|
|
242
|
+
const result = RelyingPartyEntityConfiguration.safeParse(pp);
|
|
243
243
|
if (result.success === false) {
|
|
244
244
|
throw result.error;
|
|
245
245
|
}
|