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

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

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

A feature more worthwhile than 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

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 AppleAuthenticationServices

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



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

Comprehensive Guide to Implementing Local Cache with AVPlayer

What's New with Universal Links