TinkoffID 1.2.1

TinkoffID 1.2.1

Dmitry Overchuk维护。



TinkoffID 1.2.1

  • Дмитрий Оверчук

TinkoffID

内容

预备步骤

要开始作为合作伙伴使用 Tinkoff ID,请在此页面上填写接入申请。您的申请被审核通过后,您将通过电子邮件收到 client_id 和密码。详细说明可在文档中找到

安装

Swift Package Manager

TinkoffID 支持Swift Package Manager。您可以在此处找到为您的项目设置SPM的说明。设置项目后,只需将存储库链接添加为依赖项。

https://github.com/tinkoff-mobile-tech/TinkoffID-iOS

Cocoapods

要使用CocoaPods安装 TinkoffID,请在你项目的 Podfile 中添加以下行:

pod 'TinkoffID'

然后在项目目录中运行 pod install 命令。

应用程序要求

为了使用SDK,需要以下条件

  • iOS 10及以上
  • 已注册的授权应用程序标识符(client_id
  • 已注册的将被授权的应用程序URL方案,该方案将在授权后用于返回应用程序
  • plist中添加条目,允许您的应用程序切换到蒂宁科夫应用程序
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>tinkoffbank</string>
</array>

SDK公共部分的结构

ITinkoffID

负责授权的对象实现了 ITinkoffID 协议。而 ITinkoffID 协议本身是由以下协议组成的组合:

  • ITinkoffAuthInitiator - 授权过程开始时的初始化器
  • ITinkoffAuthCallbackHandler - 从应用返回 Tinkoff 应用的回调处理器
  • ITinkoffCredentialsRefresher - 能够通过它们的 Refresh token 更新 Credentials 的对象
  • ITinkoffSignOutInitiator - 用于撤回授权数据的初始化器

根据应用的架构,可以直接使用 ITinkoffID 或者在系统所需部分单独使用每个子协议。

TinkoffAuthError

TinkoffAuthError 类型的 enum 描述了可能的授权错误

描述
failedToLaunchApp 无法启动 Tinkoff 应用
cancelledByUser 用户在进入 Tinkoff 之后取消了授权
unavailable 用户不可用授权其他应用
failedToObtainToken 在返回应用后无法完成授权
failedToRefreshCredentials 无法更新令牌

在发生错误时,建议建议用户稍后再试。

获取 ITinkoffID

SDK 提供了公共抽象 ITinkoffIDFactory 和实现它的公共类 TinkoffIDFactory,该类用于组装和提供实现 ITinkoffID 的对象。

// Идентификатор приложения
let clientId = "someClient"
// URL обратного вызова, необходимый для возврата в приложение
let callbackUrl = "myapp://authorized"

// Инициализация фабрики ITinkoffID
let factory = TinkoffIDFactory(clientId: clientId,
                               callbackUrl: callbackUrl)
// Получение ITinkoffID
let tinkoffId = factory.build()

在获得 ITinkoffID 后,应用程序可以开始进行授权。

授权

开始前

ITinkoffAuthInitiator 可以通过 isTinkoffAuthAvailable 标志提供执行授权的能力。如果标志被设置,表示用户已安装了 Tinkoff 应用程序,可以通过该应用程序登录。当使用设置标志的 startTinkoffAuth 方法调用时,将引发到指定应用程序以初始化授权的跳转,如果标志是未设置的,用户将被重定向到 App Store 中的该应用程序页面。

执行授权

开始授权需要调用 ITinkoffAuthInitiator 对象的 startTinkoffAuth 方法。

tinkoffId.startTinkoffAuth { result in
    do {
        let payload = try result.get()
        
        print("Access token obtained: \(payload.accessToken)"
    } catch {
        print(error)
    }
}

调用此方法将导致用户被引导到 Tinkoff 应用程序以确认应用程序的授权。

继续授权

用户确认授权后,将返回到授权的应用程序以完成授权。使用应用程序提供的 URL 反馈调用,将回退。该阶段应用程序的任务是将接收到的 AppDelegate URL 传递给 ITinkoffAuthCallbackHandler 对象的 handleCallbackUrl 方法。

func application(_ app: UIApplication,
                 open url: URL,
                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return tinkoffId.handleCallbackUrl(url)
}

SDK将处理URL传递的参数,完成授权,并将对象Result<TinkoffTokenPayload, TinkoffAuthError>传递给在调用startTinkoffAuth()时定义的块。

更新授权数据

有时,应用程序需要获取最新的TinkoffTokenPayload对象(例如,当旧トークンの有效期限到期时)。

为此,需要调用对象ITinkoffCredentialsRefresherobtainTokenPayload方法,如下所示

let credentials: TinkoffTokenPayload = ...

tinkoffId.obtainTokenPayload(using: credentials.refreshToken) { result in
    do {
        let newCredentials: TinkoffTokenPayload = try result.get()
    } catch {
        print(error)
    }
}

撤回授权数据

有时,可能会出现不再需要获取的授权数据的情况。例如,在改变或退出已授权的应用程序中的用户账户时。在这种情况下,应用程序需要通过ITinkoffSignOutInitiator撤回授权数据。

let credentials: TinkoffTokenPayload = ...

tinkoffId.signOut(with: credentials.accessToken, tokenTypeHint: .access, completion: { result in
    do {
        _ = try result.get()
        
        print("Signed out")
    } catch {
        print(error)
    }
})

TinkoffTokenPayload结构

授权成功后,应用程序将获取包含以下属性的Credentials对象

  • accessToken - 用于调用Tinkoff API的令牌
  • refreshToken - 获取新的accessToken所需的令牌。如果用户禁止应用程序随时随地访问,则可能不存在。
  • idToken - JWT格式的用户标识符
  • expirationTimeout - accessToken失效并需要使用refreshToken获取新accessToken的时间

存储 Refresh Token

在接收 TinkoffTokenPayload 且其中包含 refreshToken 字段时,建议保存该字段的值。如此,当原有的 accessToken 变得无效时,可以请求新的 accessToken。推荐使用 Keychain Services 存储令牌。

UI

SDK 提供了两种 Tinkoff 品牌登录按钮。第一种是带有文本的标准矩形按钮,可以设置文本、圆角和字体。还可以选择三种颜色风格和大小。还可以添加额外的文本以吸引客户。第二种是没有文本的紧凑型按钮,也可以选择三种颜色风格。

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Создание стандартной кнопки
    let button = TinkoffIDButtonBuilder.build()
    
    // Добавление обработчика нажатия
    button.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)
    
    // Добавление в иерархию
    view.addSubview(button)
    
    // Отступ кнопки от краёв
    let padding: CGFloat = 16
    
    // Расположение кнопки на экране
    button.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        button.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: padding),
        button.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -padding),
        button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -padding),
    ])
}

请注意:在获取按钮后,需要将其放置在屏幕上,并添加点击事件处理器。对于布局,建议使用 AutoLayout 并且不指定高度,因为高度是通过 intrinsicContentSize 自动确定的。

您可以在这里详细了解按钮布局规则:此处

无需 Tinkoff 应用程序进行调试

在需要在不使用 Tinkoff 应用程序或 iOS 模拟器上调试 Tinkoff ID 集成时,SDK 提供了 TinkoffID 的调试实现。

与主要实现的区别

  • 而不是进入 Tinkoff 应用程序,过渡会转向专门的应用程序,允许选择身份验证场景
  • 不会对 Tinkoff 服务器进行请求

调试应用程序设置

要允许应用程序进入调试应用程序,您需要在您的应用程序的 plist 文件中添加以下内容

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>tinkoffiddebug</string>
</array>

接下来,请将应用程序 TinkoffID Debug 安装到设备或模拟器上。项目位于 此处

ℹ️调试 SDK 使用与标准实现相同的 ITinkoffID 接口,因此,在遵循依赖倒置原则的情况下,您可以在不修改您的应用程序代码的情况下引入它。此外,ITinkoffIDFactory 是一个抽象工厂,这意味着在遵循相同的依赖倒置原则时,您可以用它代替已编译的 ITinkoffID 对象进行实例化。

获取调试用的 ITinkoffID 实现

现在,您的应用程序已配置并且已安装调试应用程序,可以构建调试 ITinkoffID 的实现。可以通过以下方式完成此操作:

// Ссылка по которой будет осуществлен возврат в приложение
let callbackUrl: String = ""

// Конфигурация для отладки
let configuration = DebugConfiguration(
    canRefreshTokens: true, // Если флаг `canRefreshTokens` поднят, то обновление токенов будет завершаться без ошибки
    canLogout: true // Если флаг `canLogout` поднят, выход из приложения будет завершаться без ошибки
)

// Фабрика, возвращающая реализацию ITinkoffID для отладки
let factory: ITinkoffIDFactory = DebugTinkoffIDFactory(
    callbackUrl: callbackUrl,
    configuration: configuration
)

// Реализация ITinkoffID для отладки
let debugTinkoffId: ITinkoffID = factory.build()

接下来,您可以像使用常规的 ITinkoffID 那样使用获取到的对象。

调试应用程序

调用调试 ITinkoffID 实现的 startTinkoffAuth 方法后,您将进入调试应用程序。进入后,您将看到以下可能的操作列表:

  • 返回并成功完成登录 - 返回到您的应用程序并成功返回占位符令牌
  • 返回未完成登录 - 返回到您的应用程序并尝试获取令牌时出现错误
  • 取消登录 - 返回到您的应用程序并模拟用户取消登录
  • 模拟登录不可用 - 返回到您的应用程序并模拟 Tinkoff ID 对用户不可用

示例应用

SDK 附带示例应用。要运行示例,请克隆仓库,在 Example 文件夹中执行 pod install 命令,打开生成的 .xcworkspace 文件并运行项目。

应用包括 AppDelegateAuthViewController

AppDelegate

AppDelegate 创建 AuthViewController 并将其设置为应用程序窗口的根控制器。当应用程序启动时,创建 ITinkoffIDFactory 工厂,在 applicationDidFinishLaunching 方法中收集 ITinkoffID,并将其作为参数传递给初始化 AuthViewController

⚠️注意!在 AppDelegate.swift 中定义了 Constant 结构,其字段之一为 clientId 类型为 String。为了测试授权,需要将其内容替换为在 Tinkoff ID 注册时获得的 client_id

AuthViewController

AuthViewController 分别通过对象实现 ITinkoffAuthInitiatorITinkoffCredentialsRefresherITinkoffSignOutInitiator

在当前实现中,所有这些链接都指向同一个实现 ITinkoffID 接口的 TinkoffID 对象实例。这种做法是选择来展示使用 ITinkoffID 子接口在不同系统部分的能力。SDK 用户有权根据应用程序架构自行决定是否使用统一的 ITinkoffID 接口或所需的子接口。

有关 ITinkoffID 子接口的详细信息,请参阅 SDK 公开部分结构 一节。

view 加载后,控制器向其添加一个按钮,用户可以通过点击此按钮通过 Tinkoff 登录,进而启动授权过程。

支持

可以在问题部分报告错误和请求新功能。联系方式邮箱 - [email protected]

开发者