cozy-client 45.6.0 → 45.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/dist/CozyClient.js +1 -1
- package/dist/flagship-certification/README.md +11 -16
- package/dist/flagship-certification/flagship-certification.js +4 -3
- package/dist/flagship-certification/flagship-certification.spec.js +4 -4
- package/dist/flagship-certification/store-attestation.android.js +8 -7
- package/dist/flagship-certification/typedefs.js +2 -1
- package/dist/models/account.js +4 -0
- package/dist/models/account.spec.js +27 -0
- package/package.json +3 -3
- package/types/flagship-certification/typedefs.d.ts +2 -1
package/dist/CozyClient.js
CHANGED
|
@@ -2703,7 +2703,7 @@ var CozyClient = /*#__PURE__*/function () {
|
|
|
2703
2703
|
CozyClient.hooks = CozyClient.hooks || {};
|
|
2704
2704
|
CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
|
|
2705
2705
|
|
|
2706
|
-
CozyClient.version = "45.
|
|
2706
|
+
CozyClient.version = "45.8.0";
|
|
2707
2707
|
|
|
2708
2708
|
_microee.default.mixin(CozyClient);
|
|
2709
2709
|
|
|
@@ -11,32 +11,27 @@ This verification is done by querying an app certificate from the app store (App
|
|
|
11
11
|
- `attestation`: result from the `store certification`
|
|
12
12
|
- `challenge`: unique token given to the app by `cozy-stack` that may be encrypted in the `attestation` as a proof of authenticity
|
|
13
13
|
- `nonce`: data type used to store the `challenge` token
|
|
14
|
-
- `
|
|
14
|
+
- `Play Integrity API`: Google's implementation of the `store certification`
|
|
15
15
|
- `AppAttest`: Apple's implementation of the `store certification`
|
|
16
16
|
|
|
17
17
|
## Android certification
|
|
18
18
|
|
|
19
|
-
Android certification is based on [
|
|
19
|
+
Android certification is based on [Play Integrity API](https://developer.android.com/google/play/integrity/overview).
|
|
20
20
|
|
|
21
|
-
This process requires to query a `challenge` from `cozy-stack` and to use it to init the `store certification` process through `
|
|
21
|
+
This process requires to query a `challenge` from `cozy-stack` and to use it to init the `store certification` process through `Play Integrity API`. Then the received `attestation` is send to `cozy-stack` for verification.
|
|
22
22
|
|
|
23
23
|
The resulting `attestation` is in the form of a `JSON Web Signature` that embbed the following `JSON`:
|
|
24
|
-
```
|
|
24
|
+
```js
|
|
25
25
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"basicIntegrity": true,
|
|
32
|
-
"ctsProfileMatch": true,
|
|
33
|
-
"evaluationType": "BASIC",
|
|
34
|
-
"nonce": "R2Rra24fVm5xa2Mg",
|
|
35
|
-
"timestampMs": 9860437986543
|
|
26
|
+
requestDetails: { ... }
|
|
27
|
+
appIntegrity: { ... }
|
|
28
|
+
deviceIntegrity: { ... }
|
|
29
|
+
accountDetails: { ... }
|
|
30
|
+
environmentDetails: { ... }
|
|
36
31
|
}
|
|
37
32
|
```
|
|
38
33
|
|
|
39
|
-
The `attestation`'s content is described in the
|
|
34
|
+
The `attestation`'s content is described in the Play Integrity API's documentation: https://developer.android.com/google/play/integrity/verdicts#returned-verdict-format
|
|
40
35
|
|
|
41
36
|
## iOS certification
|
|
42
37
|
|
|
@@ -65,7 +60,7 @@ const client = await initClient(uri, {
|
|
|
65
60
|
clientName: 'YOUR_APP_NAME',
|
|
66
61
|
shouldRequireFlagshipPermissions: true,
|
|
67
62
|
certificationConfig: {
|
|
68
|
-
|
|
63
|
+
cloudProjectNumber: 'YOUR_CLOUD_PROJECT_NUMBER'
|
|
69
64
|
}
|
|
70
65
|
},
|
|
71
66
|
```
|
|
@@ -69,20 +69,21 @@ var getStackChallenge = /*#__PURE__*/function () {
|
|
|
69
69
|
|
|
70
70
|
var giveAppAttestationToStack = /*#__PURE__*/function () {
|
|
71
71
|
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(appAttestation, nonce, client) {
|
|
72
|
-
var platform, attestation, keyId, stackClient;
|
|
72
|
+
var platform, attestation, keyId, issuer, stackClient;
|
|
73
73
|
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
74
74
|
while (1) {
|
|
75
75
|
switch (_context2.prev = _context2.next) {
|
|
76
76
|
case 0:
|
|
77
77
|
_context2.prev = 0;
|
|
78
|
-
platform = appAttestation.platform, attestation = appAttestation.attestation, keyId = appAttestation.keyId;
|
|
78
|
+
platform = appAttestation.platform, attestation = appAttestation.attestation, keyId = appAttestation.keyId, issuer = appAttestation.issuer;
|
|
79
79
|
stackClient = client.getStackClient();
|
|
80
80
|
_context2.next = 5;
|
|
81
81
|
return stackClient.fetchJSON('POST', "/auth/clients/".concat(stackClient.oauthOptions.clientID, "/attestation"), {
|
|
82
82
|
platform: platform,
|
|
83
83
|
attestation: attestation,
|
|
84
84
|
challenge: nonce,
|
|
85
|
-
keyId: keyId
|
|
85
|
+
keyId: keyId,
|
|
86
|
+
issuer: issuer
|
|
86
87
|
}, {
|
|
87
88
|
headers: {
|
|
88
89
|
Authorization: stackClient.registrationAccessTokenToAuthHeader()
|
|
@@ -44,7 +44,7 @@ const mockCorrectStoreApiRequest = () => {
|
|
|
44
44
|
|
|
45
45
|
const mockCorrectCertificationConfig = () => {
|
|
46
46
|
return {
|
|
47
|
-
|
|
47
|
+
cloudProjectNumber: 'SOME_CLOUD_PROJECT_NUMBER'
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -90,7 +90,7 @@ describe('certifyFlagship', () => {
|
|
|
90
90
|
)
|
|
91
91
|
|
|
92
92
|
expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
|
|
93
|
-
|
|
93
|
+
cloudProjectNumber: 'SOME_CLOUD_PROJECT_NUMBER'
|
|
94
94
|
})
|
|
95
95
|
|
|
96
96
|
expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
|
|
@@ -171,7 +171,7 @@ describe('certifyFlagship', () => {
|
|
|
171
171
|
)
|
|
172
172
|
|
|
173
173
|
expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
|
|
174
|
-
|
|
174
|
+
cloudProjectNumber: 'SOME_CLOUD_PROJECT_NUMBER'
|
|
175
175
|
})
|
|
176
176
|
|
|
177
177
|
expect(console.warn).toHaveBeenCalledWith(
|
|
@@ -201,7 +201,7 @@ describe('certifyFlagship', () => {
|
|
|
201
201
|
)
|
|
202
202
|
|
|
203
203
|
expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
|
|
204
|
-
|
|
204
|
+
cloudProjectNumber: 'SOME_CLOUD_PROJECT_NUMBER'
|
|
205
205
|
})
|
|
206
206
|
|
|
207
207
|
expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
|
|
@@ -11,9 +11,9 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
|
|
|
11
11
|
|
|
12
12
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _reactNativeGooglePlayIntegrity = _interopRequireDefault(require("react-native-google-play-integrity"));
|
|
15
15
|
|
|
16
|
-
//@ts-ignore next-line
|
|
16
|
+
//@ts-ignore next-line - this is a react-native module that is not installed in the monorepo, only in the consumer app
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Retrieve the app's attestation from the Google Play store
|
|
@@ -24,26 +24,27 @@ var _reactNativeGoogleSafetynet = _interopRequireDefault(require("react-native-g
|
|
|
24
24
|
*/
|
|
25
25
|
var getAppAttestationFromStore = /*#__PURE__*/function () {
|
|
26
26
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(nonce, certificationConfig) {
|
|
27
|
-
var
|
|
27
|
+
var integrityToken;
|
|
28
28
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
29
29
|
while (1) {
|
|
30
30
|
switch (_context.prev = _context.next) {
|
|
31
31
|
case 0:
|
|
32
32
|
_context.prev = 0;
|
|
33
33
|
_context.next = 3;
|
|
34
|
-
return
|
|
34
|
+
return _reactNativeGooglePlayIntegrity.default.requestIntegrityToken(nonce, certificationConfig.cloudProjectNumber);
|
|
35
35
|
|
|
36
36
|
case 3:
|
|
37
|
-
|
|
37
|
+
integrityToken = _context.sent;
|
|
38
38
|
return _context.abrupt("return", {
|
|
39
39
|
platform: 'android',
|
|
40
|
-
attestation:
|
|
40
|
+
attestation: integrityToken,
|
|
41
|
+
issuer: 'playintegrity'
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
case 7:
|
|
44
45
|
_context.prev = 7;
|
|
45
46
|
_context.t0 = _context["catch"](0);
|
|
46
|
-
throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while requesting an attestation from Google
|
|
47
|
+
throw new Error('[FLAGSHIP_CERTIFICATION] Something went wrong while requesting an attestation from Google Play Integrity API:\n' + _context.t0.message);
|
|
47
48
|
|
|
48
49
|
case 10:
|
|
49
50
|
case "end":
|
|
@@ -18,11 +18,12 @@ exports.default = void 0;
|
|
|
18
18
|
* @property {string} platform
|
|
19
19
|
* @property {jws|base64string} attestation
|
|
20
20
|
* @property {string} [keyId]
|
|
21
|
+
* @property {string} [issuer]
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* @typedef {object} CertificationConfig - Configuration to access the stores certification API
|
|
25
|
-
* @property {string}
|
|
26
|
+
* @property {string} cloudProjectNumber
|
|
26
27
|
*/
|
|
27
28
|
var _default = {};
|
|
28
29
|
exports.default = _default;
|
package/dist/models/account.js
CHANGED
|
@@ -121,6 +121,10 @@ var setContractSyncStatusInAccount = function setContractSyncStatusInAccount(acc
|
|
|
121
121
|
exports.setContractSyncStatusInAccount = setContractSyncStatusInAccount;
|
|
122
122
|
|
|
123
123
|
var getAccountLogin = function getAccountLogin(account) {
|
|
124
|
+
if (account.identifier) {
|
|
125
|
+
return account.auth[account.identifier];
|
|
126
|
+
}
|
|
127
|
+
|
|
124
128
|
if (account && account.auth) {
|
|
125
129
|
var _iterator = _createForOfIteratorHelper(_manifest.legacyLoginFields),
|
|
126
130
|
_step;
|
|
@@ -209,6 +209,33 @@ describe('account model', () => {
|
|
|
209
209
|
})
|
|
210
210
|
})
|
|
211
211
|
|
|
212
|
+
describe('getAccountLogin', () => {
|
|
213
|
+
it('should return account.identifier field if defined', () => {
|
|
214
|
+
expect(
|
|
215
|
+
getAccountLogin({
|
|
216
|
+
_id: 'accountid',
|
|
217
|
+
auth: {
|
|
218
|
+
login: 'loginfield',
|
|
219
|
+
other: 'identifierfield'
|
|
220
|
+
},
|
|
221
|
+
identifier: 'other'
|
|
222
|
+
})
|
|
223
|
+
).toStrictEqual('identifierfield')
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('should return login field if no identifier is defined', () => {
|
|
227
|
+
expect(
|
|
228
|
+
getAccountLogin({
|
|
229
|
+
_id: 'accountid',
|
|
230
|
+
auth: {
|
|
231
|
+
login: 'loginfield',
|
|
232
|
+
other: 'identifierfield'
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
).toStrictEqual('loginfield')
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
212
239
|
describe('getAccountName', () => {
|
|
213
240
|
it('should return auth.accountName in priority', () => {
|
|
214
241
|
expect(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-client",
|
|
3
|
-
"version": "45.
|
|
3
|
+
"version": "45.8.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.node.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"cozy-ui": ">=93.1.1",
|
|
71
71
|
"react": "^16.7.0",
|
|
72
72
|
"react-native": "^0.65.0",
|
|
73
|
-
"react-native-google-
|
|
73
|
+
"react-native-google-play-integrity": "github:cozy/react-native-google-play-integrity#1.0.1",
|
|
74
74
|
"react-native-inappbrowser-reborn": "^3.5.1",
|
|
75
75
|
"react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
|
|
76
76
|
},
|
|
77
77
|
"sideEffects": false,
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "c88d681a2f65911a240a4b0bae19e88c8fd5a86e"
|
|
79
79
|
}
|
|
@@ -15,10 +15,11 @@ export type AttestationResult = {
|
|
|
15
15
|
platform: string;
|
|
16
16
|
attestation: jws | base64string;
|
|
17
17
|
keyId?: string;
|
|
18
|
+
issuer?: string;
|
|
18
19
|
};
|
|
19
20
|
/**
|
|
20
21
|
* - Configuration to access the stores certification API
|
|
21
22
|
*/
|
|
22
23
|
export type CertificationConfig = {
|
|
23
|
-
|
|
24
|
+
cloudProjectNumber: string;
|
|
24
25
|
};
|