Post

iOS Cross-Platform Account Integration|Enhance Login Experience Beyond Sign in with Apple

iOS users struggle with fragmented logins; unify passwords across platforms to simplify access, boost security, and streamline authentication for a seamless experience better than Sign in with Apple.

iOS Cross-Platform Account Integration|Enhance Login Experience Beyond Sign in with Apple

点击这里查看本文章简体中文版本。

點擊這裡查看本文章正體中文版本。

This post was translated with AI assistance — let me know if anything sounds off!


iOS Cross-Platform Account and Password Integration to Enhance Login Experience

Other Features Worth Adding Besides Sign in with Apple

Photo by [Dan Nelson](https://unsplash.com/@danny144?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText){:target="_blank"}

Photo by Dan Nelson

Features

The most common issue in services with both a website and an app is that users who have registered and saved their passwords on the website are forced to re-enter their credentials when opening the app after installation. This feature automatically fills the existing account and password stored on the phone into the app linked to the website, speeding up the login process.

Result Image

No fuss, here is the final effect image; at first glance, you might think it’s the iOS ≥ 11 Password AutoFill feature. But please look closely—the keyboard does not pop up, and I clicked the “Select Saved Password” button to bring up the account and password selection window.

Since Password AutoFill was mentioned, let me tease a bit first by introducing Password AutoFill and how to set it up!

Password AutoFill

Support: iOS ≥ 11

Now that it’s already iOS 14, this feature is very common and nothing special; on the app’s login page for account and password, when the keyboard pops up for input, you can quickly select the website version’s account and password, and after selecting, it will automatically fill in for a fast 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” -> top left “+ Capabilities” -> “Associated Domains”

Add webcredentials:yourwebsite.com (e.g., webcredentials:google.com).

2. Log in to the Apple Developer Console

Record the Team ID in the Membership tab.

3. Go to “Certificates, Identifiers & Profiles” -> “Identifiers” -> find your project -> enable the “Associated Domains” feature

App Setup Complete!

4. Web Website Settings

Create a file named “apple-app-site-association” (no 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 site domain is set to google.com, then the file must be accessible at google.com/apple-app-site-association or google.com/.well-known/apple-app-site-association.

Supplement: Subdomains

Excerpt from the official documentation: if they are subdomains, they must all be listed in Associated Domains.

Web setup completed!

Supplement: applinks

Here, it was found that if a universal link applinks is set, adding the webcredentials part is not necessary for it to work; however, let’s 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 to:

1
2
usernameTextField.textContentType = .username
passwordTextField.textContentType = .password

If you are newly registered, the password confirmation field can use:

1
repeatPasswordTextField.textContentType = .newPassword

At this point, after rebuilding and running the app, saved password options for the same website will appear above the keyboard when entering the account.

Done!

Not appearing?

The autofill password feature might be turned off (it is off by default in the simulator). Please go to “Settings” -> “Passwords” -> “Autofill Passwords” -> turn on “Autofill Passwords”.

Or if the site has no existing password, you can also go to “Settings” -> “Passwords” -> top right corner “+” -> Add new.

Getting Started

After introducing the appetizer, Password AutoFill, let’s move on to the main topic of this article: how to achieve the effect shown in the image.

Shared Web Credentials

Starting from iOS 8.0, although few apps used it before, this API could be used to integrate website usernames and passwords for users to quickly select even before Password AutoFill was introduced.

Shared Web Credentials can not only read usernames and passwords but also add new credentials, modify existing ones, and delete them.

Settings

⚠️ You still need to set up Associated Domains, just like the previously mentioned Password AutoFill settings.

So it can be said that this is an enhanced version of the Password AutoFill feature!!

Because you need to set up the environment required for Password AutoFill before using this “advanced” feature.

Read

Reading 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 specify none.

  • account Specifies the account to query; use null to specify none

Rendered image. (You may notice it differs from the initial rendered image)

⚠️ This reading method has been marked Deprecated since iOS 14!

⚠️ This reading method has been marked Deprecated since iOS 14!

⚠️ This reading method has been marked Deprecated since iOS 14!

"Use ASAuthorizationController to create an ASAuthorizationPasswordRequest (AuthenticationServices framework)"

This method only applies to iOS 8 ~ iOS 14. For iOS 13 and later, you can use the same Sign in with Apple API — 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
    }
}

The mockup shows that the new approach integrates better with Sign in with Apple in both the process and display.

⚠️ This sign-in cannot replace Sign in with Apple (they are two different things).

Write account and password to “Password”

Only the reading part is deprecated; adding, deleting, and editing still work as before.

Additions, deletions, and edits are performed using 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 {
      // show error alert
      return
    }
    // show success alert
  }
}

SecAddSharedWebCredential(fqdn, account, password, completionHandler)

  • fqdn can be freely assigned to any domain to be stored and does not have to be in webcredentials

  • account specifies the account to add, modify, or delete

  • To delete data, set password to nil.

  • Processing logic:

    • account exists & password provided = update password
    • account exists & password is nil = remove account and password from domain
    • account does not exist & password provided = add account and password to domain

⚠️ It also does not allow secret background modifications. Every time a change is made, a prompt will notify the user, and the user must click “Update Password” to confirm the change.

Password Generator

The last small feature, a password generator.

Use SecCreateSharedWebCredentialPassword() to perform the operation.

1
let password = SecCreateSharedWebCredentialPassword() as String? ?? ""

The generated password consists of uppercase and lowercase letters, numbers, and uses “-“ as a separator (e.g., Jpn-4t2-gaF-dYk).

Complete Test Project Download

Minor Drawbacks

If you use third-party password managers (e.g., 1Password, LastPass), you might notice that while the keyboard’s Password AutoFill supports display and input, they do not appear in AuthenticationServices or SecRequestSharedWebCredential. It is unclear if this requirement can be met.

End

Thank you all for reading, and thanks to saiday and StreetVoice for letting me know about this feature XD.

XCode ≥ 12.5 simulators now support screen recording with the handy option to save as GIF!

Press “Command” + “R” on the simulator to start recording. Click the red dot to stop recording; then right-click the preview thumbnail that slides out from the bottom right corner -> “Save as Animated GIF” to save it as a GIF and paste it directly into the article!

If you have any questions or feedback, feel free to contact me.


Buy me a beer

This post was originally published on Medium (View original post), and automatically converted and synced by ZMediumToMarkdown.

Improve this page on Github.

This post is licensed under CC BY 4.0 by the author.