iOS Cross-Platform Account and Password Integration to Enhance Login Experience
A feature more worthwhile than Sign in with Apple
Photo by Dan Nelson
Features
One of the most common problems in services that have both a website and an app is that users register and log in on the website, with passwords remembered; but when guided to install the app, they find it very inconvenient to re-enter their account and password from scratch. This feature allows the existing account and password on the phone to be automatically filled into the app associated with the website, speeding up the user login process.
Effect Diagram
Without further ado, here is the completed effect diagram; at first glance, you might think it’s the iOS ≥ 11 Password AutoFill feature; but please look carefully, the keyboard did not pop up, and I clicked the “Choose Saved Password” button to bring up the account and password selection window.
Since Password AutoFill is mentioned, let me first introduce Password AutoFill and how to set it up!
Password AutoFill
Support: iOS ≥ 11
By now, iOS 14, this feature is very common and nothing special; on the account and password login page in the app, when the keyboard is called up for input, you can quickly select the account and password of the web version service, and after selection, it will be automatically filled in for quick login!
So how do the app and web recognize each other?
Associated Domains! We specify Associated Domains in the app and upload the apple-app-site-association file on the website, and they can recognize each other.
1. In the project settings “Signing & Capabilities” -> Top left “+ Capabilities” -> “Associated Domains”
Add webcredentials:your website domain
(ex: webcredentials:google.com
).
2. Go to Apple Developer Console
In the “ Membership “ tab, record the “ Team ID “
3. Go to “Certificates, Identifiers & Profiles” -> “Identifiers” -> Find your project -> Enable the “Associated Domains” feature
App-side settings completed!
4. Web Site Configuration
Create a file named “apple-app-site-association” (without an extension), edit it with a text editor, and enter the following content:
1
2
3
4
5
6
7
{
"webcredentials": {
"apps": [
"TeamID.BundleId"
]
}
}
Replace TeamID.BundleId
with your project settings (e.g., TeamID = ABCD
, BundleID = li.zhgchg.demoapp
=> ABCD.li.zhgchg.demoapp
).
Upload this file to the website’s root directory
or /.well-known
directory. Assuming your webcredentials website domain
is set to google.com
, this file should be accessible at google.com/apple-app-site-association
or google.com/.well-known/apple-app-site-association
.
Note: Subdomains
According to the official documentation, if there are subdomains, they must all be listed in the Associated Domains.
Web Configuration Complete!
Note: applinks
It has been observed that if a universal link applinks
has been set, the webcredentials
part is not necessary for it to be effective. However, we will follow the documentation to avoid potential issues in the future.
Back to the Program
For the code part, we only need to set the TextField as follows:
1
2
usernameTextField.textContentType = .username
passwordTextField.textContentType = .password
If it is a new registration, the password confirmation field can use:
1
repeatPasswordTextField.textContentType = .newPassword
After rebuilding and running the app, the option to use saved passwords from the same website will appear above the keyboard when entering the account.
Done!
Not Appearing?
It might be because the autofill password feature is not enabled (it is disabled by default in the simulator). Go to “Settings” -> “Passwords” -> “Autofill Passwords” -> Enable “Autofill Passwords”.
Alternatively, the website might not have any existing passwords. You can add one in “Settings” -> “Passwords” -> Top right corner “+” -> Add.
Getting to the Main Topic
After introducing Password AutoFill, let’s move on to the main topic: how to achieve the effect shown in the illustration.
Shared Web Credentials
Introduced in iOS 8.0, although rarely seen in apps before Password AutoFill was released, this API can integrate website account passwords for quick user selection.
Shared Web Credentials can not only read account passwords but also add, modify, and delete stored account passwords.
Configuration
⚠️ The configuration part must also set up Associated Domains, as mentioned in the Password AutoFill setup.
So it can be said to be an enhanced version of the Password AutoFill feature!!
Because the environment required for Password AutoFill must be set up first to use this “advanced” feature.
Reading
Reading is done using the SecRequestSharedWebCredential
method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SecRequestSharedWebCredential(nil, nil) { (credentials, error) in
guard error == nil else {
DispatchQueue.main.async {
//alert error
}
return
}
guard CFArrayGetCount(credentials) > 0,
let dict = unsafeBitCast(CFArrayGetValueAtIndex(credentials, 0), to: CFDictionary.self) as? Dictionary<String, String>,
let account = dict[kSecAttrAccount as String],
let password = dict[kSecSharedPassword as String] else {
DispatchQueue.main.async {
//alert error
}
return
}
DispatchQueue.main.async {
//fill account,password to textfield
}
}
SecRequestSharedWebCredential(fqdn, account, completionHandler)
- fqdn If there are multiple
webcredentials
domains, you can specify one, or use null to not specify - account Specify a particular account to query, use null to not specify
Effect image. (You may notice it is different from the initial effect image)
⚠️ This method has been marked as Deprecated in iOS 14!
⚠️ This method has been marked as Deprecated in iOS 14!
⚠️ This method has been marked as Deprecated in iOS 14!
"Use ASAuthorizationController to make an ASAuthorizationPasswordRequest (AuthenticationServices framework)"
This method is only applicable for iOS 8 ~ iOS 14. After iOS 13, you can use the same API as Sign in with Apple — AuthenticationServices
AuthenticationServices Reading Method
Support iOS ≥ 13
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import AuthenticationServices
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//...
let request: ASAuthorizationPasswordRequest = ASAuthorizationPasswordProvider().createRequest()
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.performRequests()
//...
}
}
extension ViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let credential = authorization.credential as? ASPasswordCredential {
// fill credential.user, credential.password to textfield
}
// else if as? ASAuthorizationAppleIDCredential... sign in with apple
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// alert error
}
}
Effect image, you can see that the new method integrates better with Sign in with Apple in terms of process and display.
⚠️ This login cannot replace Sign in with Apple (they are different things).
Writing Account and Password to “Passwords”
Only the reading part is deprecated, the parts for adding, deleting, and editing can still be used as usual.
The parts for adding, deleting, and editing use SecAddSharedWebCredential
for operations.
1
2
3
4
5
6
7
8
9
SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { (error) in
DispatchQueue.main.async {
guard error == nil else {
// alert error
return
}
// alert success
}
}
SecAddSharedWebCredential(fqdn, account, password, completionHandler)
- fqdn can freely specify the domain to be stored, it does not necessarily have to be in
webcredentials
- account specifies the account to be added, modified, or deleted
- To delete data, set password to
nil
- Processing logic:
- account exists & password is provided = modify password
- account exists & password is nil = delete account and password from domain
- account does not exist & password is provided = add account and password to domain
⚠️ Additionally, you cannot modify in the background secretly; a prompt will appear each time you modify, asking the user to confirm by clicking “Update Password” to actually change the data.
Password Generator
The last small feature, the password generator.
Use SecCreateSharedWebCredentialPassword()
to operate.
1
let password = SecCreateSharedWebCredentialPassword() as String? ?? ""
The generated password consists of uppercase and lowercase English letters and numbers, using “-“ as a separator (e.g., Jpn-4t2-gaF-dYk).
Complete Test Project Download
Room for Improvement
If you use third-party password management tools (e.g., onepass, lastpass), you might notice that while Password AutoFill on the keyboard supports display & input, it does not show up in AuthenticationServices or SecRequestSharedWebCredential. It’s unclear if this can be achieved.
Conclusion
Thank you for reading, and thanks to saiday and StreetVoice for letting me know about this feature XD.
Also, XCode ≥ 12.5 simulators have added recording and GIF saving features, which are super useful!
Press “Command” + “R” on the simulator to start recording, click the red dot to stop recording; right-click on the preview image that slides out from the bottom right -> “Save as Animated GIF” to save it as a GIF and directly paste it into the article!
For any questions or feedback, feel free to contact me.
===
===
This article was first published in Traditional Chinese on Medium ➡️ View Here