@capawesome/capacitor-app-integrity 0.0.1
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/CapawesomeCapacitorAppIntegrity.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +385 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/AppIntegrity.java +208 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/AppIntegrityPlugin.java +137 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/CustomException.java +20 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/CustomExceptions.java +11 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/options/PrepareIntegrityTokenOptions.java +26 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/options/RequestIntegrityTokenOptions.java +51 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/results/IsAvailableResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/classes/results/RequestIntegrityTokenResult.java +23 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/interfaces/EmptyCallback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/appintegrity/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +472 -0
- package/dist/esm/definitions.d.ts +384 -0
- package/dist/esm/definitions.js +152 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +10 -0
- package/dist/esm/web.js +22 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +188 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +191 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/AppIntegrity.swift +78 -0
- package/ios/Plugin/AppIntegrityPlugin.swift +116 -0
- package/ios/Plugin/Classes/Options/AttestKeyOptions.swift +29 -0
- package/ios/Plugin/Classes/Options/GenerateAssertionOptions.swift +29 -0
- package/ios/Plugin/Classes/Results/AttestKeyResult.swift +16 -0
- package/ios/Plugin/Classes/Results/GenerateAssertionResult.swift +16 -0
- package/ios/Plugin/Classes/Results/GenerateKeyResult.swift +16 -0
- package/ios/Plugin/Classes/Results/IsAvailableResult.swift +16 -0
- package/ios/Plugin/Enums/CustomError.swift +59 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/Protocols/Result.swift +5 -0
- package/package.json +91 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CapawesomeCapacitorAppIntegrity'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '15.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robin Genz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/Package.swift
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "CapawesomeCapacitorAppIntegrity",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "CapawesomeCapacitorAppIntegrity",
|
|
10
|
+
targets: ["AppIntegrityPlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "AppIntegrityPlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Plugin"),
|
|
23
|
+
.testTarget(
|
|
24
|
+
name: "AppIntegrityPluginTests",
|
|
25
|
+
dependencies: ["AppIntegrityPlugin"],
|
|
26
|
+
path: "ios/PluginTests")
|
|
27
|
+
]
|
|
28
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# @capawesome/capacitor-app-integrity
|
|
2
|
+
|
|
3
|
+
Capacitor plugin to verify app and device integrity using the Play Integrity API (Android) and App Attest (iOS).
|
|
4
|
+
|
|
5
|
+
<div class="capawesome-z29o10a">
|
|
6
|
+
<a href="https://cloud.capawesome.io/" target="_blank">
|
|
7
|
+
<img alt="Deliver Live Updates to your Capacitor app with Capawesome Cloud" src="https://cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
|
|
8
|
+
</a>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🤖 **Play Integrity**: Request standard and classic integrity tokens from the Play Integrity API on Android.
|
|
14
|
+
- 🍏 **App Attest**: Generate hardware-backed keys, attest them and generate assertions with App Attest on iOS.
|
|
15
|
+
- 🛡️ **Server-verifiable**: Tokens and assertions are verified on your backend via Google and Apple, so they cannot be spoofed on the device.
|
|
16
|
+
- 🔗 **Compatibility**: Works alongside the [Root Detection](https://capawesome.io/plugins/root-detection/) plugin for additional client-side checks.
|
|
17
|
+
- 📦 **SPM**: Supports Swift Package Manager and CocoaPods for iOS.
|
|
18
|
+
- 🔁 **Up-to-date**: Always supports the latest Capacitor version.
|
|
19
|
+
|
|
20
|
+
Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
|
|
21
|
+
|
|
22
|
+
## Newsletter
|
|
23
|
+
|
|
24
|
+
Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
|
|
25
|
+
|
|
26
|
+
## Compatibility
|
|
27
|
+
|
|
28
|
+
| Plugin Version | Capacitor Version | Status |
|
|
29
|
+
| -------------- | ----------------- | -------------- |
|
|
30
|
+
| 0.x.x | >=8.x.x | Active support |
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
You can use our **AI-Assisted Setup** to install the plugin.
|
|
35
|
+
Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx skills add capawesome-team/skills --skill capacitor-plugins
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then use the following prompt:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-app-integrity` plugin in my project.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @capawesome/capacitor-app-integrity
|
|
51
|
+
npx cap sync
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Android
|
|
55
|
+
|
|
56
|
+
The Play Integrity API must be set up in the Google Play Console before you can request integrity tokens. Follow the [official setup instructions](https://developer.android.com/google/play/integrity/setup) to link a Google Cloud project to your app. You need the **Google Cloud project number** of the linked project for the standard request flow.
|
|
57
|
+
|
|
58
|
+
#### Variables
|
|
59
|
+
|
|
60
|
+
This plugin will use the following project variables (defined in your app's `variables.gradle` file):
|
|
61
|
+
|
|
62
|
+
- `$playIntegrityVersion` version of `com.google.android.play:integrity` (default: `1.6.0`)
|
|
63
|
+
|
|
64
|
+
### iOS
|
|
65
|
+
|
|
66
|
+
App Attest requires the **App Attest capability**. Add it to your app in Xcode under **Signing & Capabilities** or add the following entitlement to your `.entitlements` file:
|
|
67
|
+
|
|
68
|
+
```xml
|
|
69
|
+
<key>com.apple.developer.devicecheck.appattest-environment</key>
|
|
70
|
+
<string>development</string>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use the value `development` to attest against Apple's sandbox servers during development and `production` for App Store builds. Note that App Attest is not supported on simulators, so you need a real device for testing.
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
No configuration required for this plugin.
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { AppIntegrity } from '@capawesome/capacitor-app-integrity';
|
|
83
|
+
import { Capacitor } from '@capacitor/core';
|
|
84
|
+
|
|
85
|
+
const isAvailable = async () => {
|
|
86
|
+
const { available } = await AppIntegrity.isAvailable();
|
|
87
|
+
return available;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const prepareIntegrityToken = async () => {
|
|
91
|
+
// Only available on Android
|
|
92
|
+
await AppIntegrity.prepareIntegrityToken({
|
|
93
|
+
cloudProjectNumber: 123456789012,
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const requestIntegrityToken = async () => {
|
|
98
|
+
// Only available on Android
|
|
99
|
+
if (Capacitor.getPlatform() === 'android') {
|
|
100
|
+
// Standard request (recommended, requires a prior call to `prepareIntegrityToken(...)`)
|
|
101
|
+
const { token } = await AppIntegrity.requestIntegrityToken({
|
|
102
|
+
requestHash: '2cp24z...',
|
|
103
|
+
});
|
|
104
|
+
// Classic request
|
|
105
|
+
const { token: classicToken } = await AppIntegrity.requestIntegrityToken({
|
|
106
|
+
nonce: 'R2Rra24fVm5xa2Mg',
|
|
107
|
+
});
|
|
108
|
+
// Send the token to your server for verification
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const attestKey = async () => {
|
|
113
|
+
// Only available on iOS
|
|
114
|
+
if (Capacitor.getPlatform() === 'ios') {
|
|
115
|
+
// 1. Generate a new key pair and store the key identifier
|
|
116
|
+
const { keyId } = await AppIntegrity.generateKey();
|
|
117
|
+
// 2. Attest the key with a one-time challenge received from your server
|
|
118
|
+
const { attestationObject } = await AppIntegrity.attestKey({
|
|
119
|
+
keyId,
|
|
120
|
+
challenge: 'dGhpc2lzYWNoYWxsZW5nZQ==',
|
|
121
|
+
});
|
|
122
|
+
// 3. Send the attestation object to your server for verification
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const generateAssertion = async () => {
|
|
127
|
+
// Only available on iOS
|
|
128
|
+
if (Capacitor.getPlatform() === 'ios') {
|
|
129
|
+
const { assertion } = await AppIntegrity.generateAssertion({
|
|
130
|
+
keyId: 'Kh0DIEwVJTDJUyIRZ4M9BvJn/i4RSSGDkFvUZOaSm5g=',
|
|
131
|
+
clientData: 'eyJjaGFsbGVuZ2UiOiJkR2hwYzJsellXTm9ZV3hzWlc1blpRPT0ifQ==',
|
|
132
|
+
});
|
|
133
|
+
// Send the assertion to your server for verification
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## API
|
|
139
|
+
|
|
140
|
+
<docgen-index>
|
|
141
|
+
|
|
142
|
+
* [`attestKey(...)`](#attestkey)
|
|
143
|
+
* [`generateAssertion(...)`](#generateassertion)
|
|
144
|
+
* [`generateKey()`](#generatekey)
|
|
145
|
+
* [`isAvailable()`](#isavailable)
|
|
146
|
+
* [`prepareIntegrityToken(...)`](#prepareintegritytoken)
|
|
147
|
+
* [`requestIntegrityToken(...)`](#requestintegritytoken)
|
|
148
|
+
* [Interfaces](#interfaces)
|
|
149
|
+
|
|
150
|
+
</docgen-index>
|
|
151
|
+
|
|
152
|
+
<docgen-api>
|
|
153
|
+
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
|
|
154
|
+
|
|
155
|
+
### attestKey(...)
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
attestKey(options: AttestKeyOptions) => Promise<AttestKeyResult>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Attest a key generated with `generateKey()` using Apple's App Attest service.
|
|
162
|
+
|
|
163
|
+
The returned attestation object must be sent to your server, which verifies
|
|
164
|
+
it with Apple and stores the key identifier for future assertions.
|
|
165
|
+
|
|
166
|
+
Only available on iOS.
|
|
167
|
+
|
|
168
|
+
| Param | Type |
|
|
169
|
+
| ------------- | ------------------------------------------------------------- |
|
|
170
|
+
| **`options`** | <code><a href="#attestkeyoptions">AttestKeyOptions</a></code> |
|
|
171
|
+
|
|
172
|
+
**Returns:** <code>Promise<<a href="#attestkeyresult">AttestKeyResult</a>></code>
|
|
173
|
+
|
|
174
|
+
**Since:** 0.1.0
|
|
175
|
+
|
|
176
|
+
--------------------
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### generateAssertion(...)
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
generateAssertion(options: GenerateAssertionOptions) => Promise<GenerateAssertionResult>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Generate an assertion for the given client data using an attested key.
|
|
186
|
+
|
|
187
|
+
The returned assertion must be sent to your server along with the client
|
|
188
|
+
data, where it is verified using the previously stored key identifier.
|
|
189
|
+
|
|
190
|
+
Only available on iOS.
|
|
191
|
+
|
|
192
|
+
| Param | Type |
|
|
193
|
+
| ------------- | ----------------------------------------------------------------------------- |
|
|
194
|
+
| **`options`** | <code><a href="#generateassertionoptions">GenerateAssertionOptions</a></code> |
|
|
195
|
+
|
|
196
|
+
**Returns:** <code>Promise<<a href="#generateassertionresult">GenerateAssertionResult</a>></code>
|
|
197
|
+
|
|
198
|
+
**Since:** 0.1.0
|
|
199
|
+
|
|
200
|
+
--------------------
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
### generateKey()
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
generateKey() => Promise<GenerateKeyResult>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Generate a new App Attest key pair.
|
|
210
|
+
|
|
211
|
+
The private key is stored in the Secure Enclave. Store the returned key
|
|
212
|
+
identifier in your app to attest the key and generate assertions later.
|
|
213
|
+
|
|
214
|
+
Only available on iOS.
|
|
215
|
+
|
|
216
|
+
**Returns:** <code>Promise<<a href="#generatekeyresult">GenerateKeyResult</a>></code>
|
|
217
|
+
|
|
218
|
+
**Since:** 0.1.0
|
|
219
|
+
|
|
220
|
+
--------------------
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
### isAvailable()
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
isAvailable() => Promise<IsAvailableResult>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Check whether app integrity attestation is available on this device.
|
|
230
|
+
|
|
231
|
+
On Android, this checks whether Google Play Services is available.
|
|
232
|
+
On iOS, this checks whether the App Attest service is supported.
|
|
233
|
+
On iOS, the App Attest service is not supported on simulators.
|
|
234
|
+
|
|
235
|
+
**Returns:** <code>Promise<<a href="#isavailableresult">IsAvailableResult</a>></code>
|
|
236
|
+
|
|
237
|
+
**Since:** 0.1.0
|
|
238
|
+
|
|
239
|
+
--------------------
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
### prepareIntegrityToken(...)
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
prepareIntegrityToken(options: PrepareIntegrityTokenOptions) => Promise<void>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Prepare the integrity token provider for standard integrity token requests.
|
|
249
|
+
|
|
250
|
+
Call this method once, for example at app start, before calling
|
|
251
|
+
`requestIntegrityToken(...)` with a request hash. The preparation can take
|
|
252
|
+
several seconds, so it should be done well ahead of the first request.
|
|
253
|
+
|
|
254
|
+
Only available on Android.
|
|
255
|
+
|
|
256
|
+
| Param | Type |
|
|
257
|
+
| ------------- | ------------------------------------------------------------------------------------- |
|
|
258
|
+
| **`options`** | <code><a href="#prepareintegritytokenoptions">PrepareIntegrityTokenOptions</a></code> |
|
|
259
|
+
|
|
260
|
+
**Since:** 0.1.0
|
|
261
|
+
|
|
262
|
+
--------------------
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
### requestIntegrityToken(...)
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
requestIntegrityToken(options: RequestIntegrityTokenOptions) => Promise<RequestIntegrityTokenResult>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Request an integrity token from the Play Integrity API.
|
|
272
|
+
|
|
273
|
+
Provide a `requestHash` for a standard request (recommended, requires a
|
|
274
|
+
prior call to `prepareIntegrityToken(...)`) or a `nonce` for a classic request.
|
|
275
|
+
|
|
276
|
+
The returned token must be sent to your server, which decrypts and
|
|
277
|
+
verifies it via Google's servers.
|
|
278
|
+
|
|
279
|
+
Only available on Android.
|
|
280
|
+
|
|
281
|
+
| Param | Type |
|
|
282
|
+
| ------------- | ------------------------------------------------------------------------------------- |
|
|
283
|
+
| **`options`** | <code><a href="#requestintegritytokenoptions">RequestIntegrityTokenOptions</a></code> |
|
|
284
|
+
|
|
285
|
+
**Returns:** <code>Promise<<a href="#requestintegritytokenresult">RequestIntegrityTokenResult</a>></code>
|
|
286
|
+
|
|
287
|
+
**Since:** 0.1.0
|
|
288
|
+
|
|
289
|
+
--------------------
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
### Interfaces
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
#### AttestKeyResult
|
|
296
|
+
|
|
297
|
+
| Prop | Type | Description | Since |
|
|
298
|
+
| ----------------------- | ------------------- | --------------------------------------------------------------------------------------------------------- | ----- |
|
|
299
|
+
| **`attestationObject`** | <code>string</code> | The attestation object, encoded as a base64 string. Send this to your server for verification with Apple. | 0.1.0 |
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
#### AttestKeyOptions
|
|
303
|
+
|
|
304
|
+
| Prop | Type | Description | Since |
|
|
305
|
+
| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
306
|
+
| **`challenge`** | <code>string</code> | The one-time challenge received from your server, encoded as a base64 string. The challenge is hashed with SHA-256 on the device before it is passed to the App Attest service. | 0.1.0 |
|
|
307
|
+
| **`keyId`** | <code>string</code> | The identifier of the key to attest, as returned by `generateKey()`. | 0.1.0 |
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
#### GenerateAssertionResult
|
|
311
|
+
|
|
312
|
+
| Prop | Type | Description | Since |
|
|
313
|
+
| --------------- | ------------------- | -------------------------------------------------------------------------------------------- | ----- |
|
|
314
|
+
| **`assertion`** | <code>string</code> | The assertion object, encoded as a base64 string. Send this to your server for verification. | 0.1.0 |
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
#### GenerateAssertionOptions
|
|
318
|
+
|
|
319
|
+
| Prop | Type | Description | Since |
|
|
320
|
+
| ---------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
321
|
+
| **`clientData`** | <code>string</code> | The client data to sign, encoded as a base64 string. This is usually a JSON payload that includes a one-time challenge received from your server. The client data is hashed with SHA-256 on the device before it is passed to the App Attest service. | 0.1.0 |
|
|
322
|
+
| **`keyId`** | <code>string</code> | The identifier of an attested key, as returned by `generateKey()`. | 0.1.0 |
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
#### GenerateKeyResult
|
|
326
|
+
|
|
327
|
+
| Prop | Type | Description | Since |
|
|
328
|
+
| ----------- | ------------------- | ----------------------------------------- | ----- |
|
|
329
|
+
| **`keyId`** | <code>string</code> | The identifier of the generated key pair. | 0.1.0 |
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
#### IsAvailableResult
|
|
333
|
+
|
|
334
|
+
| Prop | Type | Description | Since |
|
|
335
|
+
| --------------- | -------------------- | -------------------------------------------------------------- | ----- |
|
|
336
|
+
| **`available`** | <code>boolean</code> | Whether app integrity attestation is available on this device. | 0.1.0 |
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
#### PrepareIntegrityTokenOptions
|
|
340
|
+
|
|
341
|
+
| Prop | Type | Description | Since |
|
|
342
|
+
| ------------------------ | ------------------- | ----------------------------------------------------------------------------------------------------- | ----- |
|
|
343
|
+
| **`cloudProjectNumber`** | <code>number</code> | The Google Cloud project number of the project that is linked to your Play Console developer account. | 0.1.0 |
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
#### RequestIntegrityTokenResult
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Description | Since |
|
|
349
|
+
| ----------- | ------------------- | --------------------------------------------------------------------------------------------------- | ----- |
|
|
350
|
+
| **`token`** | <code>string</code> | The integrity token. Send this to your server, which decrypts and verifies it via Google's servers. | 0.1.0 |
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
#### RequestIntegrityTokenOptions
|
|
354
|
+
|
|
355
|
+
| Prop | Type | Description | Since |
|
|
356
|
+
| ------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
357
|
+
| **`cloudProjectNumber`** | <code>number</code> | The Google Cloud project number of the project that is linked to your Play Console developer account. Only used for classic requests. It is required if your app is distributed outside of Google Play. | 0.1.0 |
|
|
358
|
+
| **`nonce`** | <code>string</code> | The one-time nonce for a classic request, encoded as a base64 web-safe no-wrap string. Provide either `nonce` or `requestHash`. | 0.1.0 |
|
|
359
|
+
| **`requestHash`** | <code>string</code> | The request hash for a standard request. Requires a prior call to `prepareIntegrityToken(...)`. Provide either `nonce` or `requestHash`. | 0.1.0 |
|
|
360
|
+
|
|
361
|
+
</docgen-api>
|
|
362
|
+
|
|
363
|
+
## How Verification Works
|
|
364
|
+
|
|
365
|
+
This plugin only provides the **client-side** part of the attestation flow. The returned tokens, attestation objects, and assertions are opaque to your app and **must** be verified on your own server. Never make security decisions based on the client-side result alone.
|
|
366
|
+
|
|
367
|
+
The typical flow looks like this:
|
|
368
|
+
|
|
369
|
+
1. Your app requests a one-time challenge (or nonce) from your server.
|
|
370
|
+
2. Your app calls this plugin to obtain an integrity token (Android) or an attestation object/assertion (iOS) that is bound to the challenge.
|
|
371
|
+
3. Your app sends the result to your server.
|
|
372
|
+
4. Your server verifies the result:
|
|
373
|
+
- **Android**: Decrypt and verify the integrity token via the [Play Integrity API](https://developer.android.com/google/play/integrity/verdicts) and evaluate the verdicts.
|
|
374
|
+
- **iOS**: Validate the attestation object or assertion as described in [Validating Apps That Connect to Your Server](https://developer.apple.com/documentation/devicecheck/validating-apps-that-connect-to-your-server).
|
|
375
|
+
5. Your server grants or denies access based on the verification result.
|
|
376
|
+
|
|
377
|
+
On Android, Google recommends the **standard request** flow (`prepareIntegrityToken(...)` + `requestIntegrityToken({ requestHash })`) for frequent integrity checks. Use the **classic request** flow (`requestIntegrityToken({ nonce })`) only for infrequent, high-value actions.
|
|
378
|
+
|
|
379
|
+
## Changelog
|
|
380
|
+
|
|
381
|
+
See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-integrity/CHANGELOG.md).
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-integrity/LICENSE).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
playIntegrityVersion = project.hasProperty('playIntegrityVersion') ? rootProject.ext.playIntegrityVersion : '1.6.0'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
buildscript {
|
|
10
|
+
repositories {
|
|
11
|
+
google()
|
|
12
|
+
mavenCentral()
|
|
13
|
+
}
|
|
14
|
+
dependencies {
|
|
15
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply plugin: 'com.android.library'
|
|
20
|
+
|
|
21
|
+
android {
|
|
22
|
+
namespace = "io.capawesome.capacitorjs.plugins.appintegrity"
|
|
23
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
24
|
+
defaultConfig {
|
|
25
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
26
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
27
|
+
versionCode 1
|
|
28
|
+
versionName "1.0"
|
|
29
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
30
|
+
}
|
|
31
|
+
buildTypes {
|
|
32
|
+
release {
|
|
33
|
+
minifyEnabled false
|
|
34
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
lintOptions {
|
|
38
|
+
abortOnError = false
|
|
39
|
+
}
|
|
40
|
+
compileOptions {
|
|
41
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
42
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
repositories {
|
|
47
|
+
google()
|
|
48
|
+
mavenCentral()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
dependencies {
|
|
53
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
54
|
+
implementation project(':capacitor-android')
|
|
55
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
56
|
+
implementation "com.google.android.play:integrity:$playIntegrityVersion"
|
|
57
|
+
testImplementation "junit:junit:$junitVersion"
|
|
58
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
59
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
60
|
+
}
|