我正在为我的网站构建一个iOS应用,并且尝试使用OAuth2管理登录凭据。在用户登录时,我使用提供的用户名和密码成功命中了身份验证端点,并且尝试将访问令牌和刷新令牌存储在钥匙串中,因此用户不必继续提供凭据。
按照这些来源的指示,我在将刷新令牌和访问令牌存储到我的钥匙串时遇到了麻烦:
我能够成功存储访问令牌或刷新令牌,但是无论我先存储哪个令牌,当尝试存储另一个令牌时,我都会收到以下错误消息:“指定的项目已存在于钥匙串。”
我添加了一个CheckForExisting
函数来删除任何具有相同规格的现有项,但是当我尝试使用相同的query
来删除现有钥匙串项时,我会收到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)
}
}
答案 0 :(得分:0)
根据此https://developer.apple.com/documentation/security/errsecduplicateitem看起来,类kSecClassInternetPassword的唯一键仅包含以下属性: kSecAttrAccount,kSecAttrSecurityDomain,kSecAttrServer,kSecAttrProtocol,kSecAttrAuthenticationType,kSecAttrPort和kSecAttrPath。
因此,kSecAttrLabel不在列表中,并且您的refresh_token_query复制了access_token_query。