@pagopa/io-react-native-wallet 0.1.0 → 0.2.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 (72) hide show
  1. package/README.md +1 -1
  2. package/lib/commonjs/index.js +12 -5
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/pid/index.js +7 -0
  5. package/lib/commonjs/pid/index.js.map +1 -1
  6. package/lib/commonjs/pid/issuing.js +231 -0
  7. package/lib/commonjs/pid/issuing.js.map +1 -0
  8. package/lib/commonjs/rp/__test__/index.test.js +18 -0
  9. package/lib/commonjs/rp/__test__/index.test.js.map +1 -0
  10. package/lib/commonjs/rp/index.js +116 -0
  11. package/lib/commonjs/rp/index.js.map +1 -0
  12. package/lib/commonjs/rp/types.js +72 -0
  13. package/lib/commonjs/rp/types.js.map +1 -0
  14. package/lib/commonjs/sd-jwt/types.js +1 -1
  15. package/lib/commonjs/sd-jwt/types.js.map +1 -1
  16. package/lib/commonjs/utils/dpop.js +27 -0
  17. package/lib/commonjs/utils/dpop.js.map +1 -0
  18. package/lib/commonjs/utils/errors.js +49 -1
  19. package/lib/commonjs/utils/errors.js.map +1 -1
  20. package/lib/commonjs/wallet-instance-attestation/issuing.js +3 -5
  21. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  22. package/lib/module/index.js +4 -4
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/pid/index.js +2 -1
  25. package/lib/module/pid/index.js.map +1 -1
  26. package/lib/module/pid/issuing.js +225 -0
  27. package/lib/module/pid/issuing.js.map +1 -0
  28. package/lib/module/rp/__test__/index.test.js +16 -0
  29. package/lib/module/rp/__test__/index.test.js.map +1 -0
  30. package/lib/module/rp/index.js +108 -0
  31. package/lib/module/rp/index.js.map +1 -0
  32. package/lib/module/rp/types.js +63 -0
  33. package/lib/module/rp/types.js.map +1 -0
  34. package/lib/module/sd-jwt/types.js +1 -1
  35. package/lib/module/sd-jwt/types.js.map +1 -1
  36. package/lib/module/utils/dpop.js +17 -0
  37. package/lib/module/utils/dpop.js.map +1 -0
  38. package/lib/module/utils/errors.js +46 -0
  39. package/lib/module/utils/errors.js.map +1 -1
  40. package/lib/module/wallet-instance-attestation/issuing.js +3 -5
  41. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  42. package/lib/typescript/index.d.ts +4 -2
  43. package/lib/typescript/index.d.ts.map +1 -1
  44. package/lib/typescript/pid/index.d.ts +2 -1
  45. package/lib/typescript/pid/index.d.ts.map +1 -1
  46. package/lib/typescript/pid/issuing.d.ts +93 -0
  47. package/lib/typescript/pid/issuing.d.ts.map +1 -0
  48. package/lib/typescript/rp/__test__/index.test.d.ts +2 -0
  49. package/lib/typescript/rp/__test__/index.test.d.ts.map +1 -0
  50. package/lib/typescript/rp/index.d.ts +43 -0
  51. package/lib/typescript/rp/index.d.ts.map +1 -0
  52. package/lib/typescript/rp/types.d.ts +840 -0
  53. package/lib/typescript/rp/types.d.ts.map +1 -0
  54. package/lib/typescript/sd-jwt/types.d.ts +5 -5
  55. package/lib/typescript/utils/dpop.d.ts +21 -0
  56. package/lib/typescript/utils/dpop.d.ts.map +1 -0
  57. package/lib/typescript/utils/errors.d.ts +26 -0
  58. package/lib/typescript/utils/errors.d.ts.map +1 -1
  59. package/lib/typescript/wallet-instance-attestation/issuing.d.ts +3 -3
  60. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  61. package/lib/typescript/wallet-instance-attestation/types.d.ts +4 -4
  62. package/package.json +4 -2
  63. package/src/index.ts +4 -5
  64. package/src/pid/index.ts +2 -1
  65. package/src/pid/issuing.ts +305 -0
  66. package/src/rp/__test__/index.test.ts +23 -0
  67. package/src/rp/index.ts +150 -0
  68. package/src/rp/types.ts +64 -0
  69. package/src/sd-jwt/types.ts +1 -1
  70. package/src/utils/dpop.ts +25 -0
  71. package/src/utils/errors.ts +48 -0
  72. package/src/wallet-instance-attestation/issuing.ts +9 -7
@@ -0,0 +1,150 @@
1
+ import { AuthRequestDecodeError, IoWalletError } from "../utils/errors";
2
+ import {
3
+ decode as decodeJwt,
4
+ decodeBase64,
5
+ sha256ToBase64,
6
+ SignJWT,
7
+ } from "@pagopa/io-react-native-jwt";
8
+ import { RequestObject, RpEntityConfiguration } from "./types";
9
+
10
+ import uuid from "react-native-uuid";
11
+ import type { JWK } from "@pagopa/io-react-native-jwt/lib/typescript/types";
12
+
13
+ export class RelyingPartySolution {
14
+ relyingPartyBaseUrl: string;
15
+ walletInstanceAttestation: string;
16
+ appFetch: GlobalFetch["fetch"];
17
+
18
+ constructor(
19
+ relyingPartyBaseUrl: string,
20
+ walletInstanceAttestation: string,
21
+ appFetch: GlobalFetch["fetch"] = fetch
22
+ ) {
23
+ this.relyingPartyBaseUrl = relyingPartyBaseUrl;
24
+ this.walletInstanceAttestation = walletInstanceAttestation;
25
+ this.appFetch = appFetch;
26
+ }
27
+
28
+ /**
29
+ * Decode a QR code content to an authentication request url.
30
+ * @function
31
+ * @param qrcode QR code content
32
+ *
33
+ * @returns The authentication request url
34
+ *
35
+ */
36
+ decodeAuthRequestQR(qrcode: string): string {
37
+ try {
38
+ const decoded = decodeBase64(qrcode);
39
+ const decodedUrl = new URL(decoded);
40
+ const requestUri = decodedUrl.searchParams.get("request_uri");
41
+ if (requestUri) {
42
+ return requestUri;
43
+ } else {
44
+ throw new AuthRequestDecodeError(
45
+ "Unable to obtain request_uri from QR code",
46
+ `${decodedUrl}`
47
+ );
48
+ }
49
+ } catch {
50
+ throw new AuthRequestDecodeError(
51
+ "Unable to decode QR code authentication request url",
52
+ qrcode
53
+ );
54
+ }
55
+ }
56
+ /**
57
+ * Obtain the unsigned wallet instance DPoP for authentication request
58
+ *
59
+ * @function
60
+ * @param walletInstanceAttestationJwk JWT of the Wallet Instance Attestation
61
+ * @param authRequestUrl authentication request url
62
+ *
63
+ * @returns The unsigned wallet instance DPoP
64
+ *
65
+ */
66
+ async getUnsignedWalletInstanceDPoP(
67
+ walletInstanceAttestationJwk: JWK,
68
+ authRequestUrl: string
69
+ ): Promise<string> {
70
+ return await new SignJWT({
71
+ jti: `${uuid.v4()}`,
72
+ htm: "GET",
73
+ htu: authRequestUrl,
74
+ ath: await sha256ToBase64(this.walletInstanceAttestation),
75
+ })
76
+ .setProtectedHeader({
77
+ alg: "ES256",
78
+ jwk: walletInstanceAttestationJwk,
79
+ typ: "dpop+jwt",
80
+ })
81
+ .setIssuedAt()
82
+ .setExpirationTime("1h")
83
+ .toSign();
84
+ }
85
+
86
+ /**
87
+ * Obtain the Request Object for RP authentication
88
+ *
89
+ * @function
90
+ * @param signedWalletInstanceDPoP JWT of the Wallet Instance Attestation DPoP
91
+ *
92
+ * @returns The Request Object JWT
93
+ *
94
+ */
95
+ async getRequestObject(
96
+ signedWalletInstanceDPoP: string
97
+ ): Promise<RequestObject> {
98
+ const decodedJwtDPop = await decodeJwt(signedWalletInstanceDPoP);
99
+ const requestUri = decodedJwtDPop.payload.htu as string;
100
+
101
+ const response = await this.appFetch(requestUri, {
102
+ method: "GET",
103
+ headers: {
104
+ Authorization: `DPoP ${this.walletInstanceAttestation}`,
105
+ DPoP: signedWalletInstanceDPoP,
106
+ },
107
+ });
108
+
109
+ if (response.status === 200) {
110
+ const responseText = await response.text();
111
+ const responseJwt = await decodeJwt(responseText);
112
+ const requestObj = RequestObject.parse({
113
+ header: responseJwt.protectedHeader,
114
+ payload: responseJwt.payload,
115
+ });
116
+ return requestObj;
117
+ }
118
+
119
+ throw new IoWalletError(
120
+ `Unable to obtain Request Object. Response code: ${response.status}`
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Obtain the relying party entity configuration.
126
+ */
127
+ async getEntityConfiguration(): Promise<RpEntityConfiguration> {
128
+ const wellKnownUrl = new URL(
129
+ "/.well-known/openid-federation",
130
+ this.relyingPartyBaseUrl
131
+ ).href;
132
+
133
+ const response = await this.appFetch(wellKnownUrl, {
134
+ method: "GET",
135
+ });
136
+
137
+ if (response.status === 200) {
138
+ const responseText = await response.text();
139
+ const responseJwt = await decodeJwt(responseText);
140
+ return RpEntityConfiguration.parse({
141
+ header: responseJwt.protectedHeader,
142
+ payload: responseJwt.payload,
143
+ });
144
+ }
145
+
146
+ throw new IoWalletError(
147
+ `Unable to obtain RP Entity Configuration. Response code: ${response.status}`
148
+ );
149
+ }
150
+ }
@@ -0,0 +1,64 @@
1
+ import { JWK } from "../utils/jwk";
2
+ import { UnixTime } from "../sd-jwt/types";
3
+ import * as z from "zod";
4
+
5
+ export type RequestObject = z.infer<typeof RequestObject>;
6
+ export const RequestObject = z.object({
7
+ header: z.object({
8
+ typ: z.literal("JWT"),
9
+ alg: z.string(),
10
+ kid: z.string(),
11
+ trust_chain: z.array(z.string()),
12
+ }),
13
+ payload: z.object({
14
+ iss: z.string(),
15
+ iat: UnixTime,
16
+ exp: UnixTime,
17
+ state: z.string(),
18
+ nonce: z.string(),
19
+ response_uri: z.string(),
20
+ response_type: z.literal("vp_token"),
21
+ response_mode: z.literal("direct_post.jwt"),
22
+ client_id: z.string(),
23
+ client_id_scheme: z.literal("entity_id"),
24
+ scope: z.string(),
25
+ }),
26
+ });
27
+
28
+ // TODO: This types is WIP in technical rules
29
+ export type RpEntityConfiguration = z.infer<typeof RpEntityConfiguration>;
30
+ export const RpEntityConfiguration = z.object({
31
+ header: z.object({
32
+ typ: z.literal("entity-statement+jwt"),
33
+ alg: z.string(),
34
+ kid: z.string(),
35
+ }),
36
+ payload: z.object({
37
+ exp: UnixTime,
38
+ iat: UnixTime,
39
+ iss: z.string(),
40
+ sub: z.string(),
41
+ jwks: z.object({
42
+ keys: z.array(JWK),
43
+ }),
44
+ metadata: z.object({
45
+ wallet_relying_party: z.object({
46
+ application_type: z.string(),
47
+ client_id: z.string(),
48
+ client_name: z.string(),
49
+ jwks: z.object({
50
+ keys: z.array(JWK),
51
+ }),
52
+ contacts: z.array(z.string()),
53
+ }),
54
+ federation_entity: z.object({
55
+ organization_name: z.string(),
56
+ homepage_uri: z.string(),
57
+ policy_uri: z.string(),
58
+ logo_uri: z.string(),
59
+ contacts: z.array(z.string()),
60
+ }),
61
+ }),
62
+ authority_hints: z.array(z.string()),
63
+ }),
64
+ });
@@ -25,7 +25,7 @@ export const SdJwt4VC = z.object({
25
25
  header: z.object({
26
26
  typ: z.literal("vc+sd-jwt"),
27
27
  alg: z.string(),
28
- kid: z.string(),
28
+ kid: z.string().optional(),
29
29
  trust_chain: z.array(z.string()),
30
30
  }),
31
31
  payload: z.object({
@@ -0,0 +1,25 @@
1
+ import * as z from "zod";
2
+
3
+ import { SignJWT } from "@pagopa/io-react-native-jwt";
4
+ import type { JWK } from "./jwk";
5
+
6
+ export const getUnsignedDPop = (jwk: JWK, payload: DPoPPayload): string => {
7
+ const dPop = new SignJWT(payload)
8
+ .setProtectedHeader({
9
+ alg: "ES256",
10
+ typ: "dpop+jwt",
11
+ jwk,
12
+ })
13
+ .setIssuedAt()
14
+ .setExpirationTime("1h")
15
+ .toSign();
16
+ return dPop;
17
+ };
18
+
19
+ export type DPoPPayload = z.infer<typeof DPoPPayload>;
20
+ export const DPoPPayload = z.object({
21
+ jti: z.string(),
22
+ htm: z.union([z.literal("POST"), z.literal("GET")]),
23
+ htu: z.string(),
24
+ ath: z.string().optional(),
25
+ });
@@ -72,3 +72,51 @@ export class WalletInstanceAttestationIssuingError extends IoWalletError {
72
72
  this.reason = reason;
73
73
  }
74
74
  }
75
+
76
+ /**
77
+ * An error subclass thrown when auth request decode fail
78
+ *
79
+ */
80
+ export class AuthRequestDecodeError extends IoWalletError {
81
+ static get code(): "ERR_IO_WALLET_AUTHENTICATION_REQUEST_DECODE_FAILED" {
82
+ return "ERR_IO_WALLET_AUTHENTICATION_REQUEST_DECODE_FAILED";
83
+ }
84
+
85
+ code = "ERR_IO_WALLET_AUTHENTICATION_REQUEST_DECODE_FAILED";
86
+
87
+ /** The Claim for which the validation failed. */
88
+ claim: string;
89
+
90
+ /** Reason code for the validation failure. */
91
+ reason: string;
92
+
93
+ constructor(message: string, claim = "unspecified", reason = "unspecified") {
94
+ super(message);
95
+ this.claim = claim;
96
+ this.reason = reason;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * An error subclass thrown when validation fail
102
+ *
103
+ */
104
+ export class PidIssuingError extends IoWalletError {
105
+ static get code(): "ERR_IO_WALLET_PID_ISSUING_FAILED" {
106
+ return "ERR_IO_WALLET_PID_ISSUING_FAILED";
107
+ }
108
+
109
+ code = "ERR_IO_WALLET_PID_ISSUING_FAILED";
110
+
111
+ /** The Claim for which the validation failed. */
112
+ claim: string;
113
+
114
+ /** Reason code for the validation failure. */
115
+ reason: string;
116
+
117
+ constructor(message: string, claim = "unspecified", reason = "unspecified") {
118
+ super(message);
119
+ this.claim = claim;
120
+ this.reason = reason;
121
+ }
122
+ }
@@ -8,9 +8,13 @@ import { WalletInstanceAttestationIssuingError } from "../utils/errors";
8
8
 
9
9
  export class Issuing {
10
10
  walletProviderBaseUrl: string;
11
-
12
- constructor(walletProviderBaseUrl: string) {
11
+ appFetch: GlobalFetch["fetch"];
12
+ constructor(
13
+ walletProviderBaseUrl: string,
14
+ appFetch: GlobalFetch["fetch"] = fetch
15
+ ) {
13
16
  this.walletProviderBaseUrl = walletProviderBaseUrl;
17
+ this.appFetch = appFetch;
14
18
  }
15
19
 
16
20
  /**
@@ -58,16 +62,14 @@ export class Issuing {
58
62
  * @param attestationRequest Wallet Instance Attestaion Request
59
63
  * obtained with {@link getAttestationRequestToSign}
60
64
  * @param signature Signature of the Wallet Instance Attestaion Request
61
- * @param appFetch Optional object with fetch function to use
62
65
  *
63
66
  * @returns {string} Wallet Instance Attestation
64
67
  *
65
68
  */
66
69
  async getAttestation(
67
70
  attestationRequest: string,
68
- signature: string,
69
- appFetch: GlobalFetch = { fetch }
70
- ): Promise<String> {
71
+ signature: string
72
+ ): Promise<string> {
71
73
  const signedAttestationRequest = await SignJWT.appendSignature(
72
74
  attestationRequest,
73
75
  signature
@@ -87,7 +89,7 @@ export class Issuing {
87
89
  "urn:ietf:params:oauth:client-assertion-type:jwt-key-attestation",
88
90
  assertion: signedAttestationRequest,
89
91
  };
90
- const response = await appFetch.fetch(tokenUrl, {
92
+ const response = await this.appFetch(tokenUrl, {
91
93
  method: "POST",
92
94
  headers: {
93
95
  "Content-Type": "application/json",