@pagopa/io-react-native-wallet 0.15.4 → 0.16.1

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 +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 +71 -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/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 +82 -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 +6 -13
  32. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  33. package/lib/module/credential/issuance/06-obtain-credential.js +5 -6
  34. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  35. package/lib/module/credential/issuance/const.js +0 -1
  36. package/lib/module/credential/issuance/const.js.map +1 -1
  37. package/lib/module/credential/status/01-start-flow.js +2 -0
  38. package/lib/module/credential/status/01-start-flow.js.map +1 -0
  39. package/lib/module/credential/status/02-status-attestation.js +63 -0
  40. package/lib/module/credential/status/02-status-attestation.js.map +1 -0
  41. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +46 -0
  42. package/lib/module/credential/status/03-verify-and-parse-status-attestation.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 +76 -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/issuance/const.d.ts +0 -1
  62. package/lib/typescript/credential/issuance/const.d.ts.map +1 -1
  63. package/lib/typescript/credential/status/01-start-flow.d.ts +10 -0
  64. package/lib/typescript/credential/status/01-start-flow.d.ts.map +1 -0
  65. package/lib/typescript/credential/status/02-status-attestation.d.ts +20 -0
  66. package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -0
  67. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts +24 -0
  68. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.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 +49 -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 +7 -14
  83. package/src/credential/issuance/06-obtain-credential.ts +8 -8
  84. package/src/credential/issuance/const.ts +0 -2
  85. package/src/credential/status/01-start-flow.ts +9 -0
  86. package/src/credential/status/02-status-attestation.ts +104 -0
  87. package/src/credential/status/03-verify-and-parse-status-attestation.ts +60 -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 +82 -1
  91. package/src/utils/misc.ts +45 -4
  92. 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,104 @@
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 {
11
+ StatusAttestationError,
12
+ StatusAttestationInvalid,
13
+ UnexpectedStatusCodeError,
14
+ } from "../../utils/errors";
15
+
16
+ export type StatusAttestation = (
17
+ issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
18
+ credential: Out<ObtainCredential>["credential"],
19
+ credentialCryptoContext: CryptoContext,
20
+ appFetch?: GlobalFetch["fetch"]
21
+ ) => Promise<{
22
+ statusAttestation: StatusAttestationResponse["status_attestation"];
23
+ }>;
24
+
25
+ /**
26
+ * WARNING: This function must be called after {@link startFlow}.
27
+ * Verify the status of the credential attestation.
28
+ * @param issuerConf - The issuer's configuration
29
+ * @param credential - The credential to be verified
30
+ * @param credentialCryptoContext - The credential's crypto context
31
+ * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
32
+ * @throws {@link StatusAttestationInvalid} if the status attestation is invalid and thus the credential is not valid
33
+ * @throws {@link StatusAttestationError} if an error occurs during the status attestation
34
+ * @returns The credential status attestation
35
+ */
36
+ export const statusAttestation: StatusAttestation = async (
37
+ issuerConf,
38
+ credential,
39
+ credentialCryptoContext,
40
+ appFetch: GlobalFetch["fetch"] = fetch
41
+ ) => {
42
+ const jwk = await credentialCryptoContext.getPublicKey();
43
+ const credentialHash = await getCredentialHashWithouDiscloures(credential);
44
+ const statusAttUrl =
45
+ issuerConf.openid_credential_issuer.status_attestation_endpoint;
46
+ const credentialPop = await new SignJWT(credentialCryptoContext)
47
+ .setPayload({
48
+ aud: statusAttUrl,
49
+ jti: uuid.v4().toString(),
50
+ credential_hash: credentialHash,
51
+ credential_hash_alg: "S256",
52
+ })
53
+ .setProtectedHeader({
54
+ alg: "ES256",
55
+ typ: "status-attestation-request+jwt",
56
+ kid: jwk.kid,
57
+ })
58
+ .setIssuedAt()
59
+ .setExpirationTime("5m")
60
+ .sign();
61
+
62
+ const body = {
63
+ credential_pop: credentialPop,
64
+ };
65
+
66
+ const result = await appFetch(statusAttUrl, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ },
71
+ body: JSON.stringify(body),
72
+ })
73
+ .then(hasStatus(201))
74
+ .then((raw) => raw.json())
75
+ .then((json) => StatusAttestationResponse.parse(json))
76
+ .catch(handleStatusAttestationError);
77
+
78
+ return { statusAttestation: result.status_attestation };
79
+ };
80
+
81
+ /**
82
+ * Handle the status attestation error by mapping it to a custom exception.
83
+ * If the error is not an instance of {@link UnexpectedStatusCodeError}, it is thrown as is.
84
+ * @param e - The error to be handled
85
+ * @throws {@link StatusAttestationError} if the status code is different from 404
86
+ * @throws {@link StatusAttestationInvalid} if the status code is 404 (meaning the credential is invalid)
87
+ */
88
+ const handleStatusAttestationError = (e: unknown) => {
89
+ if (!(e instanceof UnexpectedStatusCodeError)) {
90
+ throw e;
91
+ }
92
+
93
+ if (e.statusCode === 404) {
94
+ throw new StatusAttestationInvalid(
95
+ "Invalid status found for the given credential",
96
+ e.message
97
+ );
98
+ }
99
+
100
+ throw new StatusAttestationError(
101
+ `Unable to obtain the status attestation for the given credential [response status code: ${e.statusCode}]`,
102
+ e.message
103
+ );
104
+ };
@@ -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,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,58 @@ 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
+ }
393
+
394
+ /**
395
+ * Error subclass thrown when the status attestation for a credential is invalid.
396
+ */
397
+ export class StatusAttestationInvalid extends IoWalletError {
398
+ static get code(): "ERR_STATUS_ATTESTATION_INVALID" {
399
+ return "ERR_STATUS_ATTESTATION_INVALID";
400
+ }
401
+
402
+ code = "ERR_STATUS_ATTESTATION_INVALID";
403
+
404
+ reason: string;
405
+
406
+ constructor(message: string, reason: string = "unspecified") {
407
+ super(serializeAttrs({ message, reason }));
408
+ this.reason = reason;
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Error subclass thrown when an error occurs while obtaining a status attestation for a credential.
414
+ */
415
+ export class StatusAttestationError extends IoWalletError {
416
+ static get code(): "ERR_STATUS_ATTESTATION_ERROR" {
417
+ return "ERR_STATUS_ATTESTATION_ERROR";
418
+ }
419
+
420
+ code = "ERR_STATUS_ATTESTATION_ERROR";
421
+
422
+ reason: string;
423
+
424
+ constructor(message: string, reason: string = "unspecified") {
425
+ super(serializeAttrs({ message, reason }));
426
+ this.reason = reason;
427
+ }
428
+ }
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,