cordova-digital-onboarding 1.0.1 → 1.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.
- package/docs/Changelog.md +25 -0
- package/docs/Device-Activation.md +39 -35
- package/docs/Process-Configuration.md +24 -15
- package/docs/Verifying-User.md +218 -60
- package/docs/_Sidebar.md +1 -1
- package/docs/images/activation-mockup.png +0 -0
- package/docs/images/intro.jpg +0 -0
- package/docs/images/verification-mockup.png +0 -0
- package/lib/index.d.ts +346 -289
- package/lib/index.js +1730 -1470
- package/package.json +1 -1
- package/plugin.xml +2 -1
package/docs/Verifying-User.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
# Verifying
|
|
1
|
+
# Verifying the User
|
|
2
2
|
|
|
3
|
-
If your
|
|
3
|
+
If your `PowerAuth` instance was activated via the `WDOActivationService`, it will be in the state that needs additional verification. Without such verification, it won't be able to properly sign requests.
|
|
4
4
|
|
|
5
|
-
Additional verification
|
|
5
|
+
Additional verification requires the user to scan their face and provide documents such as an ID card or passport.
|
|
6
6
|
|
|
7
7
|
## When is the verification needed?
|
|
8
8
|
|
|
9
9
|
Verification is needed if the `activationFlags` in the `PowerAuthActivationStatus` contains `VERIFICATION_PENDING` or `VERIFICATION_IN_PROGRESS` value.
|
|
10
10
|
|
|
11
|
+
<!-- begin box info -->
|
|
11
12
|
To simplify this check, you can use the `WDOVerificationService.isVerificationRequired` method that returns a boolean indicating whether verification is required.
|
|
13
|
+
<!-- end -->
|
|
12
14
|
|
|
13
15
|
Example:
|
|
14
16
|
|
|
@@ -42,73 +44,71 @@ The final flow (which screens come after another) is controlled by the backend.
|
|
|
42
44
|
- The screen that should be displayed is driven by the state on the server "session".
|
|
43
45
|
- At the beginning of the verification process, you will call the status which will tell you what to display to the user and which function to call next.
|
|
44
46
|
- Each API call returns a result and a next screen to display.
|
|
45
|
-
- This repeats until the process is finished or an "
|
|
47
|
+
- This repeats until the process is finished or an "end state" is presented which terminates the process.
|
|
46
48
|
|
|
47
49
|
## Possible state values
|
|
48
50
|
|
|
49
|
-
State is defined by the `WDOVerificationStateType`
|
|
51
|
+
State is defined by the `WDOVerificationStateType` with the following possibilities:
|
|
50
52
|
|
|
51
53
|
```typescript
|
|
52
54
|
/** Types of the verification state */
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Show the verification introduction screen where the user can start the activation.
|
|
56
|
-
*
|
|
57
|
-
* The next step should be calling the `
|
|
55
|
+
enum WDOVerificationStateType {
|
|
56
|
+
/**
|
|
57
|
+
* Show the verification introduction screen where the user can start the activation.
|
|
58
|
+
*
|
|
59
|
+
* The next step should be calling the `start()`.
|
|
58
60
|
*/
|
|
59
61
|
intro = "intro",
|
|
60
|
-
/**
|
|
61
|
-
* Show approve/cancel user consent.
|
|
62
|
-
*
|
|
63
|
-
* The content of the text depends on the server configuration and might be plain text or HTML.
|
|
64
|
-
*
|
|
65
|
-
* The next step should be calling the `consentApprove`.
|
|
66
|
-
*/
|
|
67
|
-
consent = "consent",
|
|
68
62
|
/**
|
|
69
63
|
* Show document selection to the user. Which documents are available and how many
|
|
70
64
|
* can the user select is up to your backend configuration.
|
|
71
|
-
*
|
|
65
|
+
*
|
|
72
66
|
* The next step should be calling the `documentsSetSelectedTypes`.
|
|
73
67
|
*/
|
|
74
68
|
documentsToScanSelect = "documentsToScanSelect",
|
|
75
69
|
/**
|
|
76
70
|
* User should scan documents - display UI for the user to scan all necessary documents.
|
|
77
|
-
*
|
|
71
|
+
*
|
|
78
72
|
* The next step should be calling the `documentsSubmit`.
|
|
79
73
|
*/
|
|
80
74
|
scanDocument = "scanDocument",
|
|
81
|
-
/**
|
|
75
|
+
/**
|
|
82
76
|
* The system is processing data - show loading with text hint from provided `WDOStatusCheckReason`.
|
|
83
|
-
*
|
|
77
|
+
*
|
|
84
78
|
* The next step should be calling the `status`.
|
|
85
79
|
*/
|
|
86
80
|
processing = "processing",
|
|
87
|
-
/**
|
|
81
|
+
/**
|
|
88
82
|
* The user should be presented with a presence check.
|
|
89
83
|
* Presence check is handled by third-party SDK based on the project setup.
|
|
90
|
-
*
|
|
84
|
+
*
|
|
91
85
|
* The next step should be calling the `presenceCheckInit` to start the check and `presenceCheckSubmit` to
|
|
92
86
|
* mark it finished. Note that these methods won't change the status and it's up to the app to handle the process of the presence check.
|
|
93
87
|
*/
|
|
94
88
|
presenceCheck = "presenceCheck",
|
|
95
89
|
/**
|
|
96
90
|
* Show enter OTP screen with resend button.
|
|
97
|
-
*
|
|
91
|
+
*
|
|
98
92
|
* The next step should be calling the `verifyOTP` with user-entered OTP.
|
|
99
93
|
* The OTP is usually SMS or email.
|
|
100
94
|
*/
|
|
101
95
|
otp = "otp",
|
|
96
|
+
/**
|
|
97
|
+
* Show "finish activation" with PIN prompt screen.
|
|
98
|
+
*
|
|
99
|
+
* The next step should be calling the `finishActivation` with user entered PIN.
|
|
100
|
+
*/
|
|
101
|
+
finishActivation = "finishActivation",
|
|
102
102
|
/**
|
|
103
103
|
* Verification failed and can be restarted
|
|
104
|
-
*
|
|
104
|
+
*
|
|
105
105
|
* The next step should be calling the `restartVerification` or `cancelWholeProcess` based on
|
|
106
106
|
* the user's decision if he wants to try it again or cancel the process.
|
|
107
107
|
*/
|
|
108
108
|
failed = "failed",
|
|
109
109
|
/**
|
|
110
110
|
* Verification is canceled and the user needs to start again with a new PowerAuth activation.
|
|
111
|
-
*
|
|
111
|
+
*
|
|
112
112
|
* The next step should be calling the `PowerAuth.removeActivationLocal()` and starting activation from scratch.
|
|
113
113
|
*/
|
|
114
114
|
endState = "endState",
|
|
@@ -119,6 +119,127 @@ declare enum WDOVerificationStateType {
|
|
|
119
119
|
}
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
Based on the type value, additional data is provided in the union type `WDOVerificationState`.
|
|
123
|
+
|
|
124
|
+
Available data per state type:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
export type WDOVerificationState =
|
|
128
|
+
| WDOIntroState
|
|
129
|
+
| WDODocumentsToScanSelectState
|
|
130
|
+
| WDOScanDocumentState
|
|
131
|
+
| WDOProcessingState
|
|
132
|
+
| WDOPresenceCheckState
|
|
133
|
+
| WDOOtpState
|
|
134
|
+
| WDOFinishActivation
|
|
135
|
+
| WDOFailedState
|
|
136
|
+
| WDOEndStateState
|
|
137
|
+
| WDOSuccessState
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Show the verification introduction screen where the user can start the activation.
|
|
141
|
+
*
|
|
142
|
+
* The next step should be calling the `start`.
|
|
143
|
+
*/
|
|
144
|
+
export interface WDOIntroState {
|
|
145
|
+
type: WDOVerificationStateType.intro,
|
|
146
|
+
/** Indicates whether the user consent is required to proceed */
|
|
147
|
+
consentRequired: boolean
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Show document selection to the user. Which documents are available and how many
|
|
152
|
+
* can the user select is up to your backend configuration.
|
|
153
|
+
*
|
|
154
|
+
* The next step should be calling the `documentsSetSelectedTypes`.
|
|
155
|
+
*/
|
|
156
|
+
export interface WDODocumentsToScanSelectState {
|
|
157
|
+
type: WDOVerificationStateType.documentsToScanSelect
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* User should scan documents - display UI for the user to scan all necessary documents.
|
|
162
|
+
*
|
|
163
|
+
* The next step should be calling the `documentsSubmit`.
|
|
164
|
+
*/
|
|
165
|
+
export interface WDOScanDocumentState {
|
|
166
|
+
type: WDOVerificationStateType.scanDocument
|
|
167
|
+
/** Scanning process that helps with the document scanning */
|
|
168
|
+
process: WDOVerificationScanProcess
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The system is processing data - show loading with text hint from provided `WDOStatusCheckReason`.
|
|
173
|
+
*
|
|
174
|
+
* The next step should be calling the `status`.
|
|
175
|
+
*/
|
|
176
|
+
export interface WDOProcessingState {
|
|
177
|
+
type: WDOVerificationStateType.processing
|
|
178
|
+
/** Reason for the current processing state */
|
|
179
|
+
item: WDOStatusCheckReason
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The user should be presented with a presence check.
|
|
184
|
+
* Presence check is handled by third-party SDK based on the project setup.
|
|
185
|
+
*
|
|
186
|
+
* The next step should be calling the `presenceCheckInit` to start the check and `presenceCheckSubmit` to
|
|
187
|
+
* mark it finished. Note that these methods won't change the status and it's up to the app to handle the process of the presence check.
|
|
188
|
+
*/
|
|
189
|
+
export interface WDOPresenceCheckState {
|
|
190
|
+
type: WDOVerificationStateType.presenceCheck
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Show enter OTP screen with resend button.
|
|
195
|
+
*
|
|
196
|
+
* The next step should be calling the `verifyOTP` with user-entered OTP.
|
|
197
|
+
* The OTP is usually SMS or email.
|
|
198
|
+
*/
|
|
199
|
+
export interface WDOOtpState {
|
|
200
|
+
type: WDOVerificationStateType.otp
|
|
201
|
+
/** Number of remaining attempts to enter the correct OTP */
|
|
202
|
+
remainingAttempts?: number
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Show "finish activation" with PIN prompt screen.
|
|
207
|
+
*
|
|
208
|
+
* The next step should be calling the `finishActivation` with user entered PIN.
|
|
209
|
+
*/
|
|
210
|
+
export interface WDOFinishActivation {
|
|
211
|
+
type: WDOVerificationStateType.finishActivation
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Verification failed and can be restarted
|
|
216
|
+
*
|
|
217
|
+
* The next step should be calling the `restartVerification` or `cancelWholeProcess` based on
|
|
218
|
+
* the user's decision if he wants to try it again or cancel the process.
|
|
219
|
+
*/
|
|
220
|
+
export interface WDOFailedState {
|
|
221
|
+
type: WDOVerificationStateType.failed
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Verification is canceled and the user needs to start again with a new PowerAuth activation.
|
|
226
|
+
*
|
|
227
|
+
* The next step should be calling the `PowerAuth.removeActivationLocal()` and starting activation from scratch.
|
|
228
|
+
*/
|
|
229
|
+
export interface WDOEndStateState {
|
|
230
|
+
type: WDOVerificationStateType.endState
|
|
231
|
+
/** Reason for the end state */
|
|
232
|
+
reason: WDOEndStateReason
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Verification was successfully ended. Continue into your app flow.
|
|
237
|
+
*/
|
|
238
|
+
export interface WDOSuccessState {
|
|
239
|
+
type: WDOVerificationStateType.success
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
122
243
|
## Creating an instance
|
|
123
244
|
|
|
124
245
|
To create an instance you will need a `PowerAuth` instance that is __already activated__.
|
|
@@ -130,11 +251,14 @@ To create an instance you will need a `PowerAuth` instance that is __already act
|
|
|
130
251
|
|
|
131
252
|
Example:
|
|
132
253
|
```typescript
|
|
254
|
+
// create and configure PowerAuth instance
|
|
133
255
|
const powerAuth = new PowerAuth("my-pa-instance")
|
|
134
|
-
powerAuth.configure({
|
|
256
|
+
await powerAuth.configure({
|
|
135
257
|
configuration: "ARCB+...jg==", // base64 PowerAuth configuration
|
|
136
258
|
baseEndpointUrl: "https://my-server-deployment.com/enrollment-server/"
|
|
137
259
|
})
|
|
260
|
+
|
|
261
|
+
// create activation service
|
|
138
262
|
const verification = new WDOVerificationService(
|
|
139
263
|
powerAuth,
|
|
140
264
|
"https://my-server-deployment.com/enrollment-server-onboarding/"
|
|
@@ -143,11 +267,11 @@ const verification = new WDOVerificationService(
|
|
|
143
267
|
|
|
144
268
|
## Getting the verification status
|
|
145
269
|
|
|
146
|
-
When
|
|
270
|
+
When starting the verification flow for the first time (such as after a fresh app launch), you should retrieve the current verification state.
|
|
147
271
|
|
|
148
|
-
|
|
272
|
+
You should also retrieve the state after any operation fails, or whenever you are unsure about the next step in the verification process.
|
|
149
273
|
|
|
150
|
-
Most verification functions return the result and
|
|
274
|
+
Most verification functions return both the result and the updated state, making it easier to determine what action to take next.
|
|
151
275
|
|
|
152
276
|
Getting the state directly:
|
|
153
277
|
|
|
@@ -156,6 +280,12 @@ const verification: WDOVerificationService // configured instance
|
|
|
156
280
|
try {
|
|
157
281
|
const vfStatus = await verification.status()
|
|
158
282
|
// handle `WDOVerificationState` state and navigate to the expected screen
|
|
283
|
+
if (vfStatus.type === WDOVerificationStateType.intro) {
|
|
284
|
+
// display intro screen
|
|
285
|
+
} else if (vfStatus.type === WDOVerificationStateType.documentsToScanSelect) {
|
|
286
|
+
// display document selector
|
|
287
|
+
}
|
|
288
|
+
// ... other states
|
|
159
289
|
} catch (error) {
|
|
160
290
|
if (error.verificationState) {
|
|
161
291
|
// show expected screen based on the state
|
|
@@ -165,37 +295,26 @@ try {
|
|
|
165
295
|
}
|
|
166
296
|
```
|
|
167
297
|
|
|
168
|
-
##
|
|
298
|
+
## Starting the identity verification
|
|
169
299
|
|
|
170
|
-
When the state is `intro`, the first step in the flow is to
|
|
300
|
+
When the state is `intro`, the first step in the flow is to start the verification process by calling `start` function.
|
|
171
301
|
|
|
172
302
|
```typescript
|
|
303
|
+
const state: WDOVerificationState // we're assuming that the state is `intro` here (WDOIntroState)
|
|
173
304
|
const verification: WDOVerificationService // configured instance
|
|
174
305
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
// show
|
|
306
|
+
let consentResult: WDOConsentResponse
|
|
307
|
+
const introState = state as WDOIntroState
|
|
308
|
+
if (introState.consentRequired) {
|
|
309
|
+
const consentTextResponse = await verification.consentGet()
|
|
310
|
+
// show consent screen to the user and get his approval
|
|
311
|
+
// assuming user approved the consent here
|
|
312
|
+
consentResult = WDOConsentResponse.approved
|
|
180
313
|
} else {
|
|
181
|
-
|
|
314
|
+
consentResult = WDOConsentResponse.notRequired
|
|
182
315
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
## Approving the user consent
|
|
187
|
-
|
|
188
|
-
When the state is `consent`, you should display the consent text to the user to approve or reject.
|
|
189
|
-
|
|
190
|
-
If the user __rejects the consent__, just return him to the intro screen, there's no API call for reject.
|
|
191
|
-
|
|
192
|
-
If the user chooses to accept the consent, call `consentApprove` function. If successful, `documentsToScanSelect` state will be returned.
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
const verification: WDOVerificationService // configured instance
|
|
196
|
-
try {
|
|
197
|
-
const approvalResult = await verification.consentApprove()
|
|
198
|
-
// state will be in the `documentsToScanSelect` case here - display the document selector
|
|
316
|
+
const startResult = await verification.start(consentResult)
|
|
317
|
+
// process the returned state, should be `documentsToScanSelect` now
|
|
199
318
|
} catch (error) {
|
|
200
319
|
if (error.verificationState) {
|
|
201
320
|
// show expected screen based on the state
|
|
@@ -207,13 +326,14 @@ try {
|
|
|
207
326
|
|
|
208
327
|
## Set document types to scan
|
|
209
328
|
|
|
210
|
-
After the user approves the consent, present a document selector for documents which will be scanned. The number and types of documents (or other rules like 1 type required) are completely dependent on your backend system integration. You can retrieve the list of available document types from the [configuration service](Process-Configuration.md).
|
|
329
|
+
After the user approves the consent, present a document selector for documents which will be scanned. The number and types of documents (or other rules like 1 type required) are completely dependent on your backend system integration. You can retrieve the list of available document types from the [configuration service](Process-Configuration.md) or have it hard-coded.
|
|
211
330
|
|
|
212
331
|
For example, your system might require a national ID and one additional document like a driver's license, passport, or any other government-issued personal document.
|
|
213
332
|
|
|
214
333
|
```typescript
|
|
215
334
|
const verification: WDOVerificationService // configured instance
|
|
216
335
|
try {
|
|
336
|
+
// assuming user selected ID card and driver's license
|
|
217
337
|
const docTypesResult = await verification.documentsSetSelectedTypes([
|
|
218
338
|
WDODocumentType.idCard,
|
|
219
339
|
WDODocumentType.driversLicense
|
|
@@ -236,7 +356,16 @@ This step does not move the state of the process but is a "stand-alone" API call
|
|
|
236
356
|
|
|
237
357
|
Since the document scanning itself is not provided by this library but by a 3rd party library, some of them need a server-side initialization.
|
|
238
358
|
|
|
239
|
-
If your chosen
|
|
359
|
+
If your chosen "document scan SDK" requires such a step, use this function to retrieve necessary data from the server.
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
const verification: WDOVerificationService // configured instance
|
|
365
|
+
const challengeFromSDK = "..." // optional challenge from the scanning SDK
|
|
366
|
+
const initResult = await verification.documentsInitSDK(challengeFromSDK)
|
|
367
|
+
// use the `initResult` to initialize the scanning SDK
|
|
368
|
+
```
|
|
240
369
|
|
|
241
370
|
## Scanning a document
|
|
242
371
|
|
|
@@ -256,7 +385,7 @@ for your implementation.
|
|
|
256
385
|
When a document is scanned (both sides when required), it needs to be uploaded to the server.
|
|
257
386
|
|
|
258
387
|
<!-- begin box warning -->
|
|
259
|
-
__Images of the document should not be bigger than
|
|
388
|
+
__Images of the document should not be bigger than hundreds of kilobytes. Files that are too big will take longer time to upload and process on the server.__
|
|
260
389
|
<!-- end -->
|
|
261
390
|
|
|
262
391
|
To upload a document, use `documentsSubmit` function. Each side of a document is a single `WDODocumentFile` instance.
|
|
@@ -270,8 +399,8 @@ const passportToUpload = WDODocumentFile(
|
|
|
270
399
|
"BASE64_ENCODED_IMAGE_DATA", // raw image data from the document scanning library/photo camera
|
|
271
400
|
WDODocumentType.passport,
|
|
272
401
|
WDODocumentSide.front, // passport has only front side
|
|
273
|
-
undefined, // use only when re-uploading the file
|
|
274
|
-
undefined //
|
|
402
|
+
undefined, // original id, optional (use only when re-uploading the file - for example when first upload was rejected because of a blur)
|
|
403
|
+
undefined // signature, optional (use when provided by the document scanning library)
|
|
275
404
|
)
|
|
276
405
|
try {
|
|
277
406
|
const result = await verification.documentsSubmit([passportToUpload])
|
|
@@ -363,6 +492,35 @@ try {
|
|
|
363
492
|
}
|
|
364
493
|
```
|
|
365
494
|
|
|
495
|
+
## Finalizing the verification (optional)
|
|
496
|
+
|
|
497
|
+
When the state `finishActivation` is received, prompt the user for a PIN code.
|
|
498
|
+
|
|
499
|
+
This PIN code is then used to activate a new `PowerAuth` object that will be used for signing requests.
|
|
500
|
+
|
|
501
|
+
Once the new `PowerAuth` instance is activated, the verification process is finished, and the user can proceed to the main app flow *with the new `PowerAuth` instance*.
|
|
502
|
+
|
|
503
|
+
<!-- begin box info -->
|
|
504
|
+
If the user's PIN used for the original activation should be equal to the one used for the new activation, then:
|
|
505
|
+
- Set the `validatePassword` parameter to `true` in the `finishActivation` call.
|
|
506
|
+
- The `PowerAuthPassword` passed to the `finishActivation` needs to be reusable (`destroyOnUse` set to false - `new PowerAuthPassword(false)`).
|
|
507
|
+
<!-- end -->
|
|
508
|
+
|
|
509
|
+
Example:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
const verification: WDOVerificationService // configured instance
|
|
513
|
+
const newPaInstance: PowerAuth // new PowerAuth instance to be activated and then used in the app
|
|
514
|
+
try {
|
|
515
|
+
const password = await PowerAuthPassword.fromString("user-password", false) // reusable password
|
|
516
|
+
const finishResult = await verification.finishActivation(newPaInstance, "my-new-activation-name", password, true)
|
|
517
|
+
// When here, the newPaInstance is activated and ready to use (to sign requests and so on).
|
|
518
|
+
// The original PowerAuth instance used for the verification will be in the `REMOVED` state and the `verification` instance can't be used anymore.
|
|
519
|
+
} catch (error) {
|
|
520
|
+
// handle error
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
366
524
|
## Success state
|
|
367
525
|
|
|
368
526
|
When a whole verification is finished, you will receive the `success` state. Show a success screen and navigate the user to a common activated flow.
|
package/docs/_Sidebar.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- [SDK Integration](SDK-Integration.md)
|
|
4
4
|
- [Process Configuration](Process-Configuration.md)
|
|
5
5
|
- [Device Activation (With Weak Credentials)](Device-Activation.md)
|
|
6
|
-
- [Verifying User
|
|
6
|
+
- [Verifying the User](Verifying-User.md)
|
|
7
7
|
- [Language Configuration](Language-Configuration.md)
|
|
8
8
|
- [Logging](Logging.md)
|
|
9
9
|
|
|
Binary file
|
package/docs/images/intro.jpg
CHANGED
|
Binary file
|
|
Binary file
|