@capgo/capacitor-autofill-save-password 6.0.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.
@@ -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 = 'CapgoCapacitorAutofillSavePassword'
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/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '13.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapgoCapacitorAutofillSavePassword",
6
+ platforms: [.iOS(.v13)],
7
+ products: [
8
+ .library(
9
+ name: "CapgoCapacitorAutofillSavePassword",
10
+ targets: ["SavePasswordPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", branch: "main")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "SavePasswordPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/SavePasswordPlugin"),
23
+ .testTarget(
24
+ name: "SavePasswordPluginTests",
25
+ dependencies: ["SavePasswordPlugin"],
26
+ path: "ios/Tests/SavePasswordPluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # @capgo/capacitor-autofill-save-password
2
+
3
+ Prompt to display dialog for saving password to keychain from webview app
4
+
5
+ <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a>
6
+
7
+ <div align="center">
8
+ <h2><a href="https://capgo.app/?ref=plugin"> ➡️ Get Instant updates for your App with Capgo 🚀</a></h2>
9
+ <h2><a href="https://capgo.app/consulting/?ref=plugin"> Fix your annoying bug now, Hire a Capacitor expert 💪</a></h2>
10
+ </div>
11
+
12
+ Fork of original plugin to work with Capacitor 7
13
+
14
+ IOS work for old versions and 18.3
15
+
16
+ Android still WIP
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install @capgo/capacitor-autofill-save-password
22
+ npx cap sync
23
+ ```
24
+
25
+ ## Prerequisite
26
+ You must set up your app’s associated domains. To learn how to set up your app’s associated domains, see [Supporting Associated Domains](https://developer.apple.com/documentation/safariservices/supporting_associated_domains) in Apple Developer document.
27
+
28
+ Then add in your `App.entitlements`
29
+
30
+ ```xml
31
+ <key>com.apple.developer.associated-domains</key>
32
+ <array>
33
+ <string>webcredentials:YOURDOMAIN</string>
34
+ </array>
35
+ ```
36
+
37
+ To associate your domain to your app.
38
+
39
+ ## How to use
40
+ ```ts
41
+ import { Capacitor } from '@capacitor/core';
42
+ import { SavePassword } from '@capgo/capacitor-autofill-save-password';
43
+
44
+ login(username: string, password: string) {
45
+ // your login logic here
46
+
47
+ SavePassword.promptDialog({
48
+ username: username,
49
+ password: password,
50
+ })
51
+ .then(() => console.log('promptDialog success'))
52
+ .catch((err) => console.error('promptDialog failure', err));
53
+ }
54
+ ```
55
+
56
+
57
+ ### Android
58
+
59
+ Add `apply plugin: 'com.google.gms.google-services'` beneath `apply plugin: 'com.android.application'` in `android/app/build.gradle`
60
+
61
+ this will allow the plugin to import the proper lib.
62
+
63
+ Then you need to make sure you did set properly your domain and did add google-services.json.
64
+
65
+ guide here https://developer.android.com/identity/sign-in/credential-manager
66
+
67
+ You need to have the file at this path `android/google-services.json` set, if you dont use firebase add empty json
68
+
69
+ then add your domain in `example-app/android/app/src/main/res/values/strings.xml`
70
+
71
+ with
72
+ ```xml
73
+ <string name="asset_statements" translatable="false">
74
+ [{
75
+ \"include\": \"https://YOURDOMAIN/.well-known/assetlinks.json\"
76
+ }]
77
+ </string>
78
+ ```
79
+
80
+ ## API
81
+
82
+ <docgen-index>
83
+
84
+ * [`promptDialog(...)`](#promptdialog)
85
+ * [Interfaces](#interfaces)
86
+
87
+ </docgen-index>
88
+
89
+ <docgen-api>
90
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
91
+
92
+ ### promptDialog(...)
93
+
94
+ ```typescript
95
+ promptDialog(options: Options) => Promise<void>
96
+ ```
97
+
98
+ Save a password to the keychain.
99
+
100
+ | Param | Type | Description |
101
+ | ------------- | ------------------------------------------- | ------------------------------- |
102
+ | **`options`** | <code><a href="#options">Options</a></code> | - The options for the password. |
103
+
104
+ --------------------
105
+
106
+
107
+ ### Interfaces
108
+
109
+
110
+ #### Options
111
+
112
+ | Prop | Type | Description |
113
+ | -------------- | ------------------- | --------------------- |
114
+ | **`username`** | <code>string</code> | The username to save. |
115
+ | **`password`** | <code>string</code> | The password to save. |
116
+
117
+ </docgen-api>
@@ -0,0 +1,59 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.2.1'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace "ee.forgr.autofill_password"
22
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
26
+ versionCode 1
27
+ versionName "1.0"
28
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_17
41
+ targetCompatibility JavaVersion.VERSION_17
42
+ }
43
+ }
44
+
45
+ repositories {
46
+ google()
47
+ mavenCentral()
48
+ }
49
+
50
+ dependencies {
51
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
52
+ implementation project(':capacitor-android')
53
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
54
+ implementation "androidx.credentials:credentials:1.5.0"
55
+ implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
56
+ testImplementation "junit:junit:$junitVersion"
57
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
58
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
59
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,111 @@
1
+ package ee.forgr.autofill_password;
2
+
3
+ import android.app.Activity;
4
+ import android.os.Build;
5
+ import android.util.Log;
6
+
7
+ import androidx.credentials.CredentialManager;
8
+ import androidx.credentials.CreatePasswordRequest;
9
+ import androidx.credentials.CreateCredentialResponse;
10
+ import androidx.credentials.PendingGetCredentialRequest;
11
+ import androidx.credentials.CredentialManagerCallback;
12
+ import androidx.credentials.exceptions.CreateCredentialException;
13
+
14
+ import com.getcapacitor.Plugin;
15
+ import com.getcapacitor.PluginCall;
16
+ import com.getcapacitor.PluginMethod;
17
+ import com.getcapacitor.annotation.CapacitorPlugin;
18
+
19
+ import java.util.HashMap;
20
+ import java.util.Map;
21
+
22
+ import androidx.core.content.ContextCompat;
23
+
24
+ @CapacitorPlugin(name = "SavePassword")
25
+ public class SavePasswordPlugin extends Plugin {
26
+ private static final String TAG = "CredentialManager";
27
+ private CredentialManager credentialManager;
28
+ private Map<String, PendingGetCredentialRequest> pendingRequestsByElementId = new HashMap<>();
29
+
30
+ @Override
31
+ public void load() {
32
+ super.load();
33
+ try {
34
+ credentialManager = CredentialManager.create(getContext());
35
+ } catch (Exception e) {
36
+ Log.e(TAG, "Error initializing CredentialManager", e);
37
+ }
38
+ }
39
+
40
+ @PluginMethod
41
+ public void promptDialog(final PluginCall call) {
42
+ if (!isCredentialManagerAvailable(call)) {
43
+ return;
44
+ }
45
+
46
+ String username = call.getString("username");
47
+ String password = call.getString("password");
48
+
49
+ if (username == null || username.isEmpty()) {
50
+ call.reject("Username is required");
51
+ return;
52
+ }
53
+
54
+ if (password == null || password.isEmpty()) {
55
+ call.reject("Password is required");
56
+ return;
57
+ }
58
+
59
+ try {
60
+ // Build request directly with username & password (API 1.5.0 signature)
61
+ CreatePasswordRequest request = new CreatePasswordRequest(username, password);
62
+
63
+ // Execute on main thread
64
+ bridge.executeOnMainThread(() -> {
65
+ Activity activity = getActivity();
66
+ if (activity == null) {
67
+ call.reject("Activity not available");
68
+ return;
69
+ }
70
+
71
+ try {
72
+ credentialManager.createCredentialAsync(
73
+ activity,
74
+ request,
75
+ null,
76
+ ContextCompat.getMainExecutor(getContext()),
77
+ new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
78
+ @Override
79
+ public void onResult(CreateCredentialResponse response) {
80
+ call.resolve();
81
+ }
82
+
83
+ @Override
84
+ public void onError(CreateCredentialException e) {
85
+ call.reject("Error saving credential: " + e.getMessage(), e);
86
+ }
87
+ }
88
+ );
89
+ } catch (Exception e) {
90
+ call.reject("Error saving credential", e);
91
+ }
92
+ });
93
+ } catch (Exception e) {
94
+ call.reject("Error building save credential request", e);
95
+ }
96
+ }
97
+
98
+ private boolean isCredentialManagerAvailable(PluginCall call) {
99
+ if (credentialManager == null) {
100
+ call.reject("Credential Manager not available on this device");
101
+ return false;
102
+ }
103
+
104
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
105
+ call.reject("Credential Manager requires Android API level 28 or higher");
106
+ return false;
107
+ }
108
+
109
+ return true;
110
+ }
111
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "api": {
3
+ "name": "SavePasswordPlugin",
4
+ "slug": "savepasswordplugin",
5
+ "docs": "",
6
+ "tags": [
7
+ {
8
+ "text": "SavePasswordPlugin",
9
+ "name": "interface"
10
+ },
11
+ {
12
+ "text": "Capacitor plugin for saving passwords to the keychain.",
13
+ "name": "description"
14
+ }
15
+ ],
16
+ "methods": [
17
+ {
18
+ "name": "promptDialog",
19
+ "signature": "(options: Options) => Promise<void>",
20
+ "parameters": [
21
+ {
22
+ "name": "options",
23
+ "docs": "- The options for the password.",
24
+ "type": "Options"
25
+ }
26
+ ],
27
+ "returns": "Promise<void>",
28
+ "tags": [
29
+ {
30
+ "name": "param",
31
+ "text": "options - The options for the password."
32
+ },
33
+ {
34
+ "name": "returns",
35
+ "text": "Success status"
36
+ },
37
+ {
38
+ "name": "example",
39
+ "text": "await SavePassword.promptDialog({\n username: 'your-username',\n password: 'your-password'\n});"
40
+ }
41
+ ],
42
+ "docs": "Save a password to the keychain.",
43
+ "complexTypes": [
44
+ "Options"
45
+ ],
46
+ "slug": "promptdialog"
47
+ }
48
+ ],
49
+ "properties": []
50
+ },
51
+ "interfaces": [
52
+ {
53
+ "name": "Options",
54
+ "slug": "options",
55
+ "docs": "",
56
+ "tags": [
57
+ {
58
+ "text": "Options",
59
+ "name": "interface"
60
+ },
61
+ {
62
+ "text": "The options for the prompt.",
63
+ "name": "description"
64
+ }
65
+ ],
66
+ "methods": [],
67
+ "properties": [
68
+ {
69
+ "name": "username",
70
+ "tags": [],
71
+ "docs": "The username to save.",
72
+ "complexTypes": [],
73
+ "type": "string"
74
+ },
75
+ {
76
+ "name": "password",
77
+ "tags": [],
78
+ "docs": "The password to save.",
79
+ "complexTypes": [],
80
+ "type": "string"
81
+ }
82
+ ]
83
+ }
84
+ ],
85
+ "enums": [],
86
+ "typeAliases": [],
87
+ "pluginConfigs": []
88
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @interface Options
3
+ * @description The options for the prompt.
4
+ */
5
+ export interface Options {
6
+ /**
7
+ * The username to save.
8
+ */
9
+ username: string;
10
+ /**
11
+ * The password to save.
12
+ */
13
+ password: string;
14
+ }
15
+ /**
16
+ * @interface SavePasswordPlugin
17
+ * @description Capacitor plugin for saving passwords to the keychain.
18
+ */
19
+ export interface SavePasswordPlugin {
20
+ /**
21
+ * Save a password to the keychain.
22
+ * @param {Options} options - The options for the password.
23
+ * @returns {Promise<void>} Success status
24
+ * @example
25
+ * await SavePassword.promptDialog({
26
+ * username: 'your-username',
27
+ * password: 'your-password'
28
+ * });
29
+ */
30
+ promptDialog(options: Options): Promise<void>;
31
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @interface Options\n * @description The options for the prompt.\n */\nexport interface Options {\n /**\n * The username to save.\n */\n username: string;\n /**\n * The password to save.\n */\n password: string;\n}\n\n/**\n * @interface SavePasswordPlugin\n * @description Capacitor plugin for saving passwords to the keychain.\n */\nexport interface SavePasswordPlugin {\n /**\n * Save a password to the keychain.\n * @param {Options} options - The options for the password.\n * @returns {Promise<void>} Success status\n * @example\n * await SavePassword.promptDialog({\n * username: 'your-username',\n * password: 'your-password'\n * });\n */\n promptDialog(options: Options): Promise<void>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { SavePasswordPlugin } from './definitions';
2
+ declare const SavePassword: SavePasswordPlugin;
3
+ export * from './definitions';
4
+ export { SavePassword };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const SavePassword = registerPlugin('SavePassword', {
3
+ web: () => import('./web').then((m) => new m.SavePasswordWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { SavePassword };
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,YAAY,GAAG,cAAc,CAAqB,cAAc,EAAE;IACtE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;CAChE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { SavePasswordPlugin } from './definitions';\n\nconst SavePassword = registerPlugin<SavePasswordPlugin>('SavePassword', {\n web: () => import('./web').then((m) => new m.SavePasswordWeb()),\n});\n\nexport * from './definitions';\nexport { SavePassword };\n"]}
@@ -0,0 +1,5 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { Options, SavePasswordPlugin } from './definitions';
3
+ export declare class SavePasswordWeb extends WebPlugin implements SavePasswordPlugin {
4
+ promptDialog(options: Options): Promise<void>;
5
+ }
@@ -0,0 +1,7 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class SavePasswordWeb extends WebPlugin {
3
+ async promptDialog(options) {
4
+ throw new Error("Not implemented on web" + JSON.stringify(options));
5
+ }
6
+ }
7
+ //# 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;AAI5C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC5C,KAAK,CAAC,YAAY,CAAC,OAAgB;QACjC,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { Options, SavePasswordPlugin } from './definitions';\n\nexport class SavePasswordWeb extends WebPlugin implements SavePasswordPlugin {\n async promptDialog(options: Options): Promise<void> {\n throw new Error(\"Not implemented on web\" + JSON.stringify(options));\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const SavePassword = core.registerPlugin('SavePassword', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.SavePasswordWeb()),
7
+ });
8
+
9
+ class SavePasswordWeb extends core.WebPlugin {
10
+ async promptDialog(options) {
11
+ throw new Error("Not implemented on web" + JSON.stringify(options));
12
+ }
13
+ }
14
+
15
+ var web = /*#__PURE__*/Object.freeze({
16
+ __proto__: null,
17
+ SavePasswordWeb: SavePasswordWeb
18
+ });
19
+
20
+ exports.SavePassword = SavePassword;
21
+ //# 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 SavePassword = registerPlugin('SavePassword', {\n web: () => import('./web').then((m) => new m.SavePasswordWeb()),\n});\nexport * from './definitions';\nexport { SavePassword };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SavePasswordWeb extends WebPlugin {\n async promptDialog(options) {\n throw new Error(\"Not implemented on web\" + JSON.stringify(options));\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,YAAY,GAAGA,mBAAc,CAAC,cAAc,EAAE;AACpD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;AACnE,CAAC;;ACFM,MAAM,eAAe,SAASC,cAAS,CAAC;AAC/C,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;AAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AAC3E;AACA;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,24 @@
1
+ var capacitorSavePassword = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const SavePassword = core.registerPlugin('SavePassword', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.SavePasswordWeb()),
6
+ });
7
+
8
+ class SavePasswordWeb extends core.WebPlugin {
9
+ async promptDialog(options) {
10
+ throw new Error("Not implemented on web" + JSON.stringify(options));
11
+ }
12
+ }
13
+
14
+ var web = /*#__PURE__*/Object.freeze({
15
+ __proto__: null,
16
+ SavePasswordWeb: SavePasswordWeb
17
+ });
18
+
19
+ exports.SavePassword = SavePassword;
20
+
21
+ return exports;
22
+
23
+ })({}, capacitorExports);
24
+ //# 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 SavePassword = registerPlugin('SavePassword', {\n web: () => import('./web').then((m) => new m.SavePasswordWeb()),\n});\nexport * from './definitions';\nexport { SavePassword };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SavePasswordWeb extends WebPlugin {\n async promptDialog(options) {\n throw new Error(\"Not implemented on web\" + JSON.stringify(options));\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,YAAY,GAAGA,mBAAc,CAAC,cAAc,EAAE;IACpD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACnE,CAAC;;ICFM,MAAM,eAAe,SAASC,cAAS,CAAC;IAC/C,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;IAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3E;IACA;;;;;;;;;;;;;;;"}
@@ -0,0 +1,62 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(SavePasswordPlugin)
9
+ public class SavePasswordPlugin: CAPPlugin, CAPBridgedPlugin {
10
+ public let identifier = "SavePasswordPlugin"
11
+
12
+ public let jsName = "SavePassword"
13
+ public let pluginMethods: [CAPPluginMethod] = [
14
+ CAPPluginMethod(name: "promptDialog", returnType: CAPPluginReturnPromise)
15
+ ]
16
+
17
+ @objc func promptDialog(_ call: CAPPluginCall) {
18
+ DispatchQueue.main.async {
19
+ let loginScreen = LoginScreenViewController()
20
+ loginScreen.usernameTextField.text = call.getString("username") ?? ""
21
+ loginScreen.passwordTextField.text = call.getString("password") ?? ""
22
+ self.bridge?.webView?.addSubview(loginScreen.view)
23
+
24
+ // Defer removal so the system registers the fields before they disappear
25
+ DispatchQueue.main.async {
26
+ loginScreen.view.removeFromSuperview()
27
+ // Clear fields *after* removal as required by Autofill heuristics
28
+ loginScreen.usernameTextField.text = ""
29
+ loginScreen.passwordTextField.text = ""
30
+ call.resolve()
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ class LoginScreenViewController: UIViewController {
37
+ let usernameTextField: UITextField = {
38
+ let textField = UITextField()
39
+ textField.frame.size.width = 1
40
+ textField.frame.size.height = 1
41
+ textField.textContentType = .username
42
+ return textField
43
+ }()
44
+
45
+ let passwordTextField: UITextField = {
46
+ let textField = UITextField()
47
+ textField.frame.size.width = 1
48
+ textField.frame.size.height = 1
49
+ textField.textContentType = .newPassword
50
+ // Fix for ios 18.3 : from https://stackoverflow.com/questions/76773166/password-autofill-wkwebview-doesnt-present-save-password-alert#comment140186929_76773167
51
+ return textField
52
+ }()
53
+
54
+ override func viewDidLoad() {
55
+ super.viewDidLoad()
56
+ view.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
57
+ view.addSubview(usernameTextField)
58
+ view.addSubview(passwordTextField)
59
+ // Make password the first responder so the strong-password prompt or save-password alert triggers reliably
60
+ passwordTextField.becomeFirstResponder()
61
+ }
62
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import SavePasswordPlugin
3
+
4
+ class SavePasswordTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = SavePassword()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@capgo/capacitor-autofill-save-password",
3
+ "version": "6.0.0",
4
+ "description": "Prompt to display dialog for saving password to keychain from webview app",
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
+ "CapgoCapacitorAutofillSavePassword.podspec"
17
+ ],
18
+ "author": "Martin Donadieu <martin@capgo.app>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Cap-go/capacitor-autofill-save-password.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/Cap-go/capacitor-autofill-save-password/issues"
26
+ },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native",
31
+ "autofill",
32
+ "save",
33
+ "password",
34
+ "ios",
35
+ "android"
36
+ ],
37
+ "scripts": {
38
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
39
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorAutofillSavePassword -destination generic/platform=iOS",
40
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
41
+ "verify:web": "npm run build",
42
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
43
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
44
+ "eslint": "eslint . --ext ts",
45
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
46
+ "swiftlint": "node-swiftlint",
47
+ "docgen": "docgen --api SavePasswordPlugin --output-readme README.md --output-json dist/docs.json",
48
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
49
+ "clean": "rimraf ./dist",
50
+ "watch": "tsc --watch",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "devDependencies": {
54
+ "@capacitor/android": "^6.0.0",
55
+ "@capacitor/core": "^6.0.0",
56
+ "@capacitor/docgen": "^0.3.0",
57
+ "@capacitor/ios": "^6.0.0",
58
+ "@ionic/eslint-config": "^0.4.0",
59
+ "@ionic/prettier-config": "^4.0.0",
60
+ "@ionic/swiftlint-config": "^2.0.0",
61
+ "eslint": "^8.57.0",
62
+ "prettier": "^3.4.2",
63
+ "prettier-plugin-java": "^2.6.6",
64
+ "rimraf": "^6.0.1",
65
+ "rollup": "^4.30.1",
66
+ "swiftlint": "^2.0.0",
67
+ "typescript": "~4.1.5"
68
+ },
69
+ "peerDependencies": {
70
+ "@capacitor/core": ">=6.0.0"
71
+ },
72
+ "prettier": "@ionic/prettier-config",
73
+ "swiftlint": "@ionic/swiftlint-config",
74
+ "eslintConfig": {
75
+ "extends": "@ionic/eslint-config/recommended"
76
+ },
77
+ "capacitor": {
78
+ "ios": {
79
+ "src": "ios"
80
+ },
81
+ "android": {
82
+ "src": "android"
83
+ }
84
+ }
85
+ }