@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.
Files changed (99) hide show
  1. package/lib/commonjs/credential/index.js +3 -1
  2. package/lib/commonjs/credential/index.js.map +1 -1
  3. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +19 -4
  4. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  5. package/lib/commonjs/credential/issuance/05-authorize-access.js +5 -12
  6. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
  7. package/lib/commonjs/credential/issuance/06-obtain-credential.js +5 -6
  8. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
  9. package/lib/commonjs/credential/issuance/const.js +1 -3
  10. package/lib/commonjs/credential/issuance/const.js.map +1 -1
  11. package/lib/commonjs/credential/status/01-start-flow.js +2 -0
  12. package/lib/commonjs/credential/status/01-start-flow.js.map +1 -0
  13. package/lib/commonjs/credential/status/02-status-attestation.js +72 -0
  14. package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -0
  15. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +52 -0
  16. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
  17. package/lib/commonjs/credential/status/errors.js +38 -0
  18. package/lib/commonjs/credential/status/errors.js.map +1 -0
  19. package/lib/commonjs/credential/status/index.js +27 -0
  20. package/lib/commonjs/credential/status/index.js.map +1 -0
  21. package/lib/commonjs/credential/status/types.js +48 -0
  22. package/lib/commonjs/credential/status/types.js.map +1 -0
  23. package/lib/commonjs/utils/errors.js +44 -2
  24. package/lib/commonjs/utils/errors.js.map +1 -1
  25. package/lib/commonjs/utils/misc.js +41 -3
  26. package/lib/commonjs/utils/misc.js.map +1 -1
  27. package/lib/commonjs/utils/par.js +1 -1
  28. package/lib/commonjs/utils/par.js.map +1 -1
  29. package/lib/module/credential/index.js +2 -1
  30. package/lib/module/credential/index.js.map +1 -1
  31. package/lib/module/credential/issuance/04-complete-user-authorization.js +21 -6
  32. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  33. package/lib/module/credential/issuance/05-authorize-access.js +6 -13
  34. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  35. package/lib/module/credential/issuance/06-obtain-credential.js +5 -6
  36. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  37. package/lib/module/credential/issuance/const.js +0 -1
  38. package/lib/module/credential/issuance/const.js.map +1 -1
  39. package/lib/module/credential/status/01-start-flow.js +2 -0
  40. package/lib/module/credential/status/01-start-flow.js.map +1 -0
  41. package/lib/module/credential/status/02-status-attestation.js +64 -0
  42. package/lib/module/credential/status/02-status-attestation.js.map +1 -0
  43. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +46 -0
  44. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
  45. package/lib/module/credential/status/errors.js +30 -0
  46. package/lib/module/credential/status/errors.js.map +1 -0
  47. package/lib/module/credential/status/index.js +5 -0
  48. package/lib/module/credential/status/index.js.map +1 -0
  49. package/lib/module/credential/status/types.js +40 -0
  50. package/lib/module/credential/status/types.js.map +1 -0
  51. package/lib/module/utils/errors.js +40 -1
  52. package/lib/module/utils/errors.js.map +1 -1
  53. package/lib/module/utils/misc.js +38 -3
  54. package/lib/module/utils/misc.js.map +1 -1
  55. package/lib/module/utils/par.js +1 -1
  56. package/lib/module/utils/par.js.map +1 -1
  57. package/lib/typescript/credential/index.d.ts +2 -1
  58. package/lib/typescript/credential/index.d.ts.map +1 -1
  59. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +3 -1
  60. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  61. package/lib/typescript/credential/issuance/05-authorize-access.d.ts +2 -1
  62. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  63. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +3 -1
  64. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  65. package/lib/typescript/credential/issuance/const.d.ts +0 -1
  66. package/lib/typescript/credential/issuance/const.d.ts.map +1 -1
  67. package/lib/typescript/credential/status/01-start-flow.d.ts +10 -0
  68. package/lib/typescript/credential/status/01-start-flow.d.ts.map +1 -0
  69. package/lib/typescript/credential/status/02-status-attestation.d.ts +20 -0
  70. package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -0
  71. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts +24 -0
  72. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +1 -0
  73. package/lib/typescript/credential/status/errors.d.ts +14 -0
  74. package/lib/typescript/credential/status/errors.d.ts.map +1 -0
  75. package/lib/typescript/credential/status/index.d.ts +7 -0
  76. package/lib/typescript/credential/status/index.d.ts.map +1 -0
  77. package/lib/typescript/credential/status/types.d.ts +305 -0
  78. package/lib/typescript/credential/status/types.d.ts.map +1 -0
  79. package/lib/typescript/utils/errors.d.ts +31 -0
  80. package/lib/typescript/utils/errors.d.ts.map +1 -1
  81. package/lib/typescript/utils/misc.d.ts +18 -1
  82. package/lib/typescript/utils/misc.d.ts.map +1 -1
  83. package/lib/typescript/utils/par.d.ts +1 -4
  84. package/lib/typescript/utils/par.d.ts.map +1 -1
  85. package/package.json +3 -3
  86. package/src/credential/index.ts +2 -1
  87. package/src/credential/issuance/04-complete-user-authorization.ts +36 -6
  88. package/src/credential/issuance/05-authorize-access.ts +7 -14
  89. package/src/credential/issuance/06-obtain-credential.ts +8 -8
  90. package/src/credential/issuance/const.ts +0 -2
  91. package/src/credential/status/01-start-flow.ts +9 -0
  92. package/src/credential/status/02-status-attestation.ts +101 -0
  93. package/src/credential/status/03-verify-and-parse-status-attestation.ts +60 -0
  94. package/src/credential/status/errors.ts +31 -0
  95. package/src/credential/status/index.ts +22 -0
  96. package/src/credential/status/types.ts +43 -0
  97. package/src/utils/errors.ts +46 -1
  98. package/src/utils/misc.ts +45 -4
  99. 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 { credentialCryptoContext, appFetch = fetch } = context;
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
- dPoPContext
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: {
@@ -2,8 +2,6 @@ import * as z from "zod";
2
2
  export const ASSERTION_TYPE =
3
3
  "urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation";
4
4
 
5
- export const DPOP_KET_TAG = `dpop`;
6
-
7
5
  export type SupportedCredentialFormat = z.infer<
8
6
  typeof SupportedCredentialFormat
9
7
  >;
@@ -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
+ });
@@ -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 = (attrs: Record<string, string | string>): string =>
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 IoWalletError(
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 = fetch,
28
+ appFetch,
29
29
  }: {
30
30
  wiaCryptoContext: CryptoContext;
31
- appFetch?: GlobalFetch["fetch"];
31
+ appFetch: GlobalFetch["fetch"];
32
32
  }) =>
33
33
  async (
34
34
  clientId: string,