@pagopa/io-react-native-wallet 0.15.4 → 0.16.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/lib/commonjs/credential/index.js +3 -1
- package/lib/commonjs/credential/index.js.map +1 -1
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +19 -4
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/05-authorize-access.js +5 -12
- package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +5 -6
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/const.js +1 -3
- package/lib/commonjs/credential/issuance/const.js.map +1 -1
- package/lib/commonjs/credential/status/01-start-flow.js +2 -0
- package/lib/commonjs/credential/status/01-start-flow.js.map +1 -0
- package/lib/commonjs/credential/status/02-status-attestation.js +72 -0
- package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -0
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +52 -0
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
- package/lib/commonjs/credential/status/errors.js +38 -0
- package/lib/commonjs/credential/status/errors.js.map +1 -0
- package/lib/commonjs/credential/status/index.js +27 -0
- package/lib/commonjs/credential/status/index.js.map +1 -0
- package/lib/commonjs/credential/status/types.js +48 -0
- package/lib/commonjs/credential/status/types.js.map +1 -0
- package/lib/commonjs/utils/errors.js +44 -2
- package/lib/commonjs/utils/errors.js.map +1 -1
- package/lib/commonjs/utils/misc.js +41 -3
- package/lib/commonjs/utils/misc.js.map +1 -1
- package/lib/commonjs/utils/par.js +1 -1
- package/lib/commonjs/utils/par.js.map +1 -1
- package/lib/module/credential/index.js +2 -1
- package/lib/module/credential/index.js.map +1 -1
- package/lib/module/credential/issuance/04-complete-user-authorization.js +21 -6
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/05-authorize-access.js +6 -13
- package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +5 -6
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/const.js +0 -1
- package/lib/module/credential/issuance/const.js.map +1 -1
- package/lib/module/credential/status/01-start-flow.js +2 -0
- package/lib/module/credential/status/01-start-flow.js.map +1 -0
- package/lib/module/credential/status/02-status-attestation.js +64 -0
- package/lib/module/credential/status/02-status-attestation.js.map +1 -0
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +46 -0
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
- package/lib/module/credential/status/errors.js +30 -0
- package/lib/module/credential/status/errors.js.map +1 -0
- package/lib/module/credential/status/index.js +5 -0
- package/lib/module/credential/status/index.js.map +1 -0
- package/lib/module/credential/status/types.js +40 -0
- package/lib/module/credential/status/types.js.map +1 -0
- package/lib/module/utils/errors.js +40 -1
- package/lib/module/utils/errors.js.map +1 -1
- package/lib/module/utils/misc.js +38 -3
- package/lib/module/utils/misc.js.map +1 -1
- package/lib/module/utils/par.js +1 -1
- package/lib/module/utils/par.js.map +1 -1
- package/lib/typescript/credential/index.d.ts +2 -1
- package/lib/typescript/credential/index.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +3 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts +2 -1
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +3 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/const.d.ts +0 -1
- package/lib/typescript/credential/issuance/const.d.ts.map +1 -1
- package/lib/typescript/credential/status/01-start-flow.d.ts +10 -0
- package/lib/typescript/credential/status/01-start-flow.d.ts.map +1 -0
- package/lib/typescript/credential/status/02-status-attestation.d.ts +20 -0
- package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -0
- package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts +24 -0
- package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +1 -0
- package/lib/typescript/credential/status/errors.d.ts +14 -0
- package/lib/typescript/credential/status/errors.d.ts.map +1 -0
- package/lib/typescript/credential/status/index.d.ts +7 -0
- package/lib/typescript/credential/status/index.d.ts.map +1 -0
- package/lib/typescript/credential/status/types.d.ts +305 -0
- package/lib/typescript/credential/status/types.d.ts.map +1 -0
- package/lib/typescript/utils/errors.d.ts +31 -0
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/misc.d.ts +18 -1
- package/lib/typescript/utils/misc.d.ts.map +1 -1
- package/lib/typescript/utils/par.d.ts +1 -4
- package/lib/typescript/utils/par.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/credential/index.ts +2 -1
- package/src/credential/issuance/04-complete-user-authorization.ts +36 -6
- package/src/credential/issuance/05-authorize-access.ts +7 -14
- package/src/credential/issuance/06-obtain-credential.ts +8 -8
- package/src/credential/issuance/const.ts +0 -2
- package/src/credential/status/01-start-flow.ts +9 -0
- package/src/credential/status/02-status-attestation.ts +101 -0
- package/src/credential/status/03-verify-and-parse-status-attestation.ts +60 -0
- package/src/credential/status/errors.ts +31 -0
- package/src/credential/status/index.ts +22 -0
- package/src/credential/status/types.ts +43 -0
- package/src/utils/errors.ts +46 -1
- package/src/utils/misc.ts +45 -4
- package/src/utils/par.ts +2 -2
|
@@ -12,16 +12,14 @@ import { CredentialResponse } from "./types";
|
|
|
12
12
|
|
|
13
13
|
import { createDPopToken } from "../../utils/dpop";
|
|
14
14
|
import uuid from "react-native-uuid";
|
|
15
|
-
import { deleteKey } from "@pagopa/io-react-native-crypto";
|
|
16
|
-
import { DPOP_KET_TAG } from "./const";
|
|
17
15
|
|
|
18
16
|
export type ObtainCredential = (
|
|
19
17
|
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
|
20
18
|
accessToken: Out<AuthorizeAccess>["accessToken"],
|
|
21
19
|
clientId: Out<StartUserAuthorization>["clientId"],
|
|
22
20
|
credentialDefinition: Out<StartUserAuthorization>["credentialDefinition"],
|
|
23
|
-
dPoPContext: CryptoContext,
|
|
24
21
|
context: {
|
|
22
|
+
dPopCryptoContext: CryptoContext;
|
|
25
23
|
credentialCryptoContext: CryptoContext;
|
|
26
24
|
appFetch?: GlobalFetch["fetch"];
|
|
27
25
|
}
|
|
@@ -61,6 +59,7 @@ export const createNonceProof = async (
|
|
|
61
59
|
* @param credentialDefinition The credential definition of the credential to be obtained returned by {@link startUserAuthorization}
|
|
62
60
|
* @param tokenRequestSignedDPop The DPoP signed token request returned by {@link authorizeAccess}
|
|
63
61
|
* @param context.credentialCryptoContext The crypto context used to obtain the credential
|
|
62
|
+
* @param context.dPopCryptoContext The DPoP crypto context
|
|
64
63
|
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
|
65
64
|
* @returns The credential response containing the credential
|
|
66
65
|
*/
|
|
@@ -69,10 +68,13 @@ export const obtainCredential: ObtainCredential = async (
|
|
|
69
68
|
accessToken,
|
|
70
69
|
clientId,
|
|
71
70
|
credentialDefinition,
|
|
72
|
-
dPoPContext,
|
|
73
71
|
context
|
|
74
72
|
) => {
|
|
75
|
-
const {
|
|
73
|
+
const {
|
|
74
|
+
credentialCryptoContext,
|
|
75
|
+
appFetch = fetch,
|
|
76
|
+
dPopCryptoContext,
|
|
77
|
+
} = context;
|
|
76
78
|
|
|
77
79
|
const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
|
|
78
80
|
|
|
@@ -122,10 +124,8 @@ export const obtainCredential: ObtainCredential = async (
|
|
|
122
124
|
jti: `${uuid.v4()}`,
|
|
123
125
|
ath: await sha256ToBase64(accessToken.access_token),
|
|
124
126
|
},
|
|
125
|
-
|
|
127
|
+
dPopCryptoContext
|
|
126
128
|
);
|
|
127
|
-
|
|
128
|
-
await deleteKey(DPOP_KET_TAG);
|
|
129
129
|
const credentialRes = await appFetch(credentialUrl, {
|
|
130
130
|
method: "POST",
|
|
131
131
|
headers: {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WARNING: This is the first function to be called in the status attestation flow. The next function to be called is {@link statusAttestation}.
|
|
3
|
+
* The beginning of the status attestation flow.
|
|
4
|
+
*
|
|
5
|
+
* @returns The url of the credential issuer to be used in the next function.
|
|
6
|
+
*/
|
|
7
|
+
export type StartFlow = () => {
|
|
8
|
+
issuerUrl: string;
|
|
9
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCredentialHashWithouDiscloures,
|
|
3
|
+
hasStatus,
|
|
4
|
+
type Out,
|
|
5
|
+
} from "../../utils/misc";
|
|
6
|
+
import type { EvaluateIssuerTrust, ObtainCredential } from "../issuance";
|
|
7
|
+
import { SignJWT, type CryptoContext } from "@pagopa/io-react-native-jwt";
|
|
8
|
+
import uuid from "react-native-uuid";
|
|
9
|
+
import { StatusAttestationResponse } from "./types";
|
|
10
|
+
import { UnexpectedStatusCodeError } from "../../utils/errors";
|
|
11
|
+
import { StatusAttestationError, StatusAttestationInvalid } from "./errors";
|
|
12
|
+
|
|
13
|
+
export type StatusAttestation = (
|
|
14
|
+
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
|
15
|
+
credential: Out<ObtainCredential>["credential"],
|
|
16
|
+
credentialCryptoContext: CryptoContext,
|
|
17
|
+
appFetch?: GlobalFetch["fetch"]
|
|
18
|
+
) => Promise<{
|
|
19
|
+
statusAttestation: StatusAttestationResponse["status_attestation"];
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* WARNING: This function must be called after {@link startFlow}.
|
|
24
|
+
* Verify the status of the credential attestation.
|
|
25
|
+
* @param issuerConf - The issuer's configuration
|
|
26
|
+
* @param credential - The credential to be verified
|
|
27
|
+
* @param credentialCryptoContext - The credential's crypto context
|
|
28
|
+
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
|
29
|
+
* @throws {@link StatusAttestationInvalid} if the status attestation is invalid and thus the credential is not valid
|
|
30
|
+
* @throws {@link StatusAttestationError} if an error occurs during the status attestation
|
|
31
|
+
* @returns The credential status attestation
|
|
32
|
+
*/
|
|
33
|
+
export const statusAttestation: StatusAttestation = async (
|
|
34
|
+
issuerConf,
|
|
35
|
+
credential,
|
|
36
|
+
credentialCryptoContext,
|
|
37
|
+
appFetch: GlobalFetch["fetch"] = fetch
|
|
38
|
+
) => {
|
|
39
|
+
const jwk = await credentialCryptoContext.getPublicKey();
|
|
40
|
+
const credentialHash = await getCredentialHashWithouDiscloures(credential);
|
|
41
|
+
const statusAttUrl =
|
|
42
|
+
issuerConf.openid_credential_issuer.status_attestation_endpoint;
|
|
43
|
+
const credentialPop = await new SignJWT(credentialCryptoContext)
|
|
44
|
+
.setPayload({
|
|
45
|
+
aud: statusAttUrl,
|
|
46
|
+
jti: uuid.v4().toString(),
|
|
47
|
+
credential_hash: credentialHash,
|
|
48
|
+
credential_hash_alg: "S256",
|
|
49
|
+
})
|
|
50
|
+
.setProtectedHeader({
|
|
51
|
+
alg: "ES256",
|
|
52
|
+
typ: "status-attestation-request+jwt",
|
|
53
|
+
kid: jwk.kid,
|
|
54
|
+
})
|
|
55
|
+
.setIssuedAt()
|
|
56
|
+
.setExpirationTime("5m")
|
|
57
|
+
.sign();
|
|
58
|
+
|
|
59
|
+
const body = {
|
|
60
|
+
credential_pop: credentialPop,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const result = await appFetch(statusAttUrl, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
})
|
|
70
|
+
.then(hasStatus(201))
|
|
71
|
+
.then((raw) => raw.json())
|
|
72
|
+
.then((json) => StatusAttestationResponse.parse(json))
|
|
73
|
+
.catch(handleStatusAttestationError);
|
|
74
|
+
|
|
75
|
+
return { statusAttestation: result.status_attestation };
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Handle the status attestation error by mapping it to a custom exception.
|
|
80
|
+
* If the error is not an instance of {@link UnexpectedStatusCodeError}, it is thrown as is.
|
|
81
|
+
* @param e - The error to be handled
|
|
82
|
+
* @throws {@link StatusAttestationError} if the status code is different from 404
|
|
83
|
+
* @throws {@link StatusAttestationInvalid} if the status code is 404 (meaning the credential is invalid)
|
|
84
|
+
*/
|
|
85
|
+
const handleStatusAttestationError = (e: unknown) => {
|
|
86
|
+
if (!(e instanceof UnexpectedStatusCodeError)) {
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (e.statusCode === 404) {
|
|
91
|
+
throw new StatusAttestationInvalid(
|
|
92
|
+
"Invalid status found for the given credential",
|
|
93
|
+
e.message
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new StatusAttestationError(
|
|
98
|
+
`Unable to obtain the status attestation for the given credential [response status code: ${e.statusCode}]`,
|
|
99
|
+
e.message
|
|
100
|
+
);
|
|
101
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Out } from "../../utils/misc";
|
|
2
|
+
import { IoWalletError } from "../../utils/errors";
|
|
3
|
+
import { verify, type CryptoContext } from "@pagopa/io-react-native-jwt";
|
|
4
|
+
import type { EvaluateIssuerTrust, StatusAttestation } from "../status";
|
|
5
|
+
import { ParsedStatusAttestation } from "./types";
|
|
6
|
+
import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
|
|
7
|
+
|
|
8
|
+
export type VerifyAndParseStatusAttestation = (
|
|
9
|
+
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
|
10
|
+
statusAttestation: Out<StatusAttestation>,
|
|
11
|
+
context: {
|
|
12
|
+
credentialCryptoContext: CryptoContext;
|
|
13
|
+
}
|
|
14
|
+
) => Promise<{ parsedStatusAttestation: ParsedStatusAttestation }>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Given a status attestation, verifies that:
|
|
18
|
+
* - It's in the supported format;
|
|
19
|
+
* - The attestation is correctly signed;
|
|
20
|
+
* - It's bound to the given key.
|
|
21
|
+
* @param issuerConf The Issuer configuration returned by {@link evaluateIssuerTrust}
|
|
22
|
+
* @param statusAttestation The encoded status attestation returned by {@link statusAttestation}
|
|
23
|
+
* @param context.credentialCryptoContext The crypto context used to obtain the credential in {@link obtainCredential}
|
|
24
|
+
* @returns A parsed status attestation
|
|
25
|
+
* @throws {IoWalletError} If the credential signature is not verified with the Issuer key set
|
|
26
|
+
* @throws {IoWalletError} If the credential is not bound to the provided user key
|
|
27
|
+
* @throws {IoWalletError} If the credential data fail to parse
|
|
28
|
+
*/
|
|
29
|
+
export const verifyAndParseStatusAttestation: VerifyAndParseStatusAttestation =
|
|
30
|
+
async (issuerConf, rawStatusAttestation, context) => {
|
|
31
|
+
try {
|
|
32
|
+
const { statusAttestation } = rawStatusAttestation;
|
|
33
|
+
const { credentialCryptoContext } = context;
|
|
34
|
+
|
|
35
|
+
await verify(
|
|
36
|
+
statusAttestation,
|
|
37
|
+
issuerConf.openid_credential_issuer.jwks.keys
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const decodedJwt = decodeJwt(statusAttestation);
|
|
41
|
+
const parsedStatusAttestation = ParsedStatusAttestation.parse({
|
|
42
|
+
header: decodedJwt.protectedHeader,
|
|
43
|
+
payload: decodedJwt.payload,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const holderBindingKey = await credentialCryptoContext.getPublicKey();
|
|
47
|
+
const { cnf } = parsedStatusAttestation.payload;
|
|
48
|
+
if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
|
|
49
|
+
throw new IoWalletError(
|
|
50
|
+
`Failed to verify holder binding for status attestation, expected kid: ${holderBindingKey.kid}, got: ${parsedStatusAttestation.payload.cnf.jwk.kid}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { parsedStatusAttestation };
|
|
55
|
+
} catch (e) {
|
|
56
|
+
throw new IoWalletError(
|
|
57
|
+
`Failed to verify status attestation: ${JSON.stringify(e)}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IoWalletError, serializeAttrs } from "../../utils/errors";
|
|
2
|
+
|
|
3
|
+
export class StatusAttestationInvalid extends IoWalletError {
|
|
4
|
+
static get code(): "ERR_STATUS_ATTESTATION_INVALID" {
|
|
5
|
+
return "ERR_STATUS_ATTESTATION_INVALID";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
code = "ERR_STATUS_ATTESTATION_INVALID";
|
|
9
|
+
|
|
10
|
+
reason: string;
|
|
11
|
+
|
|
12
|
+
constructor(message: string, reason: string = "unspecified") {
|
|
13
|
+
super(serializeAttrs({ message, reason }));
|
|
14
|
+
this.reason = reason;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class StatusAttestationError extends IoWalletError {
|
|
19
|
+
static get code(): "ERR_STATUS_ATTESTATION_ERROR" {
|
|
20
|
+
return "ERR_STATUS_ATTESTATION_ERROR";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
code = "ERR_STATUS_ATTESTATION_ERROR";
|
|
24
|
+
|
|
25
|
+
reason: string;
|
|
26
|
+
|
|
27
|
+
constructor(message: string, reason: string = "unspecified") {
|
|
28
|
+
super(serializeAttrs({ message, reason }));
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type StartFlow } from "./01-start-flow";
|
|
2
|
+
import {
|
|
3
|
+
statusAttestation,
|
|
4
|
+
type StatusAttestation,
|
|
5
|
+
} from "./02-status-attestation";
|
|
6
|
+
import { evaluateIssuerTrust, type EvaluateIssuerTrust } from "../issuance";
|
|
7
|
+
import {
|
|
8
|
+
verifyAndParseStatusAttestation,
|
|
9
|
+
type VerifyAndParseStatusAttestation,
|
|
10
|
+
} from "./03-verify-and-parse-status-attestation";
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
evaluateIssuerTrust,
|
|
14
|
+
statusAttestation,
|
|
15
|
+
verifyAndParseStatusAttestation,
|
|
16
|
+
};
|
|
17
|
+
export type {
|
|
18
|
+
StartFlow,
|
|
19
|
+
EvaluateIssuerTrust,
|
|
20
|
+
StatusAttestation,
|
|
21
|
+
VerifyAndParseStatusAttestation,
|
|
22
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { UnixTime } from "../../sd-jwt/types";
|
|
2
|
+
import { JWK } from "../../utils/jwk";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shape from parsing a status attestation response in case of 201.
|
|
7
|
+
*/
|
|
8
|
+
export const StatusAttestationResponse = z.object({
|
|
9
|
+
status_attestation: z.string(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Type from parsing a status attestation response in case of 201.
|
|
14
|
+
* Inferred from {@link StatusAttestationResponse}.
|
|
15
|
+
*/
|
|
16
|
+
export type StatusAttestationResponse = z.infer<
|
|
17
|
+
typeof StatusAttestationResponse
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Type for a parsed status attestation.
|
|
22
|
+
*/
|
|
23
|
+
export type ParsedStatusAttestation = z.infer<typeof ParsedStatusAttestation>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Shape for parsing a status attestation in a JWT.
|
|
27
|
+
*/
|
|
28
|
+
export const ParsedStatusAttestation = z.object({
|
|
29
|
+
header: z.object({
|
|
30
|
+
typ: z.literal("status-attestation+jwt"),
|
|
31
|
+
alg: z.string(),
|
|
32
|
+
kid: z.string().optional(),
|
|
33
|
+
}),
|
|
34
|
+
payload: z.object({
|
|
35
|
+
credential_hash_alg: z.string(),
|
|
36
|
+
credential_hash: z.string(),
|
|
37
|
+
cnf: z.object({
|
|
38
|
+
jwk: JWK,
|
|
39
|
+
}),
|
|
40
|
+
exp: UnixTime,
|
|
41
|
+
iat: UnixTime,
|
|
42
|
+
}),
|
|
43
|
+
});
|
package/src/utils/errors.ts
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* @param attrs A key value record set
|
|
9
9
|
* @returns a human-readable serialization of the set
|
|
10
10
|
*/
|
|
11
|
-
const serializeAttrs = (
|
|
11
|
+
export const serializeAttrs = (
|
|
12
|
+
attrs: Record<string, string | string>
|
|
13
|
+
): string =>
|
|
12
14
|
Object.entries(attrs)
|
|
13
15
|
.map(([k, v]) => [k, Array.isArray(v) ? `(${v.join(", ")})` : v])
|
|
14
16
|
.map((_) => _.join("="))
|
|
@@ -41,6 +43,30 @@ export class IoWalletError extends Error {
|
|
|
41
43
|
Error.captureStackTrace?.(this, this.constructor);
|
|
42
44
|
}
|
|
43
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An error subclass thrown when a Wallet Provider http request has a status code different from the one expected.
|
|
49
|
+
*/
|
|
50
|
+
export class UnexpectedStatusCodeError extends IoWalletError {
|
|
51
|
+
static get code(): "ERR_UNEXPECTED_STATUS_CODE" {
|
|
52
|
+
return "ERR_UNEXPECTED_STATUS_CODE";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
code = "ERR_UNEXPECTED_STATUS_CODE";
|
|
56
|
+
|
|
57
|
+
/** HTTP status code */
|
|
58
|
+
statusCode: number;
|
|
59
|
+
|
|
60
|
+
constructor(message: string, statusCode: number) {
|
|
61
|
+
super(
|
|
62
|
+
serializeAttrs({
|
|
63
|
+
message,
|
|
64
|
+
statusCode: statusCode.toString(),
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
this.statusCode = statusCode;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
44
70
|
/**
|
|
45
71
|
* An error subclass thrown when validation fail
|
|
46
72
|
*
|
|
@@ -345,3 +371,22 @@ export class AuthorizationIdpError extends IoWalletError {
|
|
|
345
371
|
this.errorDescription = errorDescription;
|
|
346
372
|
}
|
|
347
373
|
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Error subclass thrown when an operation has been aborted.
|
|
377
|
+
*/
|
|
378
|
+
export class OperationAbortedError extends IoWalletError {
|
|
379
|
+
static get code(): "ERR_IO_WALLET_OPERATION_ABORTED" {
|
|
380
|
+
return "ERR_IO_WALLET_OPERATION_ABORTED";
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
code = "ERR_IO_WALLET_OPERATION_ABORTED";
|
|
384
|
+
|
|
385
|
+
/** The aborted operation */
|
|
386
|
+
operation: string;
|
|
387
|
+
|
|
388
|
+
constructor(operation: string) {
|
|
389
|
+
super(serializeAttrs({ operation }));
|
|
390
|
+
this.operation = operation;
|
|
391
|
+
}
|
|
392
|
+
}
|
package/src/utils/misc.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { IoWalletError } from "./errors";
|
|
1
|
+
import { IoWalletError, UnexpectedStatusCodeError } from "./errors";
|
|
2
|
+
import { sha256 } from "js-sha256";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Check if a response is in the expected status, other
|
|
5
|
-
* @param status The expected status
|
|
6
|
+
* @param status - The expected status
|
|
7
|
+
* @throws {@link UnexpectedStatusCodeError} if the status is different from the one expected
|
|
6
8
|
* @returns The given response object
|
|
7
9
|
*/
|
|
8
10
|
export const hasStatus =
|
|
9
11
|
(status: number) =>
|
|
10
12
|
async (res: Response): Promise<Response> => {
|
|
11
13
|
if (res.status !== status) {
|
|
12
|
-
throw new
|
|
14
|
+
throw new UnexpectedStatusCodeError(
|
|
13
15
|
`Http request failed. Expected ${status}, got ${res.status}, url: ${
|
|
14
16
|
res.url
|
|
15
|
-
} with response: ${await res.text()}
|
|
17
|
+
} with response: ${await res.text()}`,
|
|
18
|
+
res.status
|
|
16
19
|
);
|
|
17
20
|
}
|
|
18
21
|
return res;
|
|
@@ -68,3 +71,41 @@ export const until = (
|
|
|
68
71
|
|
|
69
72
|
poll();
|
|
70
73
|
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the hash of a credential without discloures.
|
|
77
|
+
* A credential is a string like `header.body.sign~sd1~sd2....` where `~` is the separator between the credential and the discloures.
|
|
78
|
+
* @param credential - The credential to hash
|
|
79
|
+
* @returns The hash of the credential without discloures
|
|
80
|
+
*/
|
|
81
|
+
export const getCredentialHashWithouDiscloures = async (
|
|
82
|
+
credential: string
|
|
83
|
+
): Promise<string> => {
|
|
84
|
+
const tildeIndex = credential.indexOf("~");
|
|
85
|
+
if (tildeIndex === -1) {
|
|
86
|
+
throw new IoWalletError("Invalid credential format");
|
|
87
|
+
}
|
|
88
|
+
return sha256(credential.slice(0, tildeIndex));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates a promise that waits until the provided signal is aborted.
|
|
93
|
+
* @returns {Object} An object with `listen` and `remove` methods to handle subscribing and unsubscribing.
|
|
94
|
+
*/
|
|
95
|
+
export const createAbortPromiseFromSignal = (signal: AbortSignal) => {
|
|
96
|
+
let listener: () => void;
|
|
97
|
+
return {
|
|
98
|
+
listen: () =>
|
|
99
|
+
new Promise<"OPERATION_ABORTED">((resolve) => {
|
|
100
|
+
if (signal.aborted) {
|
|
101
|
+
return resolve("OPERATION_ABORTED");
|
|
102
|
+
}
|
|
103
|
+
listener = () => resolve("OPERATION_ABORTED");
|
|
104
|
+
signal.addEventListener("abort", listener);
|
|
105
|
+
}),
|
|
106
|
+
remove: () => signal.removeEventListener("abort", listener),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const isDefined = <T>(x: T | undefined | null | ""): x is T =>
|
|
111
|
+
Boolean(x);
|
package/src/utils/par.ts
CHANGED
|
@@ -25,10 +25,10 @@ export const AuthorizationDetails = z.array(AuthorizationDetail);
|
|
|
25
25
|
export const makeParRequest =
|
|
26
26
|
({
|
|
27
27
|
wiaCryptoContext,
|
|
28
|
-
appFetch
|
|
28
|
+
appFetch,
|
|
29
29
|
}: {
|
|
30
30
|
wiaCryptoContext: CryptoContext;
|
|
31
|
-
appFetch
|
|
31
|
+
appFetch: GlobalFetch["fetch"];
|
|
32
32
|
}) =>
|
|
33
33
|
async (
|
|
34
34
|
clientId: string,
|