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