Post

iOS 跨平台帐号密码整合|提升 APP 与网站登入体验,快速自动填入密码

解决 APP 与网站帐号密码分离问题,透过 iOS Associated Domains 与 Shared Web Credentials 技术,自动带入已存密码,减少重复输入痛点,提升用户登入速度与便利性,强化跨平台使用者体验。

iOS 跨平台帐号密码整合|提升 APP 与网站登入体验,快速自动填入密码

Click here to view the English version of this article.

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

基于 SEO 考量,本文标题与描述经 AI 调整,原始版本请参考内文。


iOS 跨平台帐号密码整合,加强登入体验

除 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

功能

在同时有网站又有 APP 的服务中最常遇到的问题就是使用者在网站登入注册过,且有记忆密码;但被引导安装 APP 后,打开登入要从头输入帐号密码非常不方便;此功能就是能将已存在在手机的帐号密码自动带入到与网站关联的 APP 之中,加速使用者登入流程。

效果图

不啰唆,先上完成效果图;第一眼看到可能会以为是 iOS ≥ 11 Password AutoFill 功能;不过请您仔细看,键盘并没有跳出来,而且我是点击「选择已存密码」按钮才跳出帐号密码选择视窗的。

既然提到了 Password AutoFill 那就先让我卖个关子,先介绍 Password AutoFill 和如何设置吧!

Password AutoFill

支援度:iOS ≥ 11

到如今已经 iOS 14 了,这个功能已经非常常见没什么特别的;在 APP 中的帐号密码登入页,叫出键盘输入时可以快速选择网站版服务的帐号密码,选择后就能自动带入,快速登入!

那么 APP 与 Web 之间是如何相认的呢?

Associated Domains!我们在 APP 中指定 Associated Domains 并在网站上上传 apple-app-site-association 档案,两边就能相认。

1.在专案设定中的「Signing & Capabilities」-> 左上「+ Capabilities」->「Associated Domains」

新增 webcredentials:你的网站域名 (ex: webcredentials:google.com )。

2.进入 苹果开发者后台

在「 Membership 」Tab 地方记录下「 Team ID

3.进入「Certificates, Identifiers & Profiles」->「Identifiers」-> 找到你的专案 -> 打开「Associated Domains」功能

APP 端设定完成!

4.Web网站端设定

建立一个名为「 apple-app-site-association 」的档案(无副档名),使用文字编辑器编辑,并输入以下内容:

1
2
3
4
5
6
7
{
  "webcredentials": {
    "apps": [
      "TeamID.BundleId"
    ]
  }
}

TeamID.BundleId 换成你的专案设定 (ex: TeamID = ABCD , BundleID = li.zhgchg.demoapp => ABCD.li.zhgchg.demoapp )

将此档案上传到网站 根目录/.well-known 目录下,假设你的 webcredentials 网站域名 是设 google.com 则此档案就要是 google.com/apple-app-site-associationgoogle.com/.well-know/apple-app-site-association 有办法存取到的。

补充:Subdomains

摘录官方文件,如果是 subdomains 则都须列在 Associated Domains 之中。

Web 端设定完成!

补充:applinks

这边有发现如果有设过 universal link applinks ,其实不用再多加 webcredentials 部分也能有效果;但我们还是照文件来吧,难保之后不会有其他问题。

回到程式

Code 部分,我们只需要将 TextField 设为 :

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

如果是新注册,密码确认栏位可使用:

1
repeatPasswordTextField.textContentType = .newPassword

这时候再重 Build & Run APP 后,在输入帐号时键盘上方就会出现同个网站下已存密码的选项了。

完成!

没出现?

可能是没打开自动填写密码功能(模拟器预设是关闭),请到「设定」->「密码」->「自动填写密码」->打开「自动填写密码」。

抑或是该网站没有已存在的密码,一样可在「设定」->「密码」-> 右上角「+ 新增」-> 新增。

进入主题

前菜 Password AutoFill 介绍完之后,再来进入本篇主题;如何达到效果图中的效果呢。

Shared Web Credentials

始于 iOS 8.0 只是之前很少看到 APP 使用,早在 Password AutoFill 出来之前其实就能使用此 API 整合网站帐号密码让使用者快速选择。

Shared Web Credentials 除了能读取帐号密码,还能新增帐号密码、对已存的帐号密码进行修改、删除。

设定

⚠️ 设定部分一样要设好 Associated Domains,同前述 Password AutoFill 设定。

所以可以说是 Password AutoFill 功能的加强版!!

因为一样要先设好 Password AutoFill 需要的环境才能使用此「进阶」功能。

读取

读取使用 SecRequestSharedWebCredential 方法进行操作:

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 如果有多个 webcredentials domain 可以指定某一个,或使用 null 不指定

  • account 指定要查某一个帐号,使用 null 不指定

效果图。(你可能有发现跟开始的效果图不一样)

⚠️ 因为此读取方法已在 iOS 14 被标示 Deprecated!

⚠️ 因为此读取方法已在 iOS 14 被标示 Deprecated!

⚠️ 因为此读取方法已在 iOS 14 被标示 Deprecated!

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

此方法仅适用 iOS 8 ~ iOS 14,iOS 13 之后可改用同 Sign in with Apple 的 API — 「 AuthenticationServices

AuthenticationServices 读取方式

支援度 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
    }
}

效果图,可以看到新的做法在流程上、显示上都能跟 Sign in with Apple 整合得更好。

⚠️ 此登入无法取代 Sign in with Apple(两个是不同东西)。

写入帐号密码到「密码」

被 Deprecated 的只有读取的部分,新增、删除、编辑的部分都还是照旧能用。

新增、删除、编辑的部分使用 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 可随意指定要存入的 domain 不一定要在 webcredentials

  • account 指定要新增、修改、删除的帐号

  • 如果要删除资料则将 password 带入 nil

  • 处理逻辑:

    • account 存在&有带入 password = 修改 password
    • account 存在&password 带入 nil = 从 domain 删除 account, password
    • account 不存在&有带入 password = 新增 account, password 到 domain

⚠️ 另外也不是能让你在背景偷修改的,每次修改都会跳出提示框提示使用者,使用者按「更新密码」才会真的修改资料。

密码产生器

最后一个小功能,密码产生器。

使用 SecCreateSharedWebCredentialPassword() 进行操作。

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

产生器产生出来的 Password 由英文大小写及数字并使用「-」组成 (ex: Jpn-4t2-gaF-dYk)。

完整测试专案下载

美中不足

如果有使用第三方密码管理工具(EX: onepass、lastpass)的朋友可能会发现,如果是键盘的 Password AutoFill 能支援显示&输入,但是在 AuthenticationServices 或 SecRequestSharedWebCredential 当中都没有显示出来;不确定有没有办法达成这个需求。

结束

感谢大家阅读,也感谢 saiday 、街声让我知道有这个功能 XD。

还有 XCode ≥ 12.5 模拟器新增录影,并支援储存成 GIF 功能太好用啦!

在模拟器上按「Command」+「R」开始录影,按一下红点停止录影;在右下角滑出的预览图上按「右键」->「Save as Animated GIF」即可存成 GIF 然后直接贴到文章内!

有任何问题及指教欢迎 与我联络


Buy me a beer

本文首次发表于 Medium (点击查看原始版本),由 ZMediumToMarkdown 提供自动转换与同步技术。

Improve this page on Github.

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