Enhancing Login Experience with Cross-Platform Account and Password Integration on iOS
A feature worth adding beyond Sign in with Apple
ℹ️ℹ️ℹ️ The following content is translated by OpenAI.
Click here to view the original Chinese version. | 點此查看本文中文版
Enhancing Login Experience with Cross-Platform Account and Password Integration on iOS
Besides Sign in with Apple, there are other features worth adding.
Photo by Dan Nelson
Features
A common issue encountered in services that have both a website and an app is that users may have registered and saved their passwords on the website. However, when prompted to install the app and log in, it can be very inconvenient to re-enter their username and password from scratch. This feature allows the app to automatically retrieve the existing account credentials stored on the device, speeding up the login process for users.
Effect Illustration
No fluff, here’s the completed effect illustration; at first glance, you might think it’s the iOS ≥ 11 Password AutoFill feature. However, take a closer look—the keyboard does not pop up, and I clicked the “Select Saved Password” button to bring up the account password selection window.
Since we mentioned Password AutoFill, let me tease you a bit and introduce Password AutoFill and how to set it up!
Password AutoFill
Support: iOS ≥ 11
Now that we are on iOS 14, this feature is quite common and not particularly special; on the login page of the app, when the keyboard is called up for input, users can quickly select the account credentials from the website version, and after selection, they can be automatically filled in for a 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, allowing both sides to recognize each other.
1. In the project settings under “Signing & Capabilities” -> Click the “+” in the top left -> “Associated Domains”
Add webcredentials:yourwebsite.com
(e.g., webcredentials:google.com
).
2. Go to the Apple Developer Account
Record the “Team ID” in the “Membership” tab.
3. Go to “Certificates, Identifiers & Profiles” -> “Identifiers” -> Find your project -> Enable the “Associated Domains” feature
APP side setup completed!
4. Web side setup
Create a file named “apple-app-site-association” (no file 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. If your webcredentials website domain
is set to google.com
, then 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 side setup completed!
Note: applinks
I’ve noticed that if you have set up universal links with applinks
, you actually don’t need to add the webcredentials
part for it to work; but let’s stick to the documentation to avoid any future issues.
Back to the Code
For the code part, we just need to set the TextField as follows:
1
2
usernameTextField.textContentType = .username
passwordTextField.textContentType = .password
If it’s a new registration, the password confirmation field can use:
1
repeatPasswordTextField.textContentType = .newPassword
After rebuilding and running the app, when entering the username, options for saved passwords from the same website will appear above the keyboard.
Done!
Not showing up?
It might be because the AutoFill Password feature is turned off (the simulator is off by default). Please go to “Settings” -> “Passwords” -> “AutoFill Passwords” -> turn on “AutoFill Passwords”.
Or it could be that there are no existing passwords for that website. You can also add one by going to “Settings” -> “Passwords” -> tap the “+” in the top right -> add a new one.
Getting to the Main Topic
Now that we’ve covered the appetizer of Password AutoFill, let’s get into the main topic; how to achieve the effect shown in the illustration.
Shared Web Credentials
Introduced in iOS 8.0, this feature was rarely seen in apps before. Even before Password AutoFill was released, this API could be used to integrate website account credentials for quick selection by users.
Shared Web Credentials can not only read account credentials but also add, modify, and delete existing account credentials.
Setup
⚠️ The setup part must also have Associated Domains configured, just like the Password AutoFill setup mentioned earlier.
So you could say this is an enhanced version of the Password AutoFill feature!!
Because you still need to set up the environment required for Password AutoFill 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 the account you want to query, use null to not specify.
Effect illustration. (You may have noticed it’s different from the initial effect illustration.)
⚠️ This reading method has been marked as Deprecated in iOS 14!
⚠️ This reading method has been marked as Deprecated in iOS 14!
⚠️ This reading method has been marked as Deprecated in iOS 14!
"Use ASAuthorizationController to make an ASAuthorizationPasswordRequest (AuthenticationServices framework)"
This method is only applicable from iOS 8 to 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 illustration, showing how the new approach integrates better with the flow and display compared to Sign in with Apple.
⚠️ This login cannot replace Sign in with Apple (the two are different things).
Writing Account Credentials to “Passwords”
Only the reading part has been Deprecated; the adding, deleting, and editing parts are still functional.
For adding, deleting, and editing, use SecAddSharedWebCredential
:
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 You can specify any domain to store in, it doesn’t have to be in
webcredentials
. - account Specify the account to add, modify, or delete.
- To delete data, pass
nil
for password. - Logic handling:
- account exists & password provided = modify password
- account exists & password passed as nil = delete account, password from domain
- account does not exist & password provided = add account, password to domain.
⚠️ Additionally, this does not allow you to modify in the background; each modification will prompt a dialog to notify the user, and the user must press “Update Password” for the data to be modified.
Password Generator
The last small feature is the password generator.
Use SecCreateSharedWebCredentialPassword()
to operate.
1
let password = SecCreateSharedWebCredentialPassword() as String? ?? ""
The generated password consists of uppercase and lowercase letters and numbers, using “-“ as a separator (e.g., Jpn-4t2-gaF-dYk).
Complete Test Project Download
Minor Drawback
If you are using third-party password management tools (e.g., 1Password, LastPass), you may notice that while the keyboard’s Password AutoFill can display and input, it does not show up in AuthenticationServices or SecRequestSharedWebCredential; it’s uncertain if this requirement can be met.
Conclusion
Thank you all for reading, and thanks to saiday and Street Voice for letting me know about this feature XD.
Also, the new recording feature in XCode ≥ 12.5 simulator, which supports saving as GIF, is incredibly useful!
In the simulator, press “Command” + “R” to start recording, click the red dot to stop recording; then right-click on the preview image that slides out in the bottom right -> “Save as Animated GIF” to save it as a GIF and paste it directly into the article!
If you have any questions or suggestions, feel free to contact me.
This article was first published on Medium ➡️ Click Here
Automatically converted and synchronized using ZMediumToMarkdown and Medium-to-jekyll-starter.