@pagopa/io-react-native-wallet 0.15.3 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) 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 +9 -12
  6. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
  7. package/lib/commonjs/credential/issuance/06-obtain-credential.js +13 -2
  8. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
  9. package/lib/commonjs/credential/status/01-start-flow.js +2 -0
  10. package/lib/commonjs/credential/status/01-start-flow.js.map +1 -0
  11. package/lib/commonjs/credential/status/02-status-attestation.js +72 -0
  12. package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -0
  13. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +52 -0
  14. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
  15. package/lib/commonjs/credential/status/errors.js +38 -0
  16. package/lib/commonjs/credential/status/errors.js.map +1 -0
  17. package/lib/commonjs/credential/status/index.js +27 -0
  18. package/lib/commonjs/credential/status/index.js.map +1 -0
  19. package/lib/commonjs/credential/status/types.js +48 -0
  20. package/lib/commonjs/credential/status/types.js.map +1 -0
  21. package/lib/commonjs/utils/errors.js +44 -2
  22. package/lib/commonjs/utils/errors.js.map +1 -1
  23. package/lib/commonjs/utils/misc.js +41 -3
  24. package/lib/commonjs/utils/misc.js.map +1 -1
  25. package/lib/commonjs/utils/par.js +1 -1
  26. package/lib/commonjs/utils/par.js.map +1 -1
  27. package/lib/module/credential/index.js +2 -1
  28. package/lib/module/credential/index.js.map +1 -1
  29. package/lib/module/credential/issuance/04-complete-user-authorization.js +21 -6
  30. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  31. package/lib/module/credential/issuance/05-authorize-access.js +9 -12
  32. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  33. package/lib/module/credential/issuance/06-obtain-credential.js +13 -3
  34. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  35. package/lib/module/credential/status/01-start-flow.js +2 -0
  36. package/lib/module/credential/status/01-start-flow.js.map +1 -0
  37. package/lib/module/credential/status/02-status-attestation.js +64 -0
  38. package/lib/module/credential/status/02-status-attestation.js.map +1 -0
  39. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +46 -0
  40. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +1 -0
  41. package/lib/module/credential/status/errors.js +30 -0
  42. package/lib/module/credential/status/errors.js.map +1 -0
  43. package/lib/module/credential/status/index.js +5 -0
  44. package/lib/module/credential/status/index.js.map +1 -0
  45. package/lib/module/credential/status/types.js +40 -0
  46. package/lib/module/credential/status/types.js.map +1 -0
  47. package/lib/module/utils/errors.js +40 -1
  48. package/lib/module/utils/errors.js.map +1 -1
  49. package/lib/module/utils/misc.js +38 -3
  50. package/lib/module/utils/misc.js.map +1 -1
  51. package/lib/module/utils/par.js +1 -1
  52. package/lib/module/utils/par.js.map +1 -1
  53. package/lib/typescript/credential/index.d.ts +2 -1
  54. package/lib/typescript/credential/index.d.ts.map +1 -1
  55. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +3 -1
  56. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  57. package/lib/typescript/credential/issuance/05-authorize-access.d.ts +2 -1
  58. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  59. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +3 -1
  60. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  61. package/lib/typescript/credential/status/01-start-flow.d.ts +10 -0
  62. package/lib/typescript/credential/status/01-start-flow.d.ts.map +1 -0
  63. package/lib/typescript/credential/status/02-status-attestation.d.ts +20 -0
  64. package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -0
  65. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts +24 -0
  66. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +1 -0
  67. package/lib/typescript/credential/status/errors.d.ts +14 -0
  68. package/lib/typescript/credential/status/errors.d.ts.map +1 -0
  69. package/lib/typescript/credential/status/index.d.ts +7 -0
  70. package/lib/typescript/credential/status/index.d.ts.map +1 -0
  71. package/lib/typescript/credential/status/types.d.ts +305 -0
  72. package/lib/typescript/credential/status/types.d.ts.map +1 -0
  73. package/lib/typescript/utils/errors.d.ts +31 -0
  74. package/lib/typescript/utils/errors.d.ts.map +1 -1
  75. package/lib/typescript/utils/misc.d.ts +18 -1
  76. package/lib/typescript/utils/misc.d.ts.map +1 -1
  77. package/lib/typescript/utils/par.d.ts +1 -4
  78. package/lib/typescript/utils/par.d.ts.map +1 -1
  79. package/package.json +3 -3
  80. package/src/credential/index.ts +2 -1
  81. package/src/credential/issuance/04-complete-user-authorization.ts +36 -6
  82. package/src/credential/issuance/05-authorize-access.ts +13 -15
  83. package/src/credential/issuance/06-obtain-credential.ts +24 -4
  84. package/src/credential/status/01-start-flow.ts +9 -0
  85. package/src/credential/status/02-status-attestation.ts +101 -0
  86. package/src/credential/status/03-verify-and-parse-status-attestation.ts +60 -0
  87. package/src/credential/status/errors.ts +31 -0
  88. package/src/credential/status/index.ts +22 -0
  89. package/src/credential/status/types.ts +43 -0
  90. package/src/utils/errors.ts +46 -1
  91. package/src/utils/misc.ts +45 -4
  92. package/src/utils/par.ts +2 -2
@@ -1,4 +1,8 @@
1
- import { SignJWT, type CryptoContext } from "@pagopa/io-react-native-jwt";
1
+ import {
2
+ sha256ToBase64,
3
+ SignJWT,
4
+ type CryptoContext,
5
+ } from "@pagopa/io-react-native-jwt";
2
6
  import type { AuthorizeAccess } from "./05-authorize-access";
3
7
  import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
4
8
  import { hasStatus, type Out } from "../../utils/misc";
@@ -6,13 +10,16 @@ import type { StartUserAuthorization } from "./03-start-user-authorization";
6
10
  import { ValidationFailed } from "../../utils/errors";
7
11
  import { CredentialResponse } from "./types";
8
12
 
13
+ import { createDPopToken } from "../../utils/dpop";
14
+ import uuid from "react-native-uuid";
15
+
9
16
  export type ObtainCredential = (
10
17
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
11
18
  accessToken: Out<AuthorizeAccess>["accessToken"],
12
19
  clientId: Out<StartUserAuthorization>["clientId"],
13
20
  credentialDefinition: Out<StartUserAuthorization>["credentialDefinition"],
14
- tokenRequestSignedDPop: Out<AuthorizeAccess>["tokenRequestSignedDPop"],
15
21
  context: {
22
+ dPopCryptoContext: CryptoContext;
16
23
  credentialCryptoContext: CryptoContext;
17
24
  appFetch?: GlobalFetch["fetch"];
18
25
  }
@@ -52,6 +59,7 @@ export const createNonceProof = async (
52
59
  * @param credentialDefinition The credential definition of the credential to be obtained returned by {@link startUserAuthorization}
53
60
  * @param tokenRequestSignedDPop The DPoP signed token request returned by {@link authorizeAccess}
54
61
  * @param context.credentialCryptoContext The crypto context used to obtain the credential
62
+ * @param context.dPopCryptoContext The DPoP crypto context
55
63
  * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
56
64
  * @returns The credential response containing the credential
57
65
  */
@@ -60,10 +68,13 @@ export const obtainCredential: ObtainCredential = async (
60
68
  accessToken,
61
69
  clientId,
62
70
  credentialDefinition,
63
- tokenRequestSignedDPop,
64
71
  context
65
72
  ) => {
66
- const { credentialCryptoContext, appFetch = fetch } = context;
73
+ const {
74
+ credentialCryptoContext,
75
+ appFetch = fetch,
76
+ dPopCryptoContext,
77
+ } = context;
67
78
 
68
79
  const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
69
80
 
@@ -106,6 +117,15 @@ export const obtainCredential: ObtainCredential = async (
106
117
  },
107
118
  };
108
119
 
120
+ const tokenRequestSignedDPop = await await createDPopToken(
121
+ {
122
+ htm: "POST",
123
+ htu: credentialUrl,
124
+ jti: `${uuid.v4()}`,
125
+ ath: await sha256ToBase64(accessToken.access_token),
126
+ },
127
+ dPopCryptoContext
128
+ );
109
129
  const credentialRes = await appFetch(credentialUrl, {
110
130
  method: "POST",
111
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
+ });
@@ -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,