@pagopa/io-react-native-wallet 0.15.4 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,
|