Oauth 2.0 Swift密钥链-存储OAuth凭据:“0”;指定的项已存在于钥匙链中;

Oauth 2.0 Swift密钥链-存储OAuth凭据:“0”;指定的项已存在于钥匙链中;,oauth-2.0,swift4,keychain,Oauth 2.0,Swift4,Keychain,我正在为我的网站构建一个iOS应用程序,并尝试使用OAuth2来管理登录凭据。在用户登录时,我正在使用提供的用户名和密码成功地访问我的身份验证端点,并且我正在尝试将访问令牌和刷新令牌存储在密钥链中,因此用户不必继续提供凭据 我在密钥链中存储刷新令牌和访问令牌时遇到问题,请遵循以下来源的说明: 我能够成功地存储访问令牌或刷新令牌,但无论我先存储哪一个,在尝试存储另一个令牌时,我都会收到以下错误消息:“指定的项已存在于密钥链中。” 我添加了一个CheckForExisting函数来删除任何

我正在为我的网站构建一个iOS应用程序,并尝试使用OAuth2来管理登录凭据。在用户登录时,我正在使用提供的用户名和密码成功地访问我的身份验证端点,并且我正在尝试将访问令牌和刷新令牌存储在密钥链中,因此用户不必继续提供凭据

我在密钥链中存储刷新令牌和访问令牌时遇到问题,请遵循以下来源的说明:

我能够成功地存储访问令牌或刷新令牌,但无论我先存储哪一个,在尝试存储另一个令牌时,我都会收到以下错误消息:“指定的项已存在于密钥链中。”

我添加了一个
CheckForExisting
函数来删除任何具有相同规格的现有项目,但是当我尝试使用相同的
查询删除现有的钥匙链项目时,我收到一个
errSecItemNotFound
状态。因此,令人沮丧的是,我被告知我无法创建我的项目,因为它已经存在,但我无法删除现有项目,因为不存在现有项目

我的假设是,访问令牌项的创建会阻止刷新令牌项的创建,因此我希望有人能够阐明以下几点:

  • 为什么第二个项目创建被阻止?钥匙链是否有我正在进行的一些内置主键检查(例如不能存储多个
    kSecClassInternetPassword
  • 区分这两种代币的正确方法是什么。现在我使用的是
    kSecAttrLabel
    ,但这是一种冒险
  • 请注意,我希望能解释为什么我目前的方法失败了。我绝对欢迎替代实现,但我真的想了解这里的幕后情况,因此如果可能的话,请说明替代实现在何处避免了我似乎陷入的陷阱

    存储令牌的Swift4代码:

    func StoreTokens(username: String, access_token: String, refresh_token: String) throws {
        func CheckForExisting(query: [String: Any]) throws {
            let status = SecItemDelete(query as CFDictionary)
            guard status == errSecSuccess || status == errSecItemNotFound else {
                let error_message = SecCopyErrorMessageString(status, nil)!
                throw KeychainError.unhandledError(status: error_message)
            }
        }
    
        let configuration = ConfigurationDetails()
    
        let server = configuration.server
        let access_token = access_token.data(using: String.Encoding.utf8)!
        let refresh_token = refresh_token.data(using: String.Encoding.utf8)!
        let access_token_query: [String: Any] = [
            kSecClass as String: kSecClassInternetPassword,
            kSecAttrAccount as String: username,
            kSecAttrServer as String: server,
            kSecAttrLabel as String: "AccessToken",
            kSecValueData as String: access_token
        ]
    
        let refresh_token_query: [String: Any] = [
            kSecClass as String: kSecClassInternetPassword,
            kSecAttrAccount as String: username,
            kSecAttrServer as String: server,
            kSecAttrLabel as String: "RefreshToken",
            kSecValueData as String: refresh_token
        ]
    
        try CheckForExisting(query: access_token_query)
        let access_status = SecItemAdd(access_token_query as CFDictionary, nil)
        guard access_status == errSecSuccess else {
            let error_message = SecCopyErrorMessageString(access_status, nil)!
            throw KeychainError.unhandledError(status: error_message)
        }
    
        try CheckForExisting(query: refresh_token_query)
        let refresh_status = SecItemAdd(refresh_token_query as CFDictionary, nil)
        guard refresh_status == errSecSuccess else {
            let error_message = SecCopyErrorMessageString(refresh_status, nil)!
            throw KeychainError.unhandledError(status: error_message)
        }
    }
    
    根据这一点,kSecClassInternetPassword类的唯一密钥似乎只包含以下属性: kSecAttrAccount、kSecAttrSecurityDomain、kSecAttrServer、kSecAttrProtocol、kSecAttrAuthenticationType、kSecAttrPort和kSecAttrPath

    因此,kSecAttrLabel不在列表中,您的刷新令牌查询与访问令牌查询重复