@capgo/capacitor-autofill-save-password 8.0.35 → 8.1.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/README.md +23 -8
- package/android/src/main/java/ee/forgr/autofill_password/SavePasswordPlugin.java +1 -1
- package/dist/docs.json +9 -3
- package/dist/esm/definitions.d.ts +14 -1
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/SavePasswordPlugin/SavePasswordPlugin.swift +52 -1
- package/package.json +1 -1
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.
|
|
31
|
+
private final String pluginVersion = "8.1.1";
|
|
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>}
|
|
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>}
|
|
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.
|
|
12
|
+
private let pluginVersion: String = "8.1.1"
|
|
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