@capgo/capacitor-autofill-save-password 8.0.35 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,9 +9,7 @@ Prompt to display dialog for saving password to keychain from webview app
9
9
  <h2><a href="https://capgo.app/consulting/?ref=plugin_autofill_save_password"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
10
10
  </div>
11
11
 
12
- Fork of original plugin to work with Capacitor 7
13
-
14
- IOS work for old versions and 18.3
12
+ Fork of original plugin to work with Capacitor 7+
15
13
 
16
14
  ## Documentation
17
15
 
@@ -104,6 +102,16 @@ with
104
102
  </string>
105
103
  ```
106
104
 
105
+ ### iOS
106
+
107
+ On iOS 26.2 and later, `promptDialog` saves through [`ASCredentialDataManager`](https://developer.apple.com/documentation/authenticationservices/ascredentialdatamanager), which routes the save to whichever credential provider the user has chosen — iCloud Keychain or a third-party manager. Below 26.2 it falls back to `SecAddSharedWebCredential`, which Apple deprecated in 26.2 and which only ever writes to iCloud Keychain.
108
+
109
+ Either way the `url` option names the domain the credential is saved against, and it must be one of the `webcredentials:` associated domains you set up above.
110
+
111
+ Note the two paths differ in what they report back. `SecAddSharedWebCredential` surfaces a dismissed prompt as an error, so `promptDialog` rejects. `ASCredentialDataManager` only throws when the system rejects the update — Apple describe it as equivalent to submitting a password form, so the user's choice never reaches the app. On 26.2 and later, treat a resolved promise as "the system accepted the request", not as confirmation that the password was saved.
112
+
113
+ On 26.2 and later you can also pass `title` to control the name the credential is filed under. Without it the password manager falls back to the bare domain, so users see `app.example.com` rather than your product name. The old API has no equivalent, so `title` is ignored below 26.2.
114
+
107
115
  ## API
108
116
 
109
117
  <docgen-index>
@@ -126,6 +134,12 @@ promptDialog(options: Options) => Promise<void>
126
134
 
127
135
  Save a password to the keychain.
128
136
 
137
+ On iOS 26.2 and later, resolving means the system accepted the request —
138
+ not that the credential was stored. The save prompt belongs to the system
139
+ and the user's choice is not reported back, so do not treat a resolved
140
+ promise as confirmation. Below 26.2, and on Android, dismissing the prompt
141
+ rejects.
142
+
129
143
  | Param | Type | Description |
130
144
  | ------------- | ------------------------------------------- | ------------------------------- |
131
145
  | **`options`** | <code><a href="#options">Options</a></code> | - The options for the password. |
@@ -166,11 +180,12 @@ Get the native Capacitor plugin version.
166
180
 
167
181
  #### Options
168
182
 
169
- | Prop | Type | Description |
170
- | -------------- | ------------------- | ------------------------------------------------------------------------------ |
171
- | **`username`** | <code>string</code> | The username to save. |
172
- | **`password`** | <code>string</code> | The password to save. |
173
- | **`url`** | <code>string</code> | The url to save the password for. (For example: "console.capgo.app") iOS only. |
183
+ | Prop | Type | Description |
184
+ | -------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
185
+ | **`username`** | <code>string</code> | The username to save. |
186
+ | **`password`** | <code>string</code> | The password to save. |
187
+ | **`url`** | <code>string</code> | The url to save the password for. (For example: "console.capgo.app") iOS only. |
188
+ | **`title`** | <code>string</code> | The name the credential is filed under in the password manager. (For example: "Capgo"). Defaults to the domain from `url`. iOS 26.2 and later only. |
174
189
 
175
190
 
176
191
  #### ReadPasswordResult
@@ -28,7 +28,7 @@ import java.util.Map;
28
28
  @CapacitorPlugin(name = "SavePassword")
29
29
  public class SavePasswordPlugin extends Plugin {
30
30
 
31
- private final String pluginVersion = "8.0.35";
31
+ private final String pluginVersion = "8.1.0";
32
32
  private static final String TAG = "CredentialManager";
33
33
  private CredentialManager credentialManager;
34
34
  private Map<String, PendingGetCredentialRequest> pendingRequestsByElementId = new HashMap<>();
package/dist/docs.json CHANGED
@@ -31,15 +31,14 @@
31
31
  "text": "options - The options for the password."
32
32
  },
33
33
  {
34
- "name": "returns",
35
- "text": "Success status"
34
+ "name": "returns"
36
35
  },
37
36
  {
38
37
  "name": "example",
39
38
  "text": "await SavePassword.promptDialog({\n username: 'your-username',\n password: 'your-password'\n});"
40
39
  }
41
40
  ],
42
- "docs": "Save a password to the keychain.",
41
+ "docs": "Save a password to the keychain.\n\nOn iOS 26.2 and later, resolving means the system accepted the request —\nnot that the credential was stored. The save prompt belongs to the system\nand the user's choice is not reported back, so do not treat a resolved\npromise as confirmation. Below 26.2, and on Android, dismissing the prompt\nrejects.",
43
42
  "complexTypes": [
44
43
  "Options"
45
44
  ],
@@ -121,6 +120,13 @@
121
120
  "docs": "The url to save the password for. (For example: \"console.capgo.app\")\niOS only.",
122
121
  "complexTypes": [],
123
122
  "type": "string | undefined"
123
+ },
124
+ {
125
+ "name": "title",
126
+ "tags": [],
127
+ "docs": "The name the credential is filed under in the password manager.\n(For example: \"Capgo\"). Defaults to the domain from `url`.\niOS 26.2 and later only.",
128
+ "complexTypes": [],
129
+ "type": "string | undefined"
124
130
  }
125
131
  ]
126
132
  },
@@ -16,6 +16,12 @@ export interface Options {
16
16
  * iOS only.
17
17
  */
18
18
  url?: string;
19
+ /**
20
+ * The name the credential is filed under in the password manager.
21
+ * (For example: "Capgo"). Defaults to the domain from `url`.
22
+ * iOS 26.2 and later only.
23
+ */
24
+ title?: string;
19
25
  }
20
26
  export interface ReadPasswordResult {
21
27
  /**
@@ -34,8 +40,15 @@ export interface ReadPasswordResult {
34
40
  export interface SavePasswordPlugin {
35
41
  /**
36
42
  * Save a password to the keychain.
43
+ *
44
+ * On iOS 26.2 and later, resolving means the system accepted the request —
45
+ * not that the credential was stored. The save prompt belongs to the system
46
+ * and the user's choice is not reported back, so do not treat a resolved
47
+ * promise as confirmation. Below 26.2, and on Android, dismissing the prompt
48
+ * rejects.
49
+ *
37
50
  * @param {Options} options - The options for the password.
38
- * @returns {Promise<void>} Success status
51
+ * @returns {Promise<void>}
39
52
  * @example
40
53
  * await SavePassword.promptDialog({
41
54
  * username: 'your-username',
@@ -1 +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 * The url to save the password for. (For example: \"console.capgo.app\")\n * iOS only.\n */\n url?: string;\n}\n\nexport interface ReadPasswordResult {\n /**\n * The username of the password.\n */\n username: string;\n /**\n * The password of the password.\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 /**\n * Read a password from the keychain. Requires the developer to setup associated domain for the app for iOS.\n * @returns {Promise<ReadPasswordResult>} The retrieved password credentials\n */\n readPassword(): Promise<ReadPasswordResult>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 1.0.0\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
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 * The url to save the password for. (For example: \"console.capgo.app\")\n * iOS only.\n */\n url?: string;\n /**\n * The name the credential is filed under in the password manager.\n * (For example: \"Capgo\"). Defaults to the domain from `url`.\n * iOS 26.2 and later only.\n */\n title?: string;\n}\n\nexport interface ReadPasswordResult {\n /**\n * The username of the password.\n */\n username: string;\n /**\n * The password of the password.\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 *\n * On iOS 26.2 and later, resolving means the system accepted the request —\n * not that the credential was stored. The save prompt belongs to the system\n * and the user's choice is not reported back, so do not treat a resolved\n * promise as confirmation. Below 26.2, and on Android, dismissing the prompt\n * rejects.\n *\n * @param {Options} options - The options for the password.\n * @returns {Promise<void>}\n * @example\n * await SavePassword.promptDialog({\n * username: 'your-username',\n * password: 'your-password'\n * });\n */\n promptDialog(options: Options): Promise<void>;\n\n /**\n * Read a password from the keychain. Requires the developer to setup associated domain for the app for iOS.\n * @returns {Promise<ReadPasswordResult>} The retrieved password credentials\n */\n readPassword(): Promise<ReadPasswordResult>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 1.0.0\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
@@ -9,7 +9,7 @@ import AuthenticationServices
9
9
  */
10
10
  @objc(SavePasswordPlugin)
11
11
  public class SavePasswordPlugin: CAPPlugin, CAPBridgedPlugin, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
12
- private let pluginVersion: String = "8.0.35"
12
+ private let pluginVersion: String = "8.1.0"
13
13
  public let identifier = "SavePasswordPlugin"
14
14
 
15
15
  public let jsName = "SavePassword"
@@ -29,6 +29,57 @@ public class SavePasswordPlugin: CAPPlugin, CAPBridgedPlugin, ASAuthorizationCon
29
29
  call.reject("URL is required for iOS shared web credentials")
30
30
  return
31
31
  }
32
+
33
+ if #available(iOS 26.2, *) {
34
+ saveWithCredentialDataManager(
35
+ call,
36
+ username: username,
37
+ password: password,
38
+ url: url,
39
+ title: call.getString("title")
40
+ )
41
+ return
42
+ }
43
+
44
+ saveWithSharedWebCredential(call, username: username, password: password, url: url)
45
+ }
46
+
47
+ @available(iOS 26.2, *)
48
+ private func saveWithCredentialDataManager(
49
+ _ call: CAPPluginCall, username: String, password: String, url: String, title: String?
50
+ ) {
51
+ Task { @MainActor in
52
+ guard let anchor = self.bridge?.viewController?.view.window else {
53
+ call.reject("Failed to save credential", "No window to present the save prompt from")
54
+ return
55
+ }
56
+ do {
57
+ try await ASCredentialDataManager().save(
58
+ password: ASPasswordCredential(user: username, password: password),
59
+ for: Self.autoFillScope(for: url),
60
+ title: title,
61
+ anchor: anchor
62
+ )
63
+ call.resolve()
64
+ } catch {
65
+ call.reject("Failed to save credential", error.localizedDescription)
66
+ }
67
+ }
68
+ }
69
+
70
+ @available(iOS 26.2, *)
71
+ private static func autoFillScope(for url: String) -> ASAutoFillURLScope {
72
+ // `url` is documented as a bare FQDN, but tolerate a full URL rather than
73
+ // handing "https://example.com" to the host initialiser verbatim
74
+ guard let parsed = URL(string: url), parsed.scheme != nil,
75
+ let scope = ASAutoFillURLScope(url: parsed) else {
76
+ return ASAutoFillURLScope(host: url)
77
+ }
78
+ return scope
79
+ }
80
+
81
+ @available(iOS, deprecated: 26.2, message: "Superseded by ASCredentialDataManager")
82
+ private func saveWithSharedWebCredential(_ call: CAPPluginCall, username: String, password: String, url: String) {
32
83
  let fqdn = url as CFString
33
84
  let user = username as CFString
34
85
  let pass = password as CFString
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-autofill-save-password",
3
- "version": "8.0.35",
3
+ "version": "8.1.0",
4
4
  "description": "Prompt to display dialog for saving password to keychain from webview app",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",