@capgo/capacitor-device-integrity 8.0.2
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/CapgoCapacitorDeviceIntegrity.podspec +17 -0
- package/LICENSE +373 -0
- package/Package.swift +28 -0
- package/README.md +346 -0
- package/android/build.gradle +61 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/app/capgo/deviceintegrity/DeviceIntegrity.java +276 -0
- package/android/src/main/java/app/capgo/deviceintegrity/DeviceIntegrityPlugin.java +260 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +495 -0
- package/dist/esm/definitions.d.ts +218 -0
- package/dist/esm/definitions.js +3 -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 +37 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +47 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +50 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/DeviceIntegrityPlugin/DeviceIntegrity.swift +49 -0
- package/ios/Sources/DeviceIntegrityPlugin/DeviceIntegrityPlugin.swift +208 -0
- package/ios/Tests/DeviceIntegrityPluginTests/DeviceIntegrityTests.swift +24 -0
- package/package.json +92 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * Device Integrity plugin configuration.\n */\n DeviceIntegrity?: DeviceIntegrityPluginConfig;\n }\n}\n\n/**\n * Capacitor config for the Device Integrity plugin.\n */\nexport interface DeviceIntegrityPluginConfig {\n /**\n * Android only. Google Cloud project number used by the Play Integrity Standard API.\n *\n * Example:\n * `plugins.DeviceIntegrity.cloudProjectNumber = '123456789012'`\n */\n cloudProjectNumber?: string;\n}\n\nexport type DeviceIntegrityPlatform = 'android' | 'ios' | 'web';\n\nexport type AttestationFormat = 'apple-app-attest' | 'google-play-integrity-standard';\n\nexport interface DeviceIntegrityPlugin {\n /**\n * Returns the native integrity capabilities available on the current platform.\n */\n getCapabilities(): Promise<DeviceIntegrityCapabilities>;\n\n /**\n * Returns an Android Widevine-derived fingerprint.\n *\n * The default `fingerprint` is SHA-256 over the Widevine device unique ID and a salt.\n * If `hashSalt` is not provided, Android uses the app package name as the salt.\n *\n * The raw Widevine ID is sensitive and is only returned as base64 when `includeRawId` is true.\n */\n getWidevineFingerprint(options?: WidevineFingerprintOptions): Promise<WidevineFingerprintResult>;\n\n /**\n * Prepares native attestation state and returns the key/provider handle.\n *\n * iOS: creates an App Attest key.\n * Android: prepares a Play Integrity Standard token provider.\n */\n prepareAttestation(options?: PrepareAttestationOptions): Promise<PrepareAttestationResult>;\n\n /**\n * Creates a registration attestation token bound to a backend-issued challenge.\n */\n createAttestation(options: CreateAttestationOptions): Promise<AttestationTokenResult>;\n\n /**\n * Creates a request assertion token bound to a request payload.\n */\n createAssertion(options: CreateAssertionOptions): Promise<AttestationTokenResult>;\n\n /**\n * Creates an iOS DeviceCheck token for server-side fraud-state lookups.\n */\n getDeviceCheckToken(): Promise<DeviceCheckTokenResult>;\n}\n\nexport interface DeviceIntegrityCapabilities {\n /**\n * Platform currently executing the plugin.\n */\n platform: DeviceIntegrityPlatform;\n\n /**\n * Android Widevine DRM support.\n */\n widevine: WidevineCapabilities;\n\n /**\n * iOS App Attest support.\n */\n appAttest: SupportStatus;\n\n /**\n * iOS DeviceCheck support.\n */\n deviceCheck: SupportStatus;\n\n /**\n * Android Play Integrity support.\n */\n playIntegrity: SupportStatus;\n}\n\nexport interface WidevineCapabilities {\n /**\n * Whether the Widevine DRM scheme is supported by the device.\n */\n supported: boolean;\n\n /**\n * Whether the Widevine device unique ID can be read.\n */\n fingerprintAvailable: boolean;\n\n /**\n * Whether the Widevine security level property can be read.\n */\n securityLevelScanSupported: boolean;\n}\n\nexport interface SupportStatus {\n /**\n * Whether the capability is available on the current device.\n */\n supported: boolean;\n}\n\nexport interface WidevineFingerprintOptions {\n /**\n * Return the raw Widevine device unique ID as base64.\n *\n * Defaults to `false`.\n */\n includeRawId?: boolean;\n\n /**\n * Optional salt used to derive `fingerprint`.\n *\n * Android uses the app package name when omitted.\n */\n hashSalt?: string;\n}\n\nexport interface WidevineFingerprintResult {\n /**\n * Always `android`.\n */\n platform: 'android';\n\n /**\n * Always `widevine`.\n */\n source: 'widevine';\n\n /**\n * Salted SHA-256 fingerprint for storing alongside a user record.\n */\n fingerprint: string;\n\n /**\n * Unsalted SHA-256 hash of the Widevine device unique ID.\n */\n widevineIdSha256: string;\n\n /**\n * Raw Widevine device unique ID encoded as base64.\n *\n * Returned only when `includeRawId` is true.\n */\n widevineIdBase64?: string;\n\n /**\n * Widevine security level when available, for example `L1` or `L3`.\n */\n securityLevel?: string;\n\n /**\n * DRM vendor when available.\n */\n vendor?: string;\n\n /**\n * DRM plugin version when available.\n */\n version?: string;\n\n /**\n * DRM plugin description when available.\n */\n description?: string;\n}\n\nexport interface PrepareAttestationOptions {\n /**\n * Android only. Google Cloud project number for Play Integrity.\n *\n * Can be configured globally via `plugins.DeviceIntegrity.cloudProjectNumber`.\n */\n cloudProjectNumber?: string;\n}\n\nexport interface PrepareAttestationResult {\n /**\n * iOS App Attest key ID or Android Play Integrity provider handle ID.\n */\n keyId: string;\n\n /**\n * Native attestation format used by the current platform.\n */\n format: AttestationFormat;\n}\n\nexport interface CreateAttestationOptions {\n /**\n * Key/provider handle returned from `prepareAttestation()`.\n */\n keyId: string;\n\n /**\n * Backend-issued one-time challenge.\n */\n challenge: string;\n\n /**\n * Android only. Google Cloud project number for Play Integrity.\n */\n cloudProjectNumber?: string;\n}\n\nexport interface CreateAssertionOptions {\n /**\n * Key/provider handle returned from `prepareAttestation()`.\n */\n keyId: string;\n\n /**\n * Backend-issued one-time request payload or nonce.\n */\n payload: string;\n\n /**\n * Android only. Google Cloud project number for Play Integrity.\n */\n cloudProjectNumber?: string;\n}\n\nexport interface AttestationTokenResult {\n /**\n * Token/assertion that must be verified server-side.\n */\n token: string;\n\n /**\n * Key/provider handle used to create the token.\n */\n keyId: string;\n\n /**\n * Native attestation format used by the current platform.\n */\n format: AttestationFormat;\n}\n\nexport interface DeviceCheckTokenResult {\n /**\n * iOS DeviceCheck token encoded as base64.\n */\n token: string;\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerPlugin } from '@capacitor/core';
|
|
2
|
+
const DeviceIntegrity = registerPlugin('DeviceIntegrity', {
|
|
3
|
+
web: () => import('./web').then((m) => new m.DeviceIntegrityWeb()),
|
|
4
|
+
});
|
|
5
|
+
export * from './definitions';
|
|
6
|
+
export { DeviceIntegrity };
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,eAAe,GAAG,cAAc,CAAwB,iBAAiB,EAAE;IAC/E,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,kBAAkB,EAAE,CAAC;CACnE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { DeviceIntegrityPlugin } from './definitions';\n\nconst DeviceIntegrity = registerPlugin<DeviceIntegrityPlugin>('DeviceIntegrity', {\n web: () => import('./web').then((m) => new m.DeviceIntegrityWeb()),\n});\n\nexport * from './definitions';\nexport { DeviceIntegrity };\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import type { AttestationTokenResult, CreateAssertionOptions, CreateAttestationOptions, DeviceCheckTokenResult, DeviceIntegrityCapabilities, DeviceIntegrityPlugin, PrepareAttestationOptions, PrepareAttestationResult, WidevineFingerprintOptions, WidevineFingerprintResult } from './definitions';
|
|
3
|
+
export declare class DeviceIntegrityWeb extends WebPlugin implements DeviceIntegrityPlugin {
|
|
4
|
+
getCapabilities(): Promise<DeviceIntegrityCapabilities>;
|
|
5
|
+
getWidevineFingerprint(_options?: WidevineFingerprintOptions): Promise<WidevineFingerprintResult>;
|
|
6
|
+
prepareAttestation(_options?: PrepareAttestationOptions): Promise<PrepareAttestationResult>;
|
|
7
|
+
createAttestation(_options: CreateAttestationOptions): Promise<AttestationTokenResult>;
|
|
8
|
+
createAssertion(_options: CreateAssertionOptions): Promise<AttestationTokenResult>;
|
|
9
|
+
getDeviceCheckToken(): Promise<DeviceCheckTokenResult>;
|
|
10
|
+
}
|
package/dist/esm/web.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
const WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';
|
|
3
|
+
export class DeviceIntegrityWeb extends WebPlugin {
|
|
4
|
+
async getCapabilities() {
|
|
5
|
+
return {
|
|
6
|
+
platform: 'web',
|
|
7
|
+
widevine: {
|
|
8
|
+
supported: false,
|
|
9
|
+
fingerprintAvailable: false,
|
|
10
|
+
securityLevelScanSupported: false,
|
|
11
|
+
},
|
|
12
|
+
appAttest: { supported: false },
|
|
13
|
+
deviceCheck: { supported: false },
|
|
14
|
+
playIntegrity: { supported: false },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async getWidevineFingerprint(_options) {
|
|
18
|
+
void _options;
|
|
19
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
20
|
+
}
|
|
21
|
+
async prepareAttestation(_options) {
|
|
22
|
+
void _options;
|
|
23
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
24
|
+
}
|
|
25
|
+
async createAttestation(_options) {
|
|
26
|
+
void _options;
|
|
27
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
28
|
+
}
|
|
29
|
+
async createAssertion(_options) {
|
|
30
|
+
void _options;
|
|
31
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
32
|
+
}
|
|
33
|
+
async getDeviceCheckToken() {
|
|
34
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAe5C,MAAM,iBAAiB,GAAG,yFAAyF,CAAC;AAEpH,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IAC/C,KAAK,CAAC,eAAe;QACnB,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE;gBACR,SAAS,EAAE,KAAK;gBAChB,oBAAoB,EAAE,KAAK;gBAC3B,0BAA0B,EAAE,KAAK;aAClC;YACD,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC/B,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YACjC,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SACpC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,QAAqC;QAChE,KAAK,QAAQ,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,QAAoC;QAC3D,KAAK,QAAQ,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,QAAkC;QACxD,KAAK,QAAQ,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAgC;QACpD,KAAK,QAAQ,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n AttestationTokenResult,\n CreateAssertionOptions,\n CreateAttestationOptions,\n DeviceCheckTokenResult,\n DeviceIntegrityCapabilities,\n DeviceIntegrityPlugin,\n PrepareAttestationOptions,\n PrepareAttestationResult,\n WidevineFingerprintOptions,\n WidevineFingerprintResult,\n} from './definitions';\n\nconst WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';\n\nexport class DeviceIntegrityWeb extends WebPlugin implements DeviceIntegrityPlugin {\n async getCapabilities(): Promise<DeviceIntegrityCapabilities> {\n return {\n platform: 'web',\n widevine: {\n supported: false,\n fingerprintAvailable: false,\n securityLevelScanSupported: false,\n },\n appAttest: { supported: false },\n deviceCheck: { supported: false },\n playIntegrity: { supported: false },\n };\n }\n\n async getWidevineFingerprint(_options?: WidevineFingerprintOptions): Promise<WidevineFingerprintResult> {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n\n async prepareAttestation(_options?: PrepareAttestationOptions): Promise<PrepareAttestationResult> {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n\n async createAttestation(_options: CreateAttestationOptions): Promise<AttestationTokenResult> {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n\n async createAssertion(_options: CreateAssertionOptions): Promise<AttestationTokenResult> {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n\n async getDeviceCheckToken(): Promise<DeviceCheckTokenResult> {\n throw new Error(WEB_ERROR_MESSAGE);\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@capacitor/core');
|
|
4
|
+
|
|
5
|
+
const DeviceIntegrity = core.registerPlugin('DeviceIntegrity', {
|
|
6
|
+
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.DeviceIntegrityWeb()),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';
|
|
10
|
+
class DeviceIntegrityWeb extends core.WebPlugin {
|
|
11
|
+
async getCapabilities() {
|
|
12
|
+
return {
|
|
13
|
+
platform: 'web',
|
|
14
|
+
widevine: {
|
|
15
|
+
supported: false,
|
|
16
|
+
fingerprintAvailable: false,
|
|
17
|
+
securityLevelScanSupported: false,
|
|
18
|
+
},
|
|
19
|
+
appAttest: { supported: false },
|
|
20
|
+
deviceCheck: { supported: false },
|
|
21
|
+
playIntegrity: { supported: false },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async getWidevineFingerprint(_options) {
|
|
25
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
26
|
+
}
|
|
27
|
+
async prepareAttestation(_options) {
|
|
28
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
29
|
+
}
|
|
30
|
+
async createAttestation(_options) {
|
|
31
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
32
|
+
}
|
|
33
|
+
async createAssertion(_options) {
|
|
34
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
35
|
+
}
|
|
36
|
+
async getDeviceCheckToken() {
|
|
37
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
42
|
+
__proto__: null,
|
|
43
|
+
DeviceIntegrityWeb: DeviceIntegrityWeb
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
exports.DeviceIntegrity = DeviceIntegrity;
|
|
47
|
+
//# sourceMappingURL=plugin.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DeviceIntegrity = registerPlugin('DeviceIntegrity', {\n web: () => import('./web').then((m) => new m.DeviceIntegrityWeb()),\n});\nexport * from './definitions';\nexport { DeviceIntegrity };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';\nexport class DeviceIntegrityWeb extends WebPlugin {\n async getCapabilities() {\n return {\n platform: 'web',\n widevine: {\n supported: false,\n fingerprintAvailable: false,\n securityLevelScanSupported: false,\n },\n appAttest: { supported: false },\n deviceCheck: { supported: false },\n playIntegrity: { supported: false },\n };\n }\n async getWidevineFingerprint(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async prepareAttestation(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async createAttestation(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async createAssertion(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async getDeviceCheckToken() {\n throw new Error(WEB_ERROR_MESSAGE);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,eAAe,GAAGA,mBAAc,CAAC,iBAAiB,EAAE;AAC1D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,kBAAkB,EAAE,CAAC;AACtE,CAAC;;ACFD,MAAM,iBAAiB,GAAG,yFAAyF;AAC5G,MAAM,kBAAkB,SAASC,cAAS,CAAC;AAClD,IAAI,MAAM,eAAe,GAAG;AAC5B,QAAQ,OAAO;AACf,YAAY,QAAQ,EAAE,KAAK;AAC3B,YAAY,QAAQ,EAAE;AACtB,gBAAgB,SAAS,EAAE,KAAK;AAChC,gBAAgB,oBAAoB,EAAE,KAAK;AAC3C,gBAAgB,0BAA0B,EAAE,KAAK;AACjD,aAAa;AACb,YAAY,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;AAC3C,YAAY,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;AAC7C,YAAY,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;AAC/C,SAAS;AACT,IAAI;AACJ,IAAI,MAAM,sBAAsB,CAAC,QAAQ,EAAE;AAE3C,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;AAC1C,IAAI;AACJ,IAAI,MAAM,kBAAkB,CAAC,QAAQ,EAAE;AAEvC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;AAC1C,IAAI;AACJ,IAAI,MAAM,iBAAiB,CAAC,QAAQ,EAAE;AAEtC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;AAC1C,IAAI;AACJ,IAAI,MAAM,eAAe,CAAC,QAAQ,EAAE;AAEpC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;AAC1C,IAAI;AACJ,IAAI,MAAM,mBAAmB,GAAG;AAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;AAC1C,IAAI;AACJ;;;;;;;;;"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var capacitorDeviceIntegrity = (function (exports, core) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const DeviceIntegrity = core.registerPlugin('DeviceIntegrity', {
|
|
5
|
+
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.DeviceIntegrityWeb()),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';
|
|
9
|
+
class DeviceIntegrityWeb extends core.WebPlugin {
|
|
10
|
+
async getCapabilities() {
|
|
11
|
+
return {
|
|
12
|
+
platform: 'web',
|
|
13
|
+
widevine: {
|
|
14
|
+
supported: false,
|
|
15
|
+
fingerprintAvailable: false,
|
|
16
|
+
securityLevelScanSupported: false,
|
|
17
|
+
},
|
|
18
|
+
appAttest: { supported: false },
|
|
19
|
+
deviceCheck: { supported: false },
|
|
20
|
+
playIntegrity: { supported: false },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async getWidevineFingerprint(_options) {
|
|
24
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
25
|
+
}
|
|
26
|
+
async prepareAttestation(_options) {
|
|
27
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
28
|
+
}
|
|
29
|
+
async createAttestation(_options) {
|
|
30
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
31
|
+
}
|
|
32
|
+
async createAssertion(_options) {
|
|
33
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
34
|
+
}
|
|
35
|
+
async getDeviceCheckToken() {
|
|
36
|
+
throw new Error(WEB_ERROR_MESSAGE);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
41
|
+
__proto__: null,
|
|
42
|
+
DeviceIntegrityWeb: DeviceIntegrityWeb
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
exports.DeviceIntegrity = DeviceIntegrity;
|
|
46
|
+
|
|
47
|
+
return exports;
|
|
48
|
+
|
|
49
|
+
})({}, capacitorExports);
|
|
50
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DeviceIntegrity = registerPlugin('DeviceIntegrity', {\n web: () => import('./web').then((m) => new m.DeviceIntegrityWeb()),\n});\nexport * from './definitions';\nexport { DeviceIntegrity };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst WEB_ERROR_MESSAGE = 'Device integrity is not available on web. Use the native Android or iOS implementation.';\nexport class DeviceIntegrityWeb extends WebPlugin {\n async getCapabilities() {\n return {\n platform: 'web',\n widevine: {\n supported: false,\n fingerprintAvailable: false,\n securityLevelScanSupported: false,\n },\n appAttest: { supported: false },\n deviceCheck: { supported: false },\n playIntegrity: { supported: false },\n };\n }\n async getWidevineFingerprint(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async prepareAttestation(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async createAttestation(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async createAssertion(_options) {\n void _options;\n throw new Error(WEB_ERROR_MESSAGE);\n }\n async getDeviceCheckToken() {\n throw new Error(WEB_ERROR_MESSAGE);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,eAAe,GAAGA,mBAAc,CAAC,iBAAiB,EAAE;IAC1D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,kBAAkB,EAAE,CAAC;IACtE,CAAC;;ICFD,MAAM,iBAAiB,GAAG,yFAAyF;IAC5G,MAAM,kBAAkB,SAASC,cAAS,CAAC;IAClD,IAAI,MAAM,eAAe,GAAG;IAC5B,QAAQ,OAAO;IACf,YAAY,QAAQ,EAAE,KAAK;IAC3B,YAAY,QAAQ,EAAE;IACtB,gBAAgB,SAAS,EAAE,KAAK;IAChC,gBAAgB,oBAAoB,EAAE,KAAK;IAC3C,gBAAgB,0BAA0B,EAAE,KAAK;IACjD,aAAa;IACb,YAAY,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IAC3C,YAAY,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IAC7C,YAAY,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IAC/C,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,sBAAsB,CAAC,QAAQ,EAAE;IAE3C,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;IAC1C,IAAI;IACJ,IAAI,MAAM,kBAAkB,CAAC,QAAQ,EAAE;IAEvC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;IAC1C,IAAI;IACJ,IAAI,MAAM,iBAAiB,CAAC,QAAQ,EAAE;IAEtC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;IAC1C,IAAI;IACJ,IAAI,MAAM,eAAe,CAAC,QAAQ,EAAE;IAEpC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;IAC1C,IAAI;IACJ,IAAI,MAAM,mBAAmB,GAAG;IAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC;IAC1C,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import CryptoKit
|
|
2
|
+
import DeviceCheck
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
@objc public class DeviceIntegrity: NSObject {
|
|
6
|
+
@objc public func isAppAttestSupported() -> Bool {
|
|
7
|
+
return DCAppAttestService.shared.isSupported
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@objc public func isDeviceCheckSupported() -> Bool {
|
|
11
|
+
return DCDevice.current.isSupported
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public func createClientDataHash(from input: String) throws -> Data {
|
|
15
|
+
guard let payloadData = input.data(using: .utf8) else {
|
|
16
|
+
throw DeviceIntegrityError.invalidInput
|
|
17
|
+
}
|
|
18
|
+
return Data(SHA256.hash(data: payloadData))
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public enum DeviceIntegrityError: LocalizedError {
|
|
23
|
+
case appAttestNotSupported
|
|
24
|
+
case deviceCheckNotSupported
|
|
25
|
+
case missingKeyId
|
|
26
|
+
case missingChallenge
|
|
27
|
+
case missingPayload
|
|
28
|
+
case missingGeneratedValue
|
|
29
|
+
case invalidInput
|
|
30
|
+
|
|
31
|
+
public var errorDescription: String? {
|
|
32
|
+
switch self {
|
|
33
|
+
case .appAttestNotSupported:
|
|
34
|
+
return "App Attest is not supported on this device"
|
|
35
|
+
case .deviceCheckNotSupported:
|
|
36
|
+
return "DeviceCheck is not supported on this device"
|
|
37
|
+
case .missingKeyId:
|
|
38
|
+
return "keyId is required"
|
|
39
|
+
case .missingChallenge:
|
|
40
|
+
return "challenge is required"
|
|
41
|
+
case .missingPayload:
|
|
42
|
+
return "payload is required"
|
|
43
|
+
case .missingGeneratedValue:
|
|
44
|
+
return "Native API returned no value"
|
|
45
|
+
case .invalidInput:
|
|
46
|
+
return "Invalid input format"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import DeviceCheck
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
@objc(DeviceIntegrityPlugin)
|
|
6
|
+
public class DeviceIntegrityPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
7
|
+
public let identifier = "DeviceIntegrityPlugin"
|
|
8
|
+
public let jsName = "DeviceIntegrity"
|
|
9
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
10
|
+
CAPPluginMethod(name: "getCapabilities", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "getWidevineFingerprint", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "prepareAttestation", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "createAttestation", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "createAssertion", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "getDeviceCheckToken", returnType: CAPPluginReturnPromise)
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
private let implementation = DeviceIntegrity()
|
|
19
|
+
private let appAttestFormat = "apple-app-attest"
|
|
20
|
+
|
|
21
|
+
@objc func getCapabilities(_ call: CAPPluginCall) {
|
|
22
|
+
call.resolve([
|
|
23
|
+
"platform": "ios",
|
|
24
|
+
"widevine": [
|
|
25
|
+
"supported": false,
|
|
26
|
+
"fingerprintAvailable": false,
|
|
27
|
+
"securityLevelScanSupported": false
|
|
28
|
+
],
|
|
29
|
+
"appAttest": [
|
|
30
|
+
"supported": implementation.isAppAttestSupported()
|
|
31
|
+
],
|
|
32
|
+
"deviceCheck": [
|
|
33
|
+
"supported": implementation.isDeviceCheckSupported()
|
|
34
|
+
],
|
|
35
|
+
"playIntegrity": [
|
|
36
|
+
"supported": false
|
|
37
|
+
]
|
|
38
|
+
])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc func getWidevineFingerprint(_ call: CAPPluginCall) {
|
|
42
|
+
call.reject("Widevine fingerprinting is only available on Android")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@objc func prepareAttestation(_ call: CAPPluginCall) {
|
|
46
|
+
do {
|
|
47
|
+
try validateAppAttestSupport()
|
|
48
|
+
|
|
49
|
+
DCAppAttestService.shared.generateKey { [weak self] keyId, error in
|
|
50
|
+
guard let self else {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if let error {
|
|
55
|
+
self.reject(call, message: "Error generating App Attest key: \(error.localizedDescription)", error: error)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
guard let keyId else {
|
|
60
|
+
self.reject(call, message: DeviceIntegrityError.missingGeneratedValue.localizedDescription)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
self.resolve(call, payload: [
|
|
65
|
+
"keyId": keyId,
|
|
66
|
+
"format": self.appAttestFormat
|
|
67
|
+
])
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
reject(call, message: error.localizedDescription, error: error)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@objc func createAttestation(_ call: CAPPluginCall) {
|
|
75
|
+
do {
|
|
76
|
+
try validateAppAttestSupport()
|
|
77
|
+
let keyId = try keyId(from: call)
|
|
78
|
+
let challenge = try challenge(from: call)
|
|
79
|
+
let clientDataHash = try implementation.createClientDataHash(from: challenge)
|
|
80
|
+
|
|
81
|
+
DCAppAttestService.shared.attestKey(keyId, clientDataHash: clientDataHash) { [weak self] attestation, error in
|
|
82
|
+
guard let self else {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if let error {
|
|
87
|
+
self.reject(call, message: "Error creating App Attest token: \(error.localizedDescription)", error: error)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
guard let attestation else {
|
|
92
|
+
self.reject(call, message: DeviceIntegrityError.missingGeneratedValue.localizedDescription)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
self.resolve(call, payload: [
|
|
97
|
+
"token": attestation.base64EncodedString(),
|
|
98
|
+
"keyId": keyId,
|
|
99
|
+
"format": self.appAttestFormat
|
|
100
|
+
])
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
reject(call, message: error.localizedDescription, error: error)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@objc func createAssertion(_ call: CAPPluginCall) {
|
|
108
|
+
do {
|
|
109
|
+
try validateAppAttestSupport()
|
|
110
|
+
let keyId = try keyId(from: call)
|
|
111
|
+
let payload = try payload(from: call)
|
|
112
|
+
let clientDataHash = try implementation.createClientDataHash(from: payload)
|
|
113
|
+
|
|
114
|
+
DCAppAttestService.shared.generateAssertion(keyId, clientDataHash: clientDataHash) { [weak self] assertion, error in
|
|
115
|
+
guard let self else {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if let error {
|
|
120
|
+
self.reject(call, message: "Error creating App Attest assertion: \(error.localizedDescription)", error: error)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
guard let assertion else {
|
|
125
|
+
self.reject(call, message: DeviceIntegrityError.missingGeneratedValue.localizedDescription)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
self.resolve(call, payload: [
|
|
130
|
+
"token": assertion.base64EncodedString(),
|
|
131
|
+
"keyId": keyId,
|
|
132
|
+
"format": self.appAttestFormat
|
|
133
|
+
])
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
reject(call, message: error.localizedDescription, error: error)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@objc func getDeviceCheckToken(_ call: CAPPluginCall) {
|
|
141
|
+
do {
|
|
142
|
+
guard implementation.isDeviceCheckSupported() else {
|
|
143
|
+
throw DeviceIntegrityError.deviceCheckNotSupported
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
DCDevice.current.generateToken { [weak self] token, error in
|
|
147
|
+
guard let self else {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if let error {
|
|
152
|
+
self.reject(call, message: "Error generating DeviceCheck token: \(error.localizedDescription)", error: error)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
guard let token else {
|
|
157
|
+
self.reject(call, message: DeviceIntegrityError.missingGeneratedValue.localizedDescription)
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
self.resolve(call, payload: [
|
|
162
|
+
"token": token.base64EncodedString()
|
|
163
|
+
])
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
reject(call, message: error.localizedDescription, error: error)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private func validateAppAttestSupport() throws {
|
|
171
|
+
guard implementation.isAppAttestSupported() else {
|
|
172
|
+
throw DeviceIntegrityError.appAttestNotSupported
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private func keyId(from call: CAPPluginCall) throws -> String {
|
|
177
|
+
guard let keyId = call.getString("keyId"), !keyId.isEmpty else {
|
|
178
|
+
throw DeviceIntegrityError.missingKeyId
|
|
179
|
+
}
|
|
180
|
+
return keyId
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private func challenge(from call: CAPPluginCall) throws -> String {
|
|
184
|
+
guard let challenge = call.getString("challenge"), !challenge.isEmpty else {
|
|
185
|
+
throw DeviceIntegrityError.missingChallenge
|
|
186
|
+
}
|
|
187
|
+
return challenge
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private func payload(from call: CAPPluginCall) throws -> String {
|
|
191
|
+
guard let payload = call.getString("payload"), !payload.isEmpty else {
|
|
192
|
+
throw DeviceIntegrityError.missingPayload
|
|
193
|
+
}
|
|
194
|
+
return payload
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private func resolve(_ call: CAPPluginCall, payload: [String: Any]) {
|
|
198
|
+
DispatchQueue.main.async {
|
|
199
|
+
call.resolve(payload)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private func reject(_ call: CAPPluginCall, message: String, error: Error? = nil) {
|
|
204
|
+
DispatchQueue.main.async {
|
|
205
|
+
call.reject(message, nil, error)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import CryptoKit
|
|
2
|
+
import XCTest
|
|
3
|
+
@testable import DeviceIntegrityPlugin
|
|
4
|
+
|
|
5
|
+
final class DeviceIntegrityTests: XCTestCase {
|
|
6
|
+
func testCreateClientDataHashUsesSha256() throws {
|
|
7
|
+
let implementation = DeviceIntegrity()
|
|
8
|
+
let payload = "server-issued-challenge"
|
|
9
|
+
|
|
10
|
+
let result = try implementation.createClientDataHash(from: payload)
|
|
11
|
+
let expected = Data(SHA256.hash(data: Data(payload.utf8)))
|
|
12
|
+
|
|
13
|
+
XCTAssertEqual(result, expected)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func testSupportChecksReturnBooleanValues() {
|
|
17
|
+
let implementation = DeviceIntegrity()
|
|
18
|
+
let appAttestSupported = implementation.isAppAttestSupported()
|
|
19
|
+
let deviceCheckSupported = implementation.isDeviceCheckSupported()
|
|
20
|
+
|
|
21
|
+
XCTAssertTrue(appAttestSupported || !appAttestSupported)
|
|
22
|
+
XCTAssertTrue(deviceCheckSupported || !deviceCheckSupported)
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capgo/capacitor-device-integrity",
|
|
3
|
+
"version": "8.0.2",
|
|
4
|
+
"description": "Device integrity and fraud signals for Capacitor using Android Widevine, Play Integrity, iOS App Attest, and DeviceCheck.",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources",
|
|
14
|
+
"ios/Tests",
|
|
15
|
+
"Package.swift",
|
|
16
|
+
"CapgoCapacitorDeviceIntegrity.podspec"
|
|
17
|
+
],
|
|
18
|
+
"author": "Martin Donadieu <martin@capgo.app>",
|
|
19
|
+
"license": "MPL-2.0",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/Cap-go/capacitor-device-integrity.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/Cap-go/capacitor-device-integrity/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://capgo.app/docs/plugins/device-integrity/",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"capacitor",
|
|
30
|
+
"device-integrity",
|
|
31
|
+
"fraud-detection",
|
|
32
|
+
"widevine",
|
|
33
|
+
"app-attest",
|
|
34
|
+
"devicecheck",
|
|
35
|
+
"play-integrity",
|
|
36
|
+
"capgo"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"verify": "bun run verify:ios && bun run verify:android && bun run verify:web",
|
|
40
|
+
"verify:ios": "xcodebuild -scheme CapgoCapacitorDeviceIntegrity -destination generic/platform=iOS",
|
|
41
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
42
|
+
"verify:web": "bun run build",
|
|
43
|
+
"lint": "bun run eslint && bun run prettier -- --check && bun run swiftlint -- lint",
|
|
44
|
+
"fmt": "bun run eslint -- --fix && bun run prettier -- --write && bun run swiftlint -- --fix --format",
|
|
45
|
+
"eslint": "eslint . --ext .ts",
|
|
46
|
+
"prettier": "prettier-pretty-check \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
47
|
+
"swiftlint": "node-swiftlint",
|
|
48
|
+
"docgen": "docgen --api DeviceIntegrityPlugin --output-readme README.md --output-json dist/docs.json",
|
|
49
|
+
"build": "bun run clean && bun run docgen && tsc && rollup -c rollup.config.mjs",
|
|
50
|
+
"clean": "rimraf ./dist",
|
|
51
|
+
"watch": "tsc --watch",
|
|
52
|
+
"prepublishOnly": "bun run build",
|
|
53
|
+
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@capacitor/android": "^8.0.0",
|
|
57
|
+
"@capacitor/cli": "^8.0.0",
|
|
58
|
+
"@capacitor/core": "^8.0.0",
|
|
59
|
+
"@capacitor/docgen": "^0.3.1",
|
|
60
|
+
"@capacitor/ios": "^8.0.0",
|
|
61
|
+
"@ionic/eslint-config": "^0.4.0",
|
|
62
|
+
"@ionic/prettier-config": "^4.0.0",
|
|
63
|
+
"@ionic/swiftlint-config": "^2.0.0",
|
|
64
|
+
"@types/node": "^24.10.1",
|
|
65
|
+
"eslint": "^8.57.1",
|
|
66
|
+
"eslint-plugin-import": "^2.31.0",
|
|
67
|
+
"husky": "^9.1.7",
|
|
68
|
+
"prettier": "^3.6.2",
|
|
69
|
+
"prettier-pretty-check": "^0.2.0",
|
|
70
|
+
"prettier-plugin-java": "^2.7.7",
|
|
71
|
+
"rimraf": "^6.1.0",
|
|
72
|
+
"rollup": "^4.53.2",
|
|
73
|
+
"swiftlint": "^2.0.0",
|
|
74
|
+
"typescript": "^5.9.3"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@capacitor/core": ">=8.0.0"
|
|
78
|
+
},
|
|
79
|
+
"prettier": "@ionic/prettier-config",
|
|
80
|
+
"swiftlint": "@ionic/swiftlint-config",
|
|
81
|
+
"eslintConfig": {
|
|
82
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
83
|
+
},
|
|
84
|
+
"capacitor": {
|
|
85
|
+
"ios": {
|
|
86
|
+
"src": "ios"
|
|
87
|
+
},
|
|
88
|
+
"android": {
|
|
89
|
+
"src": "android"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|