cozy-client 27.7.0 → 27.8.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [27.8.0](https://github.com/cozy/cozy-client/compare/v27.7.0...v27.8.0) (2022-01-11)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Rebuild `permission.d.ts` to fit CI version ([380db52](https://github.com/cozy/cozy-client/commit/380db528004e9a64b244a99bb238351fac4f2a05))
12
+ * UseQuery returns fetch method ([93e7f3d](https://github.com/cozy/cozy-client/commit/93e7f3d217353003795d43dfedf1397bb64815af))
13
+
14
+
15
+ ### Features
16
+
17
+ * Add `react-native-google-safetynet` module as peerDependencies ([cd4b37f](https://github.com/cozy/cozy-client/commit/cd4b37fc3353b702035e773b2b423ef7e7aa3156))
18
+ * Add `react-native-ios11-devicecheck` module as peerDependencies ([b9ed2e9](https://github.com/cozy/cozy-client/commit/b9ed2e9c42518a50961f5b233b5f6ae1894ed7d6))
19
+ * Add flagship certification mechanism ([fe8474c](https://github.com/cozy/cozy-client/commit/fe8474c3286393c996eb5704184de652aa8028fe))
20
+
21
+
22
+
23
+
24
+
6
25
  # [27.7.0](https://github.com/cozy/cozy-client/compare/v27.6.5...v27.7.0) (2022-01-03)
7
26
 
8
27
 
@@ -83,6 +83,8 @@ var _jsonStableStringify = _interopRequireDefault(require("json-stable-stringify
83
83
 
84
84
  var _promiseCache = _interopRequireDefault(require("./promise-cache"));
85
85
 
86
+ var _flagshipCertification = require("./flagship-certification/flagship-certification");
87
+
86
88
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
87
89
 
88
90
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
@@ -1958,9 +1960,18 @@ var CozyClient = /*#__PURE__*/function () {
1958
1960
  return stackClient.register();
1959
1961
 
1960
1962
  case 3:
1963
+ if (!stackClient.oauthOptions.shouldRequireFlagshipPermissions) {
1964
+ _context14.next = 6;
1965
+ break;
1966
+ }
1967
+
1968
+ _context14.next = 6;
1969
+ return (0, _flagshipCertification.certifyFlagship)(stackClient.oauthOptions.certificationConfig, this);
1970
+
1971
+ case 6:
1961
1972
  return _context14.abrupt("return", this.authorize(openURLCallback));
1962
1973
 
1963
- case 4:
1974
+ case 7:
1964
1975
  case "end":
1965
1976
  return _context14.stop();
1966
1977
  }
@@ -2452,7 +2463,7 @@ var CozyClient = /*#__PURE__*/function () {
2452
2463
  CozyClient.hooks = CozyClient.hooks || {};
2453
2464
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2454
2465
 
2455
- CozyClient.version = "27.7.0";
2466
+ CozyClient.version = "27.8.0";
2456
2467
 
2457
2468
  _microee.default.mixin(CozyClient);
2458
2469
 
@@ -0,0 +1,96 @@
1
+ # Flagship certification
2
+
3
+ Flagship certification is the process of verifying that the current running app is a genuine Cozy application.
4
+
5
+ This verification is done by querying an app certificate from the app store (Apple AppStore or Google Play) and sending the resulting certificate to `cozy-stack`. Then `cozy-stack` will be able to an analyze the certificate and conclude if the app is genuine or not.
6
+
7
+ ## Nomenclature
8
+
9
+ - `flagship`: refers to the Cozy main application
10
+ - `store certification`: process of verifying app's genuineness through the platform's store
11
+ - `attestation`: result from the `store certification`
12
+ - `challenge`: unique token given to the app by `cozy-stack` that may be encrypted in the `attestation` as a proof of authenticity
13
+ - `nonce`: data type used to store the `challenge` token
14
+ - `SafetyNet`: Google's implementation of the `store certification`
15
+ - `AppAttest`: Apple's implementation of the `store certification`
16
+
17
+ ## Android certification
18
+
19
+ Android certification is based on [SafetyNet](https://developer.android.com/training/safetynet/index.html).
20
+
21
+ This process requires to query a `challenge` from `cozy-stack` and to use it to init the `store certification` process through `SafetyNet`. Then the received `attestation` is send to `cozy-stack` for verification.
22
+
23
+ The resulting `attestation` is in the form of a `JSON Web Signature` that embbed the following `JSON`:
24
+ ```json
25
+ {
26
+ "apkCertificateDigestSha256": [
27
+ "base64 encoded, SHA-256 hash of the certificate used to sign requesting app="
28
+ ],
29
+ "apkDigestSha256": "kNv83tJLFqwliYQ/6HUPCeGkBzLLCX/nvT+EF3OEB2I=",
30
+ "apkPackageName": "com.package.name.of.requesting.app",
31
+ "basicIntegrity": true,
32
+ "ctsProfileMatch": true,
33
+ "evaluationType": "BASIC",
34
+ "nonce": "R2Rra24fVm5xa2Mg",
35
+ "timestampMs": 9860437986543
36
+ }
37
+ ```
38
+
39
+ The `attestation`'s content is described in the SafetyNet's documentation: https://developer.android.com/training/safetynet/attestation#use-response-server
40
+
41
+ ## iOS certification
42
+
43
+ iOS certification is based on [AppAttest](https://developer.apple.com/documentation/devicecheck).
44
+
45
+ This process requires to query a `challenge` from `cozy-stack` and to use it to init the `store certification` process through `AppAttest`. Then the received `attestation` is send to `cozy-stack` for verification.
46
+
47
+ The resulting `attestation` is in the form of a `base64` token.
48
+
49
+ The `attestation`'s content is described in the `AppAttest`'s documentation: https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server#3576643
50
+
51
+ ## Client's configuration
52
+
53
+ In order to configure `flagship` certification on an app, the OAuth property of `cozy-client` must contain `shouldRequireFlagshipPermissions` property set to `true` and `certificationConfig` property filled with required API keys.
54
+
55
+ Example of `cozy-client` configuration:
56
+ ```js
57
+ const client = await initClient(uri, {
58
+ scope: [
59
+ 'io.cozy.apps'
60
+ ],
61
+ oauth: {
62
+ redirectURI: 'REDIRECT_URI',
63
+ softwareID: 'YOUR_APP_ID',
64
+ clientKind: 'mobile',
65
+ clientName: 'YOUR_APP_NAME',
66
+ shouldRequireFlagshipPermissions: true,
67
+ certificationConfig: {
68
+ androidSafetyNetApiKey: 'YOUR_GOOGLE_SAFETY_NET_API_KEY'
69
+ }
70
+ },
71
+ ```
72
+
73
+ ## Stacks' configuration
74
+
75
+ In order to certify an app, the `cozy-stack` needs to have the following data:
76
+ - App's package name
77
+ - The app's package name is set on the react-native project
78
+ - It should be put in `flagship.apk_package_names` in the `cozy-stack`'s configuration file
79
+ - iOS app's id
80
+ - The iOS app's id is the concatenation of the developper team's id and the app's package name as defined in the XCode project
81
+ - If team's id is `ABCDEFGHIJ` and package name is `io.cozy.some_app` then the resulting app's id should be `ABCDEFGHIJ.io.cozy.some_app`
82
+ - It should be put in `flagship.apple_app_ids` in the `cozy-stack`'s configuration file
83
+ - Android app's certificate digest
84
+ - The Android certificate digest can be found in the Google Play app's Console, in `Configuration/App integrity` page
85
+ - On local dev environment, the dev certificate location is:
86
+ - On a ReactNative project: `<project_root>/android/app/debug.keystore`
87
+ - On an Android Studio project:
88
+ - On OSX: `~/.android/debug.keystore`
89
+ - On Windows: `%UserProfile%\.android\debug.keystore`
90
+ - On a Xamarin Project:
91
+ - On OSX: `~/.local/share/Xamarin/Mono for Android/debug.keystore`
92
+ - On Windows: `%LocalAppData%\Xamarin\Mono for Android\debug.keystore`
93
+ - To extract the certificat digest from local dev certificate:
94
+ - `keytool -list -v -keystore ./android/app/debug.keystore -alias androiddebugkey -storepass android -keypass android | grep "SHA256: " | cut -d " " -f 3 | xxd -r -p | openssl base64`
95
+ - The certificate digest is the `App signing key certificate` in SHA-256 format converted from HEX to base64
96
+ - It should be put in `flagship.apk_certificate_digests` in the `cozy-stack`'s configuration file
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.certifyFlagship = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _CozyClient = _interopRequireDefault(require("../CozyClient"));
15
+
16
+ var _storeAttestation = require("./store-attestation");
17
+
18
+ /**
19
+ * Request a challenge from the Stack that can be used to request the app attestation from the app store
20
+ *
21
+ * @param {CozyClient} client - the CozyClient instance
22
+ * @returns {Promise<string>} - the Nonce string returned by the stack
23
+ */
24
+ var getStackChallenge = /*#__PURE__*/function () {
25
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client) {
26
+ var stackClient, result;
27
+ return _regenerator.default.wrap(function _callee$(_context) {
28
+ while (1) {
29
+ switch (_context.prev = _context.next) {
30
+ case 0:
31
+ _context.prev = 0;
32
+ stackClient = client.getStackClient();
33
+ _context.next = 4;
34
+ return stackClient.fetchJSON('POST', "/auth/clients/".concat(stackClient.oauthOptions.clientID, "/challenge"), null, {
35
+ headers: {
36
+ Authorization: stackClient.registrationAccessTokenToAuthHeader()
37
+ }
38
+ });
39
+
40
+ case 4:
41
+ result = _context.sent;
42
+ return _context.abrupt("return", result.nonce);
43
+
44
+ case 8:
45
+ _context.prev = 8;
46
+ _context.t0 = _context["catch"](0);
47
+ throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while requesting a challenge from CozyStack:\n' + _context.t0.message);
48
+
49
+ case 11:
50
+ case "end":
51
+ return _context.stop();
52
+ }
53
+ }
54
+ }, _callee, null, [[0, 8]]);
55
+ }));
56
+
57
+ return function getStackChallenge(_x) {
58
+ return _ref.apply(this, arguments);
59
+ };
60
+ }();
61
+ /**
62
+ * Give the app attestation to the Stack
63
+ *
64
+ * @param {AttestationResult} appAttestation - the app attestation that was returned by the app store
65
+ * @param {string} nonce - the Nonce string retrieved from the stack
66
+ * @param {CozyClient} client - the CozyClient instance
67
+ */
68
+
69
+
70
+ var giveAppAttestationToStack = /*#__PURE__*/function () {
71
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(appAttestation, nonce, client) {
72
+ var platform, attestation, keyId, stackClient;
73
+ return _regenerator.default.wrap(function _callee2$(_context2) {
74
+ while (1) {
75
+ switch (_context2.prev = _context2.next) {
76
+ case 0:
77
+ _context2.prev = 0;
78
+ platform = appAttestation.platform, attestation = appAttestation.attestation, keyId = appAttestation.keyId;
79
+ stackClient = client.getStackClient();
80
+ _context2.next = 5;
81
+ return stackClient.fetchJSON('POST', "/auth/clients/".concat(stackClient.oauthOptions.clientID, "/attestation"), {
82
+ platform: platform,
83
+ attestation: attestation,
84
+ challenge: nonce,
85
+ keyId: keyId
86
+ }, {
87
+ headers: {
88
+ Authorization: stackClient.registrationAccessTokenToAuthHeader()
89
+ }
90
+ });
91
+
92
+ case 5:
93
+ _context2.next = 10;
94
+ break;
95
+
96
+ case 7:
97
+ _context2.prev = 7;
98
+ _context2.t0 = _context2["catch"](0);
99
+ throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while giving attestation to CozyStack:\n' + _context2.t0.message);
100
+
101
+ case 10:
102
+ case "end":
103
+ return _context2.stop();
104
+ }
105
+ }
106
+ }, _callee2, null, [[0, 7]]);
107
+ }));
108
+
109
+ return function giveAppAttestationToStack(_x2, _x3, _x4) {
110
+ return _ref2.apply(this, arguments);
111
+ };
112
+ }();
113
+ /**
114
+ * Verify app's identity and integrity so the Stack can trust it
115
+ * Verification is done on Stack side by using information from the app's store (Google Play or Apple AppStore)
116
+ *
117
+ * @param {CertificationConfig} certificationConfig - the required configuration to access the stores API
118
+ * @param {CozyClient} client - the CozyClient instance
119
+ */
120
+
121
+
122
+ var certifyFlagship = /*#__PURE__*/function () {
123
+ var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(certificationConfig, client) {
124
+ var stackChallengeNonce, appAttestation;
125
+ return _regenerator.default.wrap(function _callee3$(_context3) {
126
+ while (1) {
127
+ switch (_context3.prev = _context3.next) {
128
+ case 0:
129
+ if (certificationConfig) {
130
+ _context3.next = 2;
131
+ break;
132
+ }
133
+
134
+ throw new Error('[FLAGSHIP_CERTIFICATION] Certification configuration is not set');
135
+
136
+ case 2:
137
+ _context3.next = 4;
138
+ return getStackChallenge(client);
139
+
140
+ case 4:
141
+ stackChallengeNonce = _context3.sent;
142
+ _context3.next = 7;
143
+ return (0, _storeAttestation.getAppAttestationFromStore)(stackChallengeNonce, certificationConfig);
144
+
145
+ case 7:
146
+ appAttestation = _context3.sent;
147
+ _context3.next = 10;
148
+ return giveAppAttestationToStack(appAttestation, stackChallengeNonce, client);
149
+
150
+ case 10:
151
+ case "end":
152
+ return _context3.stop();
153
+ }
154
+ }
155
+ }, _callee3);
156
+ }));
157
+
158
+ return function certifyFlagship(_x5, _x6) {
159
+ return _ref3.apply(this, arguments);
160
+ };
161
+ }();
162
+
163
+ exports.certifyFlagship = certifyFlagship;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.getAppAttestationFromStore = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _reactNativeGoogleSafetynet = _interopRequireDefault(require("react-native-google-safetynet"));
15
+
16
+ //@ts-ignore
17
+
18
+ /**
19
+ * Retrieve the app's attestation from the Google Play store
20
+ *
21
+ * @param {string} nonce - the Nonce string retrieved from the stack
22
+ * @param {CertificationConfig} certificationConfig - Configuration to access the stores certification API
23
+ * @returns {Promise<AttestationResult>} the app's attestation
24
+ */
25
+ var getAppAttestationFromStore = /*#__PURE__*/function () {
26
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(nonce, certificationConfig) {
27
+ var attestationResult;
28
+ return _regenerator.default.wrap(function _callee$(_context) {
29
+ while (1) {
30
+ switch (_context.prev = _context.next) {
31
+ case 0:
32
+ _context.prev = 0;
33
+ _context.next = 3;
34
+ return _reactNativeGoogleSafetynet.default.sendAttestationRequestJWT(nonce, certificationConfig.androidSafetyNetApiKey);
35
+
36
+ case 3:
37
+ attestationResult = _context.sent;
38
+ return _context.abrupt("return", {
39
+ platform: 'android',
40
+ attestation: attestationResult
41
+ });
42
+
43
+ case 7:
44
+ _context.prev = 7;
45
+ _context.t0 = _context["catch"](0);
46
+ throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while requesting an attestation from Google Safetynet:\n' + _context.t0.message);
47
+
48
+ case 10:
49
+ case "end":
50
+ return _context.stop();
51
+ }
52
+ }
53
+ }, _callee, null, [[0, 7]]);
54
+ }));
55
+
56
+ return function getAppAttestationFromStore(_x, _x2) {
57
+ return _ref.apply(this, arguments);
58
+ };
59
+ }();
60
+
61
+ exports.getAppAttestationFromStore = getAppAttestationFromStore;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.getAppAttestationFromStore = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _reactNativeIos11Devicecheck = _interopRequireDefault(require("react-native-ios11-devicecheck"));
15
+
16
+ //@ts-ignore
17
+
18
+ /**
19
+ * Retrieve the app's attestation from the Apple AppStore
20
+ *
21
+ * @param {string} nonce - the Nonce string retrieved from the stack
22
+ * @param {CertificationConfig} certificationConfig - Configuration to access the stores certification API
23
+ * @returns {Promise<AttestationResult>} the app's attestation
24
+ */
25
+ var getAppAttestationFromStore = /*#__PURE__*/function () {
26
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(nonce, certificationConfig) {
27
+ var keyId, attestKey;
28
+ return _regenerator.default.wrap(function _callee$(_context) {
29
+ while (1) {
30
+ switch (_context.prev = _context.next) {
31
+ case 0:
32
+ _context.prev = 0;
33
+ _context.next = 3;
34
+ return _reactNativeIos11Devicecheck.default.generateKey();
35
+
36
+ case 3:
37
+ keyId = _context.sent;
38
+ _context.next = 6;
39
+ return _reactNativeIos11Devicecheck.default.attestKey(keyId, nonce);
40
+
41
+ case 6:
42
+ attestKey = _context.sent;
43
+ return _context.abrupt("return", {
44
+ platform: 'ios',
45
+ attestation: attestKey,
46
+ keyId: keyId
47
+ });
48
+
49
+ case 10:
50
+ _context.prev = 10;
51
+ _context.t0 = _context["catch"](0);
52
+ throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while requesting an attestation from Apple DeviceCheck:\n' + _context.t0.message);
53
+
54
+ case 13:
55
+ case "end":
56
+ return _context.stop();
57
+ }
58
+ }
59
+ }, _callee, null, [[0, 10]]);
60
+ }));
61
+
62
+ return function getAppAttestationFromStore(_x, _x2) {
63
+ return _ref.apply(this, arguments);
64
+ };
65
+ }();
66
+
67
+ exports.getAppAttestationFromStore = getAppAttestationFromStore;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.getAppAttestationFromStore = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ /**
15
+ * Retrieve the app's attestation from the app's store
16
+ * /!\ This is a mock implementation that should never be called
17
+ *
18
+ * @param {string} nonce - the Nonce string retrieved from the stack
19
+ * @param {CertificationConfig} certificationConfig - Configuration to access the stores certification API
20
+ * @returns {Promise<AttestationResult>} the app's attestation
21
+ */
22
+ var validateAppMock = /*#__PURE__*/function () {
23
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(nonce, certificationConfig) {
24
+ return _regenerator.default.wrap(function _callee$(_context) {
25
+ while (1) {
26
+ switch (_context.prev = _context.next) {
27
+ case 0:
28
+ throw new Error("validateApp can only be called from a React Native container");
29
+
30
+ case 1:
31
+ case "end":
32
+ return _context.stop();
33
+ }
34
+ }
35
+ }, _callee);
36
+ }));
37
+
38
+ return function validateAppMock(_x, _x2) {
39
+ return _ref.apply(this, arguments);
40
+ };
41
+ }();
42
+
43
+ var getAppAttestationFromStore = validateAppMock;
44
+ exports.getAppAttestationFromStore = getAppAttestationFromStore;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A JSON Web Signature
3
+ * @typedef {string} jws
4
+ */
5
+
6
+ /**
7
+ * A JSON Web Signature
8
+ * @typedef {string} base64string
9
+ */
10
+
11
+ /**
12
+ * An app attestation from the app store
13
+ * @typedef {object} AttestationResult
14
+ * @property {string} platform
15
+ * @property {jws|base64string} attestation
16
+ * @property {string} [keyId]
17
+ */
18
+
19
+ /**
20
+ * Configuration to access the stores certification API
21
+ * @typedef {object} CertificationConfig
22
+ * @property {string} androidSafetyNetApiKey
23
+ */
24
+ "use strict";
@@ -103,8 +103,12 @@ var useQuery = function useQuery(queryDefinition, options) {
103
103
  as: as
104
104
  });
105
105
  }, [as, client]);
106
+ var fetch = (0, _react.useCallback)(function () {
107
+ return client.query(definition, options);
108
+ }, [client, definition, options]);
106
109
  return _objectSpread(_objectSpread({}, queryState), {}, {
107
- fetchMore: fetchMore
110
+ fetchMore: fetchMore,
111
+ fetch: fetch
108
112
  });
109
113
  };
110
114
 
@@ -52,6 +52,46 @@ describe('use query', () => {
52
52
  })
53
53
  })
54
54
 
55
+ it('should share the same API than ObservableQuery', () => {
56
+ const {
57
+ hookResult: {
58
+ result: { current }
59
+ }
60
+ } = setupQuery({
61
+ queryOptions: {
62
+ as: 'simpsons'
63
+ },
64
+ queryDefinition: () => Q('io.cozy.simpsons')
65
+ })
66
+ expect(current).toMatchObject({
67
+ data: [
68
+ expect.objectContaining({ name: 'Homer' }),
69
+ expect.objectContaining({ name: 'Marge' })
70
+ ],
71
+ definition: expect.objectContaining({
72
+ doctype: 'io.cozy.simpsons'
73
+ })
74
+ })
75
+
76
+ expect(Object.keys(current)).toEqual(
77
+ expect.objectContaining([
78
+ 'id',
79
+ 'definition',
80
+ 'fetchStatus',
81
+ 'lastFetch',
82
+ 'lastUpdate',
83
+ 'lastErrorUpdate',
84
+ 'lastError',
85
+ 'hasMore',
86
+ 'count',
87
+ 'data',
88
+ 'bookmark',
89
+ 'options',
90
+ 'fetchMore',
91
+ 'fetch'
92
+ ])
93
+ )
94
+ })
55
95
  it('should query through the client in context', () => {
56
96
  const { client } = setupQuery({
57
97
  queryOptions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "27.7.0",
3
+ "version": "27.8.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -66,8 +66,10 @@
66
66
  "cozy-ui": ">34.0.0",
67
67
  "react": "^16.7.0",
68
68
  "react-native": "^0.65.0",
69
- "react-native-inappbrowser-reborn": "^3.5.1"
69
+ "react-native-google-safetynet": "0.0.4",
70
+ "react-native-inappbrowser-reborn": "^3.5.1",
71
+ "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
70
72
  },
71
73
  "sideEffects": false,
72
- "gitHead": "b301a3deef6018f8aa91ffcbcf1ad660b68711cd"
74
+ "gitHead": "503b86d3aef51c8f9b684dbcffcdad49bd41ca5e"
73
75
  }
@@ -0,0 +1,2 @@
1
+ export function certifyFlagship(certificationConfig: CertificationConfig, client: CozyClient): Promise<void>;
2
+ import CozyClient from "../CozyClient";
@@ -0,0 +1 @@
1
+ export function getAppAttestationFromStore(nonce: string, certificationConfig: CertificationConfig): Promise<AttestationResult>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Retrieve the app's attestation from the app's store
3
+ * /!\ This is a mock implementation that should never be called
4
+ *
5
+ * @param {string} nonce - the Nonce string retrieved from the stack
6
+ * @param {CertificationConfig} certificationConfig - Configuration to access the stores certification API
7
+ * @returns {Promise<AttestationResult>} the app's attestation
8
+ */
9
+ export function getAppAttestationFromStore(nonce: string, certificationConfig: CertificationConfig): Promise<AttestationResult>;
@@ -0,0 +1 @@
1
+ export function getAppAttestationFromStore(nonce: string, certificationConfig: CertificationConfig): Promise<AttestationResult>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A JSON Web Signature
3
+ */
4
+ type jws = string;
5
+ /**
6
+ * A JSON Web Signature
7
+ */
8
+ type base64string = string;
9
+ /**
10
+ * An app attestation from the app store
11
+ */
12
+ type AttestationResult = {
13
+ platform: string;
14
+ attestation: jws | base64string;
15
+ keyId?: string;
16
+ };
17
+ /**
18
+ * Configuration to access the stores certification API
19
+ */
20
+ type CertificationConfig = {
21
+ androidSafetyNetApiKey: string;
22
+ };
@@ -52,7 +52,7 @@ export type Document = {
52
52
  _type: string;
53
53
  type: string;
54
54
  };
55
- export type PermissionVerb = "DELETE" | "PUT" | "POST" | "GET" | "ALL" | "PATCH";
55
+ export type PermissionVerb = "POST" | "DELETE" | "PUT" | "GET" | "ALL" | "PATCH";
56
56
  export type PermissionItem = {
57
57
  /**
58
58
  * - ALL, GET, PUT, PATCH, DELETE, POST…