@pagopa/io-react-native-wallet 2.2.0 → 2.4.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 (167) hide show
  1. package/README.md +4 -3
  2. package/lib/commonjs/credential/index.js +3 -1
  3. package/lib/commonjs/credential/index.js.map +1 -1
  4. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +24 -6
  5. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
  6. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +26 -2
  7. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  8. package/lib/commonjs/credential/issuance/README.md +155 -18
  9. package/lib/commonjs/credential/issuance/index.js +9 -1
  10. package/lib/commonjs/credential/issuance/index.js.map +1 -1
  11. package/lib/commonjs/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js +57 -0
  12. package/lib/commonjs/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js.map +1 -0
  13. package/lib/commonjs/credential/issuance/mrtd-pop/02-init-challenge.js +61 -0
  14. package/lib/commonjs/credential/issuance/mrtd-pop/02-init-challenge.js.map +1 -0
  15. package/lib/commonjs/credential/issuance/mrtd-pop/03-validate-challenge.js +95 -0
  16. package/lib/commonjs/credential/issuance/mrtd-pop/03-validate-challenge.js.map +1 -0
  17. package/lib/commonjs/credential/issuance/mrtd-pop/README.md +92 -0
  18. package/lib/commonjs/credential/issuance/mrtd-pop/index.js +33 -0
  19. package/lib/commonjs/credential/issuance/mrtd-pop/index.js.map +1 -0
  20. package/lib/commonjs/credential/issuance/mrtd-pop/types.js +57 -0
  21. package/lib/commonjs/credential/issuance/mrtd-pop/types.js.map +1 -0
  22. package/lib/commonjs/credential/offer/01-start-flow.js +75 -0
  23. package/lib/commonjs/credential/offer/01-start-flow.js.map +1 -0
  24. package/lib/commonjs/credential/offer/02-fetch-credential-offer.js +45 -0
  25. package/lib/commonjs/credential/offer/02-fetch-credential-offer.js.map +1 -0
  26. package/lib/commonjs/credential/offer/README.md +174 -0
  27. package/lib/commonjs/credential/offer/errors.js +22 -0
  28. package/lib/commonjs/credential/offer/errors.js.map +1 -0
  29. package/lib/commonjs/credential/offer/index.js +25 -0
  30. package/lib/commonjs/credential/offer/index.js.map +1 -0
  31. package/lib/commonjs/credential/offer/types.js +51 -0
  32. package/lib/commonjs/credential/offer/types.js.map +1 -0
  33. package/lib/commonjs/credential/presentation/01-start-flow.js +1 -1
  34. package/lib/commonjs/credentials-catalogue/README.md +15 -0
  35. package/lib/commonjs/credentials-catalogue/fetch-and-parse-catalogue.js +42 -0
  36. package/lib/commonjs/credentials-catalogue/fetch-and-parse-catalogue.js.map +1 -0
  37. package/lib/commonjs/credentials-catalogue/index.js +13 -0
  38. package/lib/commonjs/credentials-catalogue/index.js.map +1 -0
  39. package/lib/commonjs/credentials-catalogue/types.js +99 -0
  40. package/lib/commonjs/credentials-catalogue/types.js.map +1 -0
  41. package/lib/commonjs/index.js +3 -1
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/utils/auth.js +9 -1
  44. package/lib/commonjs/utils/auth.js.map +1 -1
  45. package/lib/commonjs/utils/par.js +7 -2
  46. package/lib/commonjs/utils/par.js.map +1 -1
  47. package/lib/commonjs/utils/zod.js +28 -0
  48. package/lib/commonjs/utils/zod.js.map +1 -0
  49. package/lib/module/credential/index.js +2 -1
  50. package/lib/module/credential/index.js.map +1 -1
  51. package/lib/module/credential/issuance/03-start-user-authorization.js +24 -6
  52. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  53. package/lib/module/credential/issuance/04-complete-user-authorization.js +25 -2
  54. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  55. package/lib/module/credential/issuance/README.md +155 -18
  56. package/lib/module/credential/issuance/index.js +3 -2
  57. package/lib/module/credential/issuance/index.js.map +1 -1
  58. package/lib/module/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js +50 -0
  59. package/lib/module/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js.map +1 -0
  60. package/lib/module/credential/issuance/mrtd-pop/02-init-challenge.js +52 -0
  61. package/lib/module/credential/issuance/mrtd-pop/02-init-challenge.js.map +1 -0
  62. package/lib/module/credential/issuance/mrtd-pop/03-validate-challenge.js +85 -0
  63. package/lib/module/credential/issuance/mrtd-pop/03-validate-challenge.js.map +1 -0
  64. package/lib/module/credential/issuance/mrtd-pop/README.md +92 -0
  65. package/lib/module/credential/issuance/mrtd-pop/index.js +5 -0
  66. package/lib/module/credential/issuance/mrtd-pop/index.js.map +1 -0
  67. package/lib/module/credential/issuance/mrtd-pop/types.js +46 -0
  68. package/lib/module/credential/issuance/mrtd-pop/types.js.map +1 -0
  69. package/lib/module/credential/offer/01-start-flow.js +66 -0
  70. package/lib/module/credential/offer/01-start-flow.js.map +1 -0
  71. package/lib/module/credential/offer/02-fetch-credential-offer.js +38 -0
  72. package/lib/module/credential/offer/02-fetch-credential-offer.js.map +1 -0
  73. package/lib/module/credential/offer/README.md +174 -0
  74. package/lib/module/credential/offer/errors.js +14 -0
  75. package/lib/module/credential/offer/errors.js.map +1 -0
  76. package/lib/module/credential/offer/index.js +5 -0
  77. package/lib/module/credential/offer/index.js.map +1 -0
  78. package/lib/module/credential/offer/types.js +41 -0
  79. package/lib/module/credential/offer/types.js.map +1 -0
  80. package/lib/module/credential/presentation/01-start-flow.js +1 -1
  81. package/lib/module/credentials-catalogue/README.md +15 -0
  82. package/lib/module/credentials-catalogue/fetch-and-parse-catalogue.js +35 -0
  83. package/lib/module/credentials-catalogue/fetch-and-parse-catalogue.js.map +1 -0
  84. package/lib/module/credentials-catalogue/index.js +2 -0
  85. package/lib/module/credentials-catalogue/index.js.map +1 -0
  86. package/lib/module/credentials-catalogue/types.js +89 -0
  87. package/lib/module/credentials-catalogue/types.js.map +1 -0
  88. package/lib/module/index.js +2 -1
  89. package/lib/module/index.js.map +1 -1
  90. package/lib/module/utils/auth.js +8 -0
  91. package/lib/module/utils/auth.js.map +1 -1
  92. package/lib/module/utils/par.js +7 -2
  93. package/lib/module/utils/par.js.map +1 -1
  94. package/lib/module/utils/zod.js +20 -0
  95. package/lib/module/utils/zod.js.map +1 -0
  96. package/lib/typescript/credential/index.d.ts +2 -1
  97. package/lib/typescript/credential/index.d.ts.map +1 -1
  98. package/lib/typescript/credential/issuance/01-start-flow.d.ts +1 -1
  99. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +14 -4
  100. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  101. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +9 -1
  102. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  103. package/lib/typescript/credential/issuance/index.d.ts +4 -3
  104. package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
  105. package/lib/typescript/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.d.ts +25 -0
  106. package/lib/typescript/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.d.ts.map +1 -0
  107. package/lib/typescript/credential/issuance/mrtd-pop/02-init-challenge.d.ts +23 -0
  108. package/lib/typescript/credential/issuance/mrtd-pop/02-init-challenge.d.ts.map +1 -0
  109. package/lib/typescript/credential/issuance/mrtd-pop/03-validate-challenge.d.ts +39 -0
  110. package/lib/typescript/credential/issuance/mrtd-pop/03-validate-challenge.d.ts.map +1 -0
  111. package/lib/typescript/credential/issuance/mrtd-pop/index.d.ts +7 -0
  112. package/lib/typescript/credential/issuance/mrtd-pop/index.d.ts.map +1 -0
  113. package/lib/typescript/credential/issuance/mrtd-pop/types.d.ts +201 -0
  114. package/lib/typescript/credential/issuance/mrtd-pop/types.d.ts.map +1 -0
  115. package/lib/typescript/credential/offer/01-start-flow.d.ts +172 -0
  116. package/lib/typescript/credential/offer/01-start-flow.d.ts.map +1 -0
  117. package/lib/typescript/credential/offer/02-fetch-credential-offer.d.ts +20 -0
  118. package/lib/typescript/credential/offer/02-fetch-credential-offer.d.ts.map +1 -0
  119. package/lib/typescript/credential/offer/errors.d.ts +10 -0
  120. package/lib/typescript/credential/offer/errors.d.ts.map +1 -0
  121. package/lib/typescript/credential/offer/index.d.ts +7 -0
  122. package/lib/typescript/credential/offer/index.d.ts.map +1 -0
  123. package/lib/typescript/credential/offer/types.d.ts +264 -0
  124. package/lib/typescript/credential/offer/types.d.ts.map +1 -0
  125. package/lib/typescript/credential/presentation/01-start-flow.d.ts +1 -1
  126. package/lib/typescript/credentials-catalogue/fetch-and-parse-catalogue.d.ts +15 -0
  127. package/lib/typescript/credentials-catalogue/fetch-and-parse-catalogue.d.ts.map +1 -0
  128. package/lib/typescript/credentials-catalogue/index.d.ts +3 -0
  129. package/lib/typescript/credentials-catalogue/index.d.ts.map +1 -0
  130. package/lib/typescript/credentials-catalogue/types.d.ts +844 -0
  131. package/lib/typescript/credentials-catalogue/types.d.ts.map +1 -0
  132. package/lib/typescript/index.d.ts +2 -1
  133. package/lib/typescript/index.d.ts.map +1 -1
  134. package/lib/typescript/utils/auth.d.ts +11 -0
  135. package/lib/typescript/utils/auth.d.ts.map +1 -1
  136. package/lib/typescript/utils/par.d.ts +34 -4
  137. package/lib/typescript/utils/par.d.ts.map +1 -1
  138. package/lib/typescript/utils/zod.d.ts +15 -0
  139. package/lib/typescript/utils/zod.d.ts.map +1 -0
  140. package/package.json +21 -2
  141. package/src/credential/index.ts +2 -1
  142. package/src/credential/issuance/01-start-flow.ts +1 -1
  143. package/src/credential/issuance/03-start-user-authorization.ts +30 -7
  144. package/src/credential/issuance/04-complete-user-authorization.ts +43 -1
  145. package/src/credential/issuance/README.md +155 -18
  146. package/src/credential/issuance/index.ts +7 -1
  147. package/src/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.ts +70 -0
  148. package/src/credential/issuance/mrtd-pop/02-init-challenge.ts +82 -0
  149. package/src/credential/issuance/mrtd-pop/03-validate-challenge.ts +140 -0
  150. package/src/credential/issuance/mrtd-pop/README.md +92 -0
  151. package/src/credential/issuance/mrtd-pop/index.ts +27 -0
  152. package/src/credential/issuance/mrtd-pop/types.ts +65 -0
  153. package/src/credential/offer/01-start-flow.ts +89 -0
  154. package/src/credential/offer/02-fetch-credential-offer.ts +54 -0
  155. package/src/credential/offer/README.md +174 -0
  156. package/src/credential/offer/errors.ts +17 -0
  157. package/src/credential/offer/index.ts +16 -0
  158. package/src/credential/offer/types.ts +59 -0
  159. package/src/credential/presentation/01-start-flow.ts +1 -1
  160. package/src/credentials-catalogue/README.md +15 -0
  161. package/src/credentials-catalogue/fetch-and-parse-catalogue.ts +54 -0
  162. package/src/credentials-catalogue/index.ts +2 -0
  163. package/src/credentials-catalogue/types.ts +97 -0
  164. package/src/index.ts +2 -0
  165. package/src/utils/auth.ts +12 -0
  166. package/src/utils/par.ts +12 -4
  167. package/src/utils/zod.ts +28 -0
@@ -0,0 +1,85 @@
1
+ import { SignJWT } from "@pagopa/io-react-native-jwt";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import { IssuerResponseError } from "../../../utils/errors";
4
+ import { hasStatusOrThrow } from "../../../utils/misc";
5
+ import { createPopToken } from "../../../utils/pop";
6
+ import * as WalletInstanceAttestation from "../../../wallet-instance-attestation";
7
+ import { MrtdPopVerificationResult } from "./types";
8
+ /**
9
+ * Validates the MRTD signed challenge by sending the MRTD and IAS payloads to the issuer.
10
+ * This function must be called after {@link initChallenge} and after obtaining the MRTD and IAS payloads
11
+ * through the CIE PACE process.
12
+ *
13
+ * @param issuerConf - The issuer configuration containing the JWKS for signature verification.
14
+ * @param verifyUrl - The endpoint to call to validate the challenge.
15
+ * @param mrtd_auth_session - Session identifier for session binding obtained from the MRTD Proof JWT.
16
+ * @param mrtd_pop_nonce - Nonce value obtained from the MRTD Proof JWT.
17
+ * @param mrtd - MRTD validation data containing Data Groups and SOD.
18
+ * @param ias - IAS validation data containing Anti-Cloning Public Key, and SOD.
19
+ * @param context - The context containing the WIA crypto context used to retrieve the client public key,
20
+ * the wallet instance attestation and an optional fetch implementation.
21
+ * @returns The MRTD PoP Verification Result containing the validation nonce and redirect URI to complete the flow.
22
+ */
23
+ export const validateChallenge = async (issuerConf, verifyUrl, mrtd_auth_session, mrtd_pop_nonce, mrtd, ias, context) => {
24
+ const {
25
+ appFetch = fetch,
26
+ walletInstanceAttestation,
27
+ wiaCryptoContext
28
+ } = context;
29
+ const aud = issuerConf.openid_credential_issuer.credential_issuer;
30
+ const iss = WalletInstanceAttestation.decode(walletInstanceAttestation).payload.cnf.jwk.kid;
31
+ const signedWiaPoP = await createPopToken({
32
+ jti: `${uuidv4()}`,
33
+ aud,
34
+ iss
35
+ }, wiaCryptoContext);
36
+ const {
37
+ kid
38
+ } = await wiaCryptoContext.getPublicKey();
39
+ const mrtd_validation_jwt = await new SignJWT(wiaCryptoContext).setProtectedHeader({
40
+ typ: "mrtd-ias+jwt",
41
+ kid
42
+ }).setPayload({
43
+ iss,
44
+ aud,
45
+ document_type: "cie",
46
+ mrtd,
47
+ ias
48
+ }).setIssuedAt().setExpirationTime("5m").sign();
49
+ const requestBody = {
50
+ mrtd_validation_jwt,
51
+ mrtd_auth_session,
52
+ mrtd_pop_nonce
53
+ };
54
+ const verifyResult = await appFetch(verifyUrl, {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ "OAuth-Client-Attestation": walletInstanceAttestation,
59
+ "OAuth-Client-Attestation-PoP": signedWiaPoP
60
+ },
61
+ body: JSON.stringify(requestBody)
62
+ }).then(hasStatusOrThrow(202, IssuerResponseError)).then(res => res.json());
63
+ const verifyResultParsed = MrtdPopVerificationResult.parse(verifyResult);
64
+ return verifyResultParsed;
65
+ };
66
+
67
+ /**
68
+ * WARNING: This function must be called after {@link validateChallenge}. The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
69
+ * Builds the callback URL to which the end user should be redirected to continue the authentication flow after the MRTD challenge validation.
70
+ * @param redirectUri - The redirect URI provided by the issuer after the challenge validation to continue the authentication flow.
71
+ * @param valPopNonce - The MRTD validation PoP nonce obtained from the challenge validation response.
72
+ * @param authSession - The MRTD authentication session identifier used for session binding.
73
+ * @returns An object containing the callback URL
74
+ */
75
+ export const buildChallengeCallbackUrl = async (redirectUri, valPopNonce, authSession) => {
76
+ const params = new URLSearchParams({
77
+ mrtd_val_pop_nonce: valPopNonce,
78
+ mrtd_auth_session: authSession
79
+ });
80
+ const callbackUrl = `${redirectUri}?${params}`;
81
+ return {
82
+ callbackUrl
83
+ };
84
+ };
85
+ //# sourceMappingURL=03-validate-challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SignJWT","v4","uuidv4","IssuerResponseError","hasStatusOrThrow","createPopToken","WalletInstanceAttestation","MrtdPopVerificationResult","validateChallenge","issuerConf","verifyUrl","mrtd_auth_session","mrtd_pop_nonce","mrtd","ias","context","appFetch","fetch","walletInstanceAttestation","wiaCryptoContext","aud","openid_credential_issuer","credential_issuer","iss","decode","payload","cnf","jwk","kid","signedWiaPoP","jti","getPublicKey","mrtd_validation_jwt","setProtectedHeader","typ","setPayload","document_type","setIssuedAt","setExpirationTime","sign","requestBody","verifyResult","method","headers","body","JSON","stringify","then","res","json","verifyResultParsed","parse","buildChallengeCallbackUrl","redirectUri","valPopNonce","authSession","params","URLSearchParams","mrtd_val_pop_nonce","callbackUrl"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/03-validate-challenge.ts"],"mappings":"AAAA,SAASA,OAAO,QAA4B,6BAA6B;AACzE,SAASC,EAAE,IAAIC,MAAM,QAAQ,MAAM;AACnC,SAASC,mBAAmB,QAAQ,uBAAuB;AAC3D,SAASC,gBAAgB,QAAkB,qBAAqB;AAChE,SAASC,cAAc,QAAQ,oBAAoB;AACnD,OAAO,KAAKC,yBAAyB,MAAM,sCAAsC;AAEjF,SACEC,yBAAyB,QAGpB,SAAS;AAyBhB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAoC,GAAG,MAAAA,CAClDC,UAAU,EACVC,SAAS,EACTC,iBAAiB,EACjBC,cAAc,EACdC,IAAI,EACJC,GAAG,EACHC,OAAO,KACJ;EACH,MAAM;IACJC,QAAQ,GAAGC,KAAK;IAChBC,yBAAyB;IACzBC;EACF,CAAC,GAAGJ,OAAO;EAEX,MAAMK,GAAG,GAAGX,UAAU,CAACY,wBAAwB,CAACC,iBAAiB;EACjE,MAAMC,GAAG,GAAGjB,yBAAyB,CAACkB,MAAM,CAACN,yBAAyB,CAAC,CACpEO,OAAO,CAACC,GAAG,CAACC,GAAG,CAACC,GAAG;EAEtB,MAAMC,YAAY,GAAG,MAAMxB,cAAc,CACvC;IACEyB,GAAG,EAAG,GAAE5B,MAAM,CAAC,CAAE,EAAC;IAClBkB,GAAG;IACHG;EACF,CAAC,EACDJ,gBACF,CAAC;EAED,MAAM;IAAES;EAAI,CAAC,GAAG,MAAMT,gBAAgB,CAACY,YAAY,CAAC,CAAC;EAErD,MAAMC,mBAAmB,GAAG,MAAM,IAAIhC,OAAO,CAACmB,gBAAgB,CAAC,CAC5Dc,kBAAkB,CAAC;IAClBC,GAAG,EAAE,cAAc;IACnBN;EACF,CAAC,CAAC,CACDO,UAAU,CAAC;IACVZ,GAAG;IACHH,GAAG;IACHgB,aAAa,EAAE,KAAK;IACpBvB,IAAI;IACJC;EACF,CAAC,CAAC,CACDuB,WAAW,CAAC,CAAC,CACbC,iBAAiB,CAAC,IAAI,CAAC,CACvBC,IAAI,CAAC,CAAC;EAET,MAAMC,WAAW,GAAG;IAClBR,mBAAmB;IACnBrB,iBAAiB;IACjBC;EACF,CAAC;EAED,MAAM6B,YAAY,GAAG,MAAMzB,QAAQ,CAACN,SAAS,EAAE;IAC7CgC,MAAM,EAAE,MAAM;IACdC,OAAO,EAAE;MACP,cAAc,EAAE,kBAAkB;MAClC,0BAA0B,EAAEzB,yBAAyB;MACrD,8BAA8B,EAAEW;IAClC,CAAC;IACDe,IAAI,EAAEC,IAAI,CAACC,SAAS,CAACN,WAAW;EAClC,CAAC,CAAC,CACCO,IAAI,CAAC3C,gBAAgB,CAAC,GAAG,EAAED,mBAAmB,CAAC,CAAC,CAChD4C,IAAI,CAAEC,GAAG,IAAKA,GAAG,CAACC,IAAI,CAAC,CAAC,CAAC;EAE5B,MAAMC,kBAAkB,GAAG3C,yBAAyB,CAAC4C,KAAK,CAACV,YAAY,CAAC;EACxE,OAAOS,kBAAkB;AAC3B,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAME,yBAAoD,GAAG,MAAAA,CAClEC,WAAW,EACXC,WAAW,EACXC,WAAW,KACR;EACH,MAAMC,MAAM,GAAG,IAAIC,eAAe,CAAC;IACjCC,kBAAkB,EAAEJ,WAAW;IAC/B3C,iBAAiB,EAAE4C;EACrB,CAAC,CAAC;EAEF,MAAMI,WAAW,GAAI,GAAEN,WAAY,IAAGG,MAAO,EAAC;EAC9C,OAAO;IAAEG;EAAY,CAAC;AACxB,CAAC"}
@@ -0,0 +1,92 @@
1
+ # MRTD PoP flow
2
+
3
+ **MRTD-PoP (Machine Readable Travel Document - Proof of Possession)** flow for the IO Wallet, following the [eID Wallet L2+ Credential Issuance specification](https://italia.github.io/eid-wallet-it-docs/versione-corrente/en/credential-issuance-l2plus.html).
4
+
5
+ The MRTD-PoP flow is used to prove possession of an MRTD (such as a CIE) during the issuance of high-assurance credentials. The process involves a challenge-response protocol between the wallet and the issuer, leveraging JWTs and cryptographic attestation.
6
+
7
+ This flow is part of the [PID issuance flow](../README.md) and must be started after the `continueUserAuthorizationWithMRTDPoPChallenge` function. Once MRTD PoP is completed, the PID issuance flow must continue with the `completeUserAuthorizationWithQueryMode` function with the authorization url obtained from the validation.
8
+
9
+ > **⚠️ Important**: The entire flow must be initiated and concluded within the same web context (e.g., the same WebView instance) to maintain session continuity. Using different contexts (such as switching between an external browser and a WebView) will result in session loss and authentication failures due to cookie/session mismatch (JSESSIONID).
10
+
11
+ ## Sequence Diagram
12
+
13
+ ```mermaid
14
+ graph TD;
15
+ A@{ shape: subproc, label: "continueUserAuthorizationWithMRTDPoPChallenge" }
16
+ subgraph MRTD PoP
17
+ B[verifyAndParseChallengeInfo]
18
+ C[initChallenge]
19
+ E[validateChallenge]
20
+ end
21
+ F@{ shape: subproc, label: "completeUserAuthorizationWithQueryModeChallenge" }
22
+
23
+
24
+ A -.-> B
25
+ B --> C
26
+ C -->E
27
+ E -.-> F
28
+
29
+ ```
30
+
31
+ ## Example
32
+
33
+ ```typescript
34
+ // Verify and parse challenge info and extract challenge data: initialization url, session and nonce
35
+ const {
36
+ htu: initUrl,
37
+ mrtd_auth_session,
38
+ mrtd_pop_jwt_nonce,
39
+ } = await Credential.Issuance.MRTDPoP.verifyAndParseChallengeInfo(
40
+ issuerConf,
41
+ challenge_info,
42
+ { wiaCryptoContext }
43
+ );
44
+
45
+ // Initialize challenge and obtain the challenge text to sign the CIE PACE protocol and validation url
46
+ const {
47
+ htu: validationUrl,
48
+ challenge,
49
+ mrtd_pop_nonce,
50
+ } = await Credential.Issuance.MRTDPoP.initChallenge(
51
+ issuerConf,
52
+ initUrl,
53
+ mrtd_auth_session,
54
+ mrtd_pop_jwt_nonce,
55
+ {
56
+ walletInstanceAttestation,
57
+ wiaCryptoContext,
58
+ appFetch,
59
+ }
60
+ );
61
+
62
+ // CIE cryptographic interaction: you need to sign the challenge with the CIE through NFC interaction
63
+ const { nis, mrtds } = /* NFC interactions functions */
64
+
65
+ // Validate challenge
66
+ const { mrtd_val_pop_nonce, redirect_uri } =
67
+ await Credential.Issuance.MRTDPoP.validateChallenge(
68
+ issuerConf,
69
+ validationUrl,
70
+ mrtd_auth_session,
71
+ mrtd_pop_nonce,
72
+ mrtd,
73
+ ias,
74
+ {
75
+ walletInstanceAttestation,
76
+ wiaCryptoContext,
77
+ appFetch,
78
+ }
79
+ );
80
+
81
+ // Build the callback url
82
+ const { callbackUrl } = await Credential.Issuance.buildChallengeCallbackUrl(
83
+ redirect_uri,
84
+ mrtd_val_pop_nonce,
85
+ mrtd_auth_session
86
+ );
87
+
88
+ // The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
89
+ const authRedirectUrl = /* From a browser or webview redirect */
90
+
91
+ // Use the authRedirectUrl to continue the PID issuance flow
92
+ ```
@@ -0,0 +1,5 @@
1
+ import { verifyAndParseChallengeInfo } from "./01-verify-and-parse-challenge-info";
2
+ import { initChallenge } from "./02-init-challenge";
3
+ import { validateChallenge, buildChallengeCallbackUrl } from "./03-validate-challenge";
4
+ export { verifyAndParseChallengeInfo, initChallenge, validateChallenge, buildChallengeCallbackUrl };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["verifyAndParseChallengeInfo","initChallenge","validateChallenge","buildChallengeCallbackUrl"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/index.ts"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,sCAAsC;AAC7C,SAASC,aAAa,QAA4B,qBAAqB;AACvE,SACEC,iBAAiB,EACjBC,yBAAyB,QAGpB,yBAAyB;AAGhC,SACEH,2BAA2B,EAC3BC,aAAa,EACbC,iBAAiB,EACjBC,yBAAyB"}
@@ -0,0 +1,46 @@
1
+ import * as z from "zod";
2
+ export const MrtdProofChallengeInfo = z.object({
3
+ protectedHeader: z.object({
4
+ typ: z.literal("mrtd-ias+jwt"),
5
+ alg: z.string(),
6
+ kid: z.string()
7
+ }),
8
+ payload: z.object({
9
+ iss: z.string(),
10
+ aud: z.string(),
11
+ iat: z.number(),
12
+ exp: z.number(),
13
+ status: z.literal("require_interaction"),
14
+ type: z.literal("mrtd+ias"),
15
+ mrtd_auth_session: z.string(),
16
+ state: z.string(),
17
+ mrtd_pop_jwt_nonce: z.string(),
18
+ htu: z.string(),
19
+ htm: z.literal("POST")
20
+ })
21
+ });
22
+ export const MrtdPoPChallenge = z.object({
23
+ protectedHeader: z.object({
24
+ typ: z.literal("mrtd-ias-pop+jwt"),
25
+ alg: z.string(),
26
+ kid: z.string()
27
+ }),
28
+ payload: z.object({
29
+ iss: z.string(),
30
+ aud: z.string(),
31
+ iat: z.number(),
32
+ exp: z.number(),
33
+ challenge: z.string(),
34
+ mrtd_pop_nonce: z.string(),
35
+ mrz: z.string().optional(),
36
+ htu: z.string(),
37
+ htm: z.literal("POST")
38
+ })
39
+ });
40
+ export const MrtdPopVerificationResult = z.object({
41
+ status: z.literal("require_interaction"),
42
+ type: z.literal("redirect_to_web"),
43
+ mrtd_val_pop_nonce: z.string(),
44
+ redirect_uri: z.string()
45
+ });
46
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["z","MrtdProofChallengeInfo","object","protectedHeader","typ","literal","alg","string","kid","payload","iss","aud","iat","number","exp","status","type","mrtd_auth_session","state","mrtd_pop_jwt_nonce","htu","htm","MrtdPoPChallenge","challenge","mrtd_pop_nonce","mrz","optional","MrtdPopVerificationResult","mrtd_val_pop_nonce","redirect_uri"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/types.ts"],"mappings":"AAAA,OAAO,KAAKA,CAAC,MAAM,KAAK;AAGxB,OAAO,MAAMC,sBAAsB,GAAGD,CAAC,CAACE,MAAM,CAAC;EAC7CC,eAAe,EAAEH,CAAC,CAACE,MAAM,CAAC;IACxBE,GAAG,EAAEJ,CAAC,CAACK,OAAO,CAAC,cAAc,CAAC;IAC9BC,GAAG,EAAEN,CAAC,CAACO,MAAM,CAAC,CAAC;IACfC,GAAG,EAAER,CAAC,CAACO,MAAM,CAAC;EAChB,CAAC,CAAC;EACFE,OAAO,EAAET,CAAC,CAACE,MAAM,CAAC;IAChBQ,GAAG,EAAEV,CAAC,CAACO,MAAM,CAAC,CAAC;IACfI,GAAG,EAAEX,CAAC,CAACO,MAAM,CAAC,CAAC;IACfK,GAAG,EAAEZ,CAAC,CAACa,MAAM,CAAC,CAAC;IACfC,GAAG,EAAEd,CAAC,CAACa,MAAM,CAAC,CAAC;IACfE,MAAM,EAAEf,CAAC,CAACK,OAAO,CAAC,qBAAqB,CAAC;IACxCW,IAAI,EAAEhB,CAAC,CAACK,OAAO,CAAC,UAAU,CAAC;IAC3BY,iBAAiB,EAAEjB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC7BW,KAAK,EAAElB,CAAC,CAACO,MAAM,CAAC,CAAC;IACjBY,kBAAkB,EAAEnB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC9Ba,GAAG,EAAEpB,CAAC,CAACO,MAAM,CAAC,CAAC;IACfc,GAAG,EAAErB,CAAC,CAACK,OAAO,CAAC,MAAM;EACvB,CAAC;AACH,CAAC,CAAC;AAGF,OAAO,MAAMiB,gBAAgB,GAAGtB,CAAC,CAACE,MAAM,CAAC;EACvCC,eAAe,EAAEH,CAAC,CAACE,MAAM,CAAC;IACxBE,GAAG,EAAEJ,CAAC,CAACK,OAAO,CAAC,kBAAkB,CAAC;IAClCC,GAAG,EAAEN,CAAC,CAACO,MAAM,CAAC,CAAC;IACfC,GAAG,EAAER,CAAC,CAACO,MAAM,CAAC;EAChB,CAAC,CAAC;EACFE,OAAO,EAAET,CAAC,CAACE,MAAM,CAAC;IAChBQ,GAAG,EAAEV,CAAC,CAACO,MAAM,CAAC,CAAC;IACfI,GAAG,EAAEX,CAAC,CAACO,MAAM,CAAC,CAAC;IACfK,GAAG,EAAEZ,CAAC,CAACa,MAAM,CAAC,CAAC;IACfC,GAAG,EAAEd,CAAC,CAACa,MAAM,CAAC,CAAC;IACfU,SAAS,EAAEvB,CAAC,CAACO,MAAM,CAAC,CAAC;IACrBiB,cAAc,EAAExB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC1BkB,GAAG,EAAEzB,CAAC,CAACO,MAAM,CAAC,CAAC,CAACmB,QAAQ,CAAC,CAAC;IAC1BN,GAAG,EAAEpB,CAAC,CAACO,MAAM,CAAC,CAAC;IACfc,GAAG,EAAErB,CAAC,CAACK,OAAO,CAAC,MAAM;EACvB,CAAC;AACH,CAAC,CAAC;AAiBF,OAAO,MAAMsB,yBAAyB,GAAG3B,CAAC,CAACE,MAAM,CAAC;EAChDa,MAAM,EAAEf,CAAC,CAACK,OAAO,CAAC,qBAAqB,CAAC;EACxCW,IAAI,EAAEhB,CAAC,CAACK,OAAO,CAAC,iBAAiB,CAAC;EAClCuB,kBAAkB,EAAE5B,CAAC,CAACO,MAAM,CAAC,CAAC;EAC9BsB,YAAY,EAAE7B,CAAC,CAACO,MAAM,CAAC;AACzB,CAAC,CAAC"}
@@ -0,0 +1,66 @@
1
+ import * as z from "zod";
2
+ import { Logger, LogLevel } from "../../utils/logging";
3
+ import { stringToJSONSchema } from "../../utils/zod";
4
+ import { InvalidQRCodeError } from "./errors";
5
+ import { CredentialOfferSchema } from "./types";
6
+ const CREDENTIAL_OFFER_SCHEMES = ["openid-credential-offer://", "haip://"];
7
+ const CREDENTIAL_OFFER_PARAM = "credential_offer";
8
+ const CREDENTIAL_OFFER_URI_PARAM = "credential_offer_uri";
9
+ const CredentialOfferParams = z.union([z.object({
10
+ credential_offer: stringToJSONSchema.pipe(CredentialOfferSchema),
11
+ credential_offer_uri: z.undefined()
12
+ }), z.object({
13
+ credential_offer: z.undefined(),
14
+ credential_offer_uri: z.string().url()
15
+ })]);
16
+
17
+ /**
18
+ * The beginning of the credential offer flow.
19
+ * To be implemented according to the user touchpoint
20
+ *
21
+ * @param params Credential offer encoded url
22
+ * @returns Object containing the credential offer by reference or by value
23
+ */
24
+
25
+ /**
26
+ * Start a credential offer flow by validating and parse an encoded url
27
+ * extracted from a QR code or a deep link.
28
+ *
29
+ * @param params The encoded url to be validated and parsed
30
+ * @returns Object containing the credential offer by reference or by value
31
+ * @throws If the provided encoded url is not valid
32
+ */
33
+ export const startFlowFromQR = encodedUrl => {
34
+ const hasValidScheme = CREDENTIAL_OFFER_SCHEMES.some(prefix => encodedUrl.startsWith(prefix));
35
+ if (!hasValidScheme) {
36
+ throw new InvalidQRCodeError("Url must have one of the supported schemes");
37
+ }
38
+ const url = new URL(encodedUrl);
39
+ const offerParam = url.searchParams.get(CREDENTIAL_OFFER_PARAM);
40
+ const offerUriParam = url.searchParams.get(CREDENTIAL_OFFER_URI_PARAM);
41
+ if (offerParam) {
42
+ const decoded = decodeURIComponent(offerParam);
43
+ const result = CredentialOfferParams.safeParse({
44
+ credential_offer: decoded
45
+ });
46
+ if (result.success) {
47
+ return result.data;
48
+ }
49
+ Logger.log(LogLevel.ERROR, `Invalid credential offer object found in QR Code: ${result.error.message}`);
50
+ throw new InvalidQRCodeError(result.error.message);
51
+ }
52
+ if (offerUriParam) {
53
+ const decoded = decodeURIComponent(offerUriParam);
54
+ const result = CredentialOfferParams.safeParse({
55
+ credential_offer_uri: decoded
56
+ });
57
+ if (result.success) {
58
+ return result.data;
59
+ }
60
+ Logger.log(LogLevel.ERROR, `Invalid credential offer URI found in QR Code: ${result.error.message}`);
61
+ throw new InvalidQRCodeError(result.error.message);
62
+ }
63
+ Logger.log(LogLevel.ERROR, `Invalid credential offer QR Code:`);
64
+ throw new InvalidQRCodeError("QR Code does not contain valid params");
65
+ };
66
+ //# sourceMappingURL=01-start-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["z","Logger","LogLevel","stringToJSONSchema","InvalidQRCodeError","CredentialOfferSchema","CREDENTIAL_OFFER_SCHEMES","CREDENTIAL_OFFER_PARAM","CREDENTIAL_OFFER_URI_PARAM","CredentialOfferParams","union","object","credential_offer","pipe","credential_offer_uri","undefined","string","url","startFlowFromQR","encodedUrl","hasValidScheme","some","prefix","startsWith","URL","offerParam","searchParams","get","offerUriParam","decoded","decodeURIComponent","result","safeParse","success","data","log","ERROR","error","message"],"sourceRoot":"../../../../src","sources":["credential/offer/01-start-flow.ts"],"mappings":"AAAA,OAAO,KAAKA,CAAC,MAAM,KAAK;AACxB,SAASC,MAAM,EAAEC,QAAQ,QAAQ,qBAAqB;AACtD,SAASC,kBAAkB,QAAQ,iBAAiB;AACpD,SAASC,kBAAkB,QAAQ,UAAU;AAC7C,SAASC,qBAAqB,QAAQ,SAAS;AAE/C,MAAMC,wBAAwB,GAAG,CAAC,4BAA4B,EAAE,SAAS,CAAC;AAC1E,MAAMC,sBAAsB,GAAG,kBAAkB;AACjD,MAAMC,0BAA0B,GAAG,sBAAsB;AAEzD,MAAMC,qBAAqB,GAAGT,CAAC,CAACU,KAAK,CAAC,CACpCV,CAAC,CAACW,MAAM,CAAC;EACPC,gBAAgB,EAAET,kBAAkB,CAACU,IAAI,CAACR,qBAAqB,CAAC;EAChES,oBAAoB,EAAEd,CAAC,CAACe,SAAS,CAAC;AACpC,CAAC,CAAC,EACFf,CAAC,CAACW,MAAM,CAAC;EACPC,gBAAgB,EAAEZ,CAAC,CAACe,SAAS,CAAC,CAAC;EAC/BD,oBAAoB,EAAEd,CAAC,CAACgB,MAAM,CAAC,CAAC,CAACC,GAAG,CAAC;AACvC,CAAC,CAAC,CACH,CAAC;;AAGF;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,eAA0B,GAAIC,UAAU,IAAK;EACxD,MAAMC,cAAc,GAAGd,wBAAwB,CAACe,IAAI,CAAEC,MAAM,IAC1DH,UAAU,CAACI,UAAU,CAACD,MAAM,CAC9B,CAAC;EAED,IAAI,CAACF,cAAc,EAAE;IACnB,MAAM,IAAIhB,kBAAkB,CAAC,4CAA4C,CAAC;EAC5E;EAEA,MAAMa,GAAG,GAAG,IAAIO,GAAG,CAACL,UAAU,CAAC;EAC/B,MAAMM,UAAU,GAAGR,GAAG,CAACS,YAAY,CAACC,GAAG,CAACpB,sBAAsB,CAAC;EAC/D,MAAMqB,aAAa,GAAGX,GAAG,CAACS,YAAY,CAACC,GAAG,CAACnB,0BAA0B,CAAC;EAEtE,IAAIiB,UAAU,EAAE;IACd,MAAMI,OAAO,GAAGC,kBAAkB,CAACL,UAAU,CAAC;IAC9C,MAAMM,MAAM,GAAGtB,qBAAqB,CAACuB,SAAS,CAAC;MAC7CpB,gBAAgB,EAAEiB;IACpB,CAAC,CAAC;IAEF,IAAIE,MAAM,CAACE,OAAO,EAAE;MAClB,OAAOF,MAAM,CAACG,IAAI;IACpB;IAEAjC,MAAM,CAACkC,GAAG,CACRjC,QAAQ,CAACkC,KAAK,EACb,qDAAoDL,MAAM,CAACM,KAAK,CAACC,OAAQ,EAC5E,CAAC;IACD,MAAM,IAAIlC,kBAAkB,CAAC2B,MAAM,CAACM,KAAK,CAACC,OAAO,CAAC;EACpD;EAEA,IAAIV,aAAa,EAAE;IACjB,MAAMC,OAAO,GAAGC,kBAAkB,CAACF,aAAa,CAAC;IACjD,MAAMG,MAAM,GAAGtB,qBAAqB,CAACuB,SAAS,CAAC;MAC7ClB,oBAAoB,EAAEe;IACxB,CAAC,CAAC;IAEF,IAAIE,MAAM,CAACE,OAAO,EAAE;MAClB,OAAOF,MAAM,CAACG,IAAI;IACpB;IAEAjC,MAAM,CAACkC,GAAG,CACRjC,QAAQ,CAACkC,KAAK,EACb,kDAAiDL,MAAM,CAACM,KAAK,CAACC,OAAQ,EACzE,CAAC;IACD,MAAM,IAAIlC,kBAAkB,CAAC2B,MAAM,CAACM,KAAK,CAACC,OAAO,CAAC;EACpD;EAEArC,MAAM,CAACkC,GAAG,CAACjC,QAAQ,CAACkC,KAAK,EAAG,mCAAkC,CAAC;EAC/D,MAAM,IAAIhC,kBAAkB,CAAC,uCAAuC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { IssuerResponseError } from "../../utils/errors";
2
+ import { Logger, LogLevel } from "../../utils/logging";
3
+ import { hasStatusOrThrow } from "../../utils/misc";
4
+ import { InvalidCredentialOfferError } from "./errors";
5
+ import { CredentialOfferSchema } from "./types";
6
+ /**
7
+ * Fetches and validates a credential offer from a given URI.
8
+ *
9
+ * This function performs an HTTP GET request to the specified `credentialOfferUri`,
10
+ * expecting a JSON response that matches the `CredentialOfferSchema`. If the response
11
+ * is invalid or does not conform to the schema, an error is logged and an
12
+ * `InvalidCredentialOfferError` is thrown.
13
+ *
14
+ * @param credentialOfferUri - The URI from which to fetch the credential offer.
15
+ * @param context - Optional context object that may provide a custom `appFetch` implementation.
16
+ * @returns The validated credential offer data.
17
+ * @throws {IssuerResponseError} If the HTTP response status is not 200.
18
+ * @throws {InvalidCredentialOfferError} If the response does not match the expected schema.
19
+ */
20
+ export const fetchCredentialOffer = async function (uri) {
21
+ let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
22
+ const {
23
+ appFetch = fetch
24
+ } = context;
25
+ const response = await appFetch(uri, {
26
+ method: "GET",
27
+ headers: {
28
+ Accept: "application/json"
29
+ }
30
+ }).then(hasStatusOrThrow(200, IssuerResponseError)).then(reqUri => reqUri.json());
31
+ const credentialOffer = CredentialOfferSchema.safeParse(response);
32
+ if (!credentialOffer.success) {
33
+ Logger.log(LogLevel.ERROR, `Invalid credential offer fetched from URI: ${uri} - ${credentialOffer.error.message}`);
34
+ throw new InvalidCredentialOfferError(`Invalid credential offer fetched from URI: ${uri} - ${credentialOffer.error.message}`);
35
+ }
36
+ return credentialOffer.data;
37
+ };
38
+ //# sourceMappingURL=02-fetch-credential-offer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["IssuerResponseError","Logger","LogLevel","hasStatusOrThrow","InvalidCredentialOfferError","CredentialOfferSchema","fetchCredentialOffer","uri","context","arguments","length","undefined","appFetch","fetch","response","method","headers","Accept","then","reqUri","json","credentialOffer","safeParse","success","log","ERROR","error","message","data"],"sourceRoot":"../../../../src","sources":["credential/offer/02-fetch-credential-offer.ts"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AACxD,SAASC,MAAM,EAAEC,QAAQ,QAAQ,qBAAqB;AACtD,SAASC,gBAAgB,QAAQ,kBAAkB;AACnD,SAASC,2BAA2B,QAAQ,UAAU;AAEtD,SAASC,qBAAqB,QAAQ,SAAS;AAS/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,oBAAwC,GAAG,eAAAA,CACtDC,GAAW,EAER;EAAA,IADHC,OAAO,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC,CAAC;EAEZ,MAAM;IAAEG,QAAQ,GAAGC;EAAM,CAAC,GAAGL,OAAO;EAEpC,MAAMM,QAAQ,GAAG,MAAMF,QAAQ,CAACL,GAAG,EAAE;IACnCQ,MAAM,EAAE,KAAK;IACbC,OAAO,EAAE;MAAEC,MAAM,EAAE;IAAmB;EACxC,CAAC,CAAC,CACCC,IAAI,CAACf,gBAAgB,CAAC,GAAG,EAAEH,mBAAmB,CAAC,CAAC,CAChDkB,IAAI,CAAEC,MAAM,IAAKA,MAAM,CAACC,IAAI,CAAC,CAAC,CAAC;EAElC,MAAMC,eAAe,GAAGhB,qBAAqB,CAACiB,SAAS,CAACR,QAAQ,CAAC;EACjE,IAAI,CAACO,eAAe,CAACE,OAAO,EAAE;IAC5BtB,MAAM,CAACuB,GAAG,CACRtB,QAAQ,CAACuB,KAAK,EACb,8CAA6ClB,GAAI,MAAKc,eAAe,CAACK,KAAK,CAACC,OAAQ,EACvF,CAAC;IACD,MAAM,IAAIvB,2BAA2B,CAClC,8CAA6CG,GAAI,MAAKc,eAAe,CAACK,KAAK,CAACC,OAAQ,EACvF,CAAC;EACH;EAEA,OAAON,eAAe,CAACO,IAAI;AAC7B,CAAC"}
@@ -0,0 +1,174 @@
1
+ # Credential Offer
2
+
3
+ This flow handles the initial step of credential issuance by processing Credential Offers from Credential Issuers. The Credential Offer contains information about what credentials are available and how they can be obtained. Each step in the flow is imported from the related file which is named with a sequential number.
4
+
5
+ A Credential Offer can be received by the Wallet in two ways: **by value** (complete offer embedded in the URL) or **by reference** (URL pointing to the offer endpoint). The offer specifies which credentials are available and what authorization flows are supported by the issuer.
6
+
7
+ The implementation follows the [OpenID for Verifiable Credential Issuance 1.0](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint) specification and supports both Authorization Code flow and Pre-Authorized Code flow.
8
+
9
+ ## Sequence Diagram
10
+
11
+ ```mermaid
12
+ sequenceDiagram
13
+ autonumber
14
+ participant U as User
15
+ participant W as Wallet
16
+ participant CI as Credential Issuer
17
+
18
+ CI->>U: QR Code / Deep Link with Credential Offer
19
+ U->>W: Scan QR / Click Link
20
+ W->>W: startFlowFromQR: Parse offer parameters
21
+ alt Credential Offer by Reference
22
+ W->>CI: fetchCredentialOffer: Fetch offer from URI
23
+ CI->>W: Return Credential Offer JSON
24
+ end
25
+ W->>W: Validate Credential Offer schema
26
+ W->>W: Determine available grant types
27
+ Note over W: Flow continues with credential issuance
28
+ ```
29
+
30
+ ## Grant Types
31
+
32
+ The Credential Offer supports two OAuth 2.0 grant types that determine how authorization is handled:
33
+
34
+ ### Authorization Code Flow
35
+
36
+ Used for interactive flows where user authentication and consent are required at the Authorization Server.
37
+
38
+ - **`issuer_state`** (optional): Binds the authorization request to a specific issuer context
39
+ - **`authorization_server`** (optional): Identifies which authorization server to use when multiple are available
40
+
41
+ ### Pre-Authorized Code Flow
42
+
43
+ Used when the user has already been authenticated and authorized out-of-band. The issuer provides a pre-authorized code that can be exchanged directly for credentials.
44
+
45
+ - **`pre-authorized_code`**: Short-lived single-use authorization code
46
+ - **`tx_code`** (optional): Additional transaction code requirements for security
47
+ - **`authorization_server`** (optional): Identifies which authorization server to use
48
+
49
+ ## Transaction Code Requirements
50
+
51
+ When a transaction code is required for Pre-Authorized Code flow, the following parameters control the user experience:
52
+
53
+ | Parameter | Type | Description |
54
+ | ------------- | ----------------------- | ---------------------------------------------------- |
55
+ | `input_mode` | `"numeric"` \| `"text"` | Character set for the code (default: `"numeric"`) |
56
+ | `length` | number | Expected code length to optimize input UI |
57
+ | `description` | string | User guidance (max 300 chars) for obtaining the code |
58
+
59
+ ## Credential Offer Transmission
60
+
61
+ ### By Value
62
+
63
+ The complete Credential Offer is embedded in the URL parameter:
64
+
65
+ ```
66
+ openid-credential-offer://?credential_offer=%7B%22credential_issuer%22...
67
+ ```
68
+
69
+ ### By Reference
70
+
71
+ A URL points to an endpoint serving the Credential Offer:
72
+
73
+ ```
74
+ openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fserver.example.com%2Foffer
75
+ ```
76
+
77
+ When using by reference, the Wallet fetches the offer via HTTP GET with `Accept: application/json`.
78
+
79
+ ## Mapped Results
80
+
81
+ The following errors are mapped during credential offer processing:
82
+
83
+ | Error | Description |
84
+ | ----------------------------- | ---------------------------------------------------------------------------------- |
85
+ | `InvalidQRCodeError` | The QR code format is invalid or doesn't contain valid credential offer parameters |
86
+ | `InvalidCredentialOfferError` | The credential offer schema validation failed or contains invalid data |
87
+
88
+ ## Examples
89
+
90
+ <details>
91
+ <summary>Credential Offer processing flow</summary>
92
+
93
+ ```ts
94
+ // Parse QR code or deep link
95
+ const qrCode =
96
+ "openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.example.com%2Foffer";
97
+ const { credential_offer_uri } = startFlowFromQR(qrCode);
98
+
99
+ // Fetch the credential offer if by reference
100
+ const offer = await fetchCredentialOffer(credential_offer_uri, { appFetch });
101
+
102
+ console.log(offer);
103
+ // {
104
+ // credential_issuer: "https://issuer.example.com",
105
+ // credential_configuration_ids: ["UniversityDegree", "DriverLicense"],
106
+ // grants: {
107
+ // authorization_code: {
108
+ // issuer_state: "xyz123"
109
+ // },
110
+ // "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
111
+ // "pre-authorized_code": "SplxlOBeZQQYbYS6WxSbIA",
112
+ // tx_code: {
113
+ // length: 6,
114
+ // input_mode: "numeric",
115
+ // description: "Enter the code sent to your email"
116
+ // }
117
+ // }
118
+ // }
119
+ // }
120
+ ```
121
+
122
+ </details>
123
+
124
+ <details>
125
+ <summary>Pre-Authorized Code with Transaction Code</summary>
126
+
127
+ ```ts
128
+ const offer: CredentialOffer = {
129
+ credential_issuer: "https://university.example.edu",
130
+ credential_configuration_ids: ["DiplomaCredential"],
131
+ grants: {
132
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
133
+ "pre-authorized_code": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
134
+ tx_code: {
135
+ length: 4,
136
+ input_mode: "numeric",
137
+ description: "Check your email for the verification code",
138
+ },
139
+ },
140
+ },
141
+ };
142
+
143
+ // The user would need to:
144
+ // 1. Check their email for a 4-digit numeric code
145
+ // 2. Enter it in the wallet when prompted
146
+ // 3. The wallet uses both pre-authorized_code and tx_code in the token request
147
+ ```
148
+
149
+ </details>
150
+
151
+ <details>
152
+ <summary>Authorization Code Flow</summary>
153
+
154
+ ```ts
155
+ const offer: CredentialOffer = {
156
+ credential_issuer: "https://dmv.example.gov",
157
+ credential_configuration_ids: ["org.iso.18013.5.1.mDL"],
158
+ grants: {
159
+ authorization_code: {
160
+ issuer_state: "af0ifjsldkj",
161
+ authorization_server: "https://auth.dmv.example.gov",
162
+ },
163
+ },
164
+ };
165
+
166
+ // This would lead to:
167
+ // 1. User authentication at the authorization server
168
+ // 2. User consent for credential issuance
169
+ // 3. Authorization code returned to wallet
170
+ // 4. Wallet exchanges code for access token
171
+ // 5. Wallet uses access token to request credential
172
+ ```
173
+
174
+ </details>
@@ -0,0 +1,14 @@
1
+ import { IoWalletError } from "../../utils/errors";
2
+ export class InvalidCredentialOfferError extends IoWalletError {
3
+ code = "ERR_INVALID_CREDENTIAL_OFFER";
4
+ constructor(message) {
5
+ super(message);
6
+ }
7
+ }
8
+ export class InvalidQRCodeError extends IoWalletError {
9
+ code = "ERR_INVALID_QR_CODE";
10
+ constructor(message) {
11
+ super(message);
12
+ }
13
+ }
14
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["IoWalletError","InvalidCredentialOfferError","code","constructor","message","InvalidQRCodeError"],"sourceRoot":"../../../../src","sources":["credential/offer/errors.ts"],"mappings":"AAAA,SAASA,aAAa,QAAQ,oBAAoB;AAElD,OAAO,MAAMC,2BAA2B,SAASD,aAAa,CAAC;EAC7DE,IAAI,GAAG,8BAA8B;EAErCC,WAAWA,CAACC,OAAgB,EAAE;IAC5B,KAAK,CAACA,OAAO,CAAC;EAChB;AACF;AAEA,OAAO,MAAMC,kBAAkB,SAASL,aAAa,CAAC;EACpDE,IAAI,GAAG,qBAAqB;EAE5BC,WAAWA,CAACC,OAAgB,EAAE;IAC5B,KAAK,CAACA,OAAO,CAAC;EAChB;AACF"}
@@ -0,0 +1,5 @@
1
+ import { startFlowFromQR } from "./01-start-flow";
2
+ import { fetchCredentialOffer } from "./02-fetch-credential-offer";
3
+ import * as Errors from "./errors";
4
+ export { Errors, fetchCredentialOffer, startFlowFromQR };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["startFlowFromQR","fetchCredentialOffer","Errors"],"sourceRoot":"../../../../src","sources":["credential/offer/index.ts"],"mappings":"AAAA,SAASA,eAAe,QAAwB,iBAAiB;AACjE,SACEC,oBAAoB,QAEf,6BAA6B;AACpC,OAAO,KAAKC,MAAM,MAAM,UAAU;AASlC,SAASA,MAAM,EAAED,oBAAoB,EAAED,eAAe"}
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * OAuth 2.0 Authorization Code flow parameters.
5
+ */
6
+ export const AuthorizationCodeGrantSchema = z.object({
7
+ issuer_state: z.string().optional(),
8
+ authorization_server: z.string().url().optional()
9
+ });
10
+ /**
11
+ * Transaction Code requirements for Pre-Authorized Code flow.
12
+ */
13
+ export const TransactionCodeSchema = z.object({
14
+ input_mode: z.enum(["numeric", "text"]).optional(),
15
+ length: z.number().int().positive().optional(),
16
+ description: z.string().max(300).optional()
17
+ });
18
+ /**
19
+ * Pre-Authorized Code flow parameters.
20
+ */
21
+ export const PreAuthorizedCodeGrantSchema = z.object({
22
+ "pre-authorized_code": z.string(),
23
+ tx_code: TransactionCodeSchema.optional(),
24
+ authorization_server: z.string().url().optional()
25
+ });
26
+ /**
27
+ * Supported grant types for Credential Offer.
28
+ */
29
+ export const GrantsSchema = z.object({
30
+ authorization_code: AuthorizationCodeGrantSchema.optional(),
31
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code": PreAuthorizedCodeGrantSchema.optional()
32
+ });
33
+ /**
34
+ * Credential Offer object as defined in OpenID4VCI Section 4.1.1.
35
+ */
36
+ export const CredentialOfferSchema = z.object({
37
+ credential_issuer: z.string().url(),
38
+ credential_configuration_ids: z.array(z.string()).min(1),
39
+ grants: GrantsSchema.optional()
40
+ });
41
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["z","AuthorizationCodeGrantSchema","object","issuer_state","string","optional","authorization_server","url","TransactionCodeSchema","input_mode","enum","length","number","int","positive","description","max","PreAuthorizedCodeGrantSchema","tx_code","GrantsSchema","authorization_code","CredentialOfferSchema","credential_issuer","credential_configuration_ids","array","min","grants"],"sourceRoot":"../../../../src","sources":["credential/offer/types.ts"],"mappings":"AAAA,SAASA,CAAC,QAAQ,KAAK;;AAEvB;AACA;AACA;AACA,OAAO,MAAMC,4BAA4B,GAAGD,CAAC,CAACE,MAAM,CAAC;EACnDC,YAAY,EAAEH,CAAC,CAACI,MAAM,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC;EACnCC,oBAAoB,EAAEN,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,GAAG,CAAC,CAAC,CAACF,QAAQ,CAAC;AAClD,CAAC,CAAC;AAMF;AACA;AACA;AACA,OAAO,MAAMG,qBAAqB,GAAGR,CAAC,CAACE,MAAM,CAAC;EAC5CO,UAAU,EAAET,CAAC,CAACU,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EAClDM,MAAM,EAAEX,CAAC,CAACY,MAAM,CAAC,CAAC,CAACC,GAAG,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC,CAACT,QAAQ,CAAC,CAAC;EAC9CU,WAAW,EAAEf,CAAC,CAACI,MAAM,CAAC,CAAC,CAACY,GAAG,CAAC,GAAG,CAAC,CAACX,QAAQ,CAAC;AAC5C,CAAC,CAAC;AAIF;AACA;AACA;AACA,OAAO,MAAMY,4BAA4B,GAAGjB,CAAC,CAACE,MAAM,CAAC;EACnD,qBAAqB,EAAEF,CAAC,CAACI,MAAM,CAAC,CAAC;EACjCc,OAAO,EAAEV,qBAAqB,CAACH,QAAQ,CAAC,CAAC;EACzCC,oBAAoB,EAAEN,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,GAAG,CAAC,CAAC,CAACF,QAAQ,CAAC;AAClD,CAAC,CAAC;AAMF;AACA;AACA;AACA,OAAO,MAAMc,YAAY,GAAGnB,CAAC,CAACE,MAAM,CAAC;EACnCkB,kBAAkB,EAAEnB,4BAA4B,CAACI,QAAQ,CAAC,CAAC;EAC3D,sDAAsD,EACpDY,4BAA4B,CAACZ,QAAQ,CAAC;AAC1C,CAAC,CAAC;AAIF;AACA;AACA;AACA,OAAO,MAAMgB,qBAAqB,GAAGrB,CAAC,CAACE,MAAM,CAAC;EAC5CoB,iBAAiB,EAAEtB,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,GAAG,CAAC,CAAC;EACnCgB,4BAA4B,EAAEvB,CAAC,CAACwB,KAAK,CAACxB,CAAC,CAACI,MAAM,CAAC,CAAC,CAAC,CAACqB,GAAG,CAAC,CAAC,CAAC;EACxDC,MAAM,EAAEP,YAAY,CAACd,QAAQ,CAAC;AAChC,CAAC,CAAC"}
@@ -9,7 +9,7 @@ const PresentationParams = z.object({
9
9
 
10
10
  /**
11
11
  * The beginning of the presentation flow.
12
- * To be implemented accordind to the user touchpoint
12
+ * To be implemented according to the user touchpoint
13
13
  *
14
14
  * @param params Presentation parameters, depending on the starting touchpoint
15
15
  * @returns The url for the Relying Party to connect with
@@ -0,0 +1,15 @@
1
+ # Digital Credentials Catalogue
2
+
3
+ Module that manages the [**Digital Credentials Catalogue**](https://italia.github.io/eid-wallet-it-docs/releases/1.1.0/en/registry-catalogue.html) published by the Trust Anchor.
4
+
5
+ The module allows:
6
+ - Fetching, verifying and parsing the catalogue's JWT.
7
+
8
+ ## Usage
9
+
10
+ ```ts
11
+ // Fetch the catalogue
12
+ const TRUST_ANCHOR_BASE_URL = "https://pre.ta.wallet.ipzs.it";
13
+ const credentialsCatalogue =
14
+ await CredentialsCatalogue.fetchAndParseCatalogue(TRUST_ANCHOR_BASE_URL);
15
+ ```