Swift Keychain - Хранение учетных данных OAuth: «Указанный элемент уже существует в связке ключей» - PullRequest
0 голосов
/ 26 августа 2018

Я создаю приложение для iOS для своего веб-сайта и пытаюсь использовать OAuth2 для управления учетными данными для входа.При входе пользователя в систему я успешно подключаюсь к своей конечной точке аутентификации, используя предоставленные имя пользователя и пароль, и пытаюсь сохранить токен доступа и токен обновления в цепочке для ключей, поэтому пользователю не нужно предоставлять учетные данные для продвижения вперед.

У меня проблемы с хранением токена обновления и токена доступа в моей цепочке для ключей, следуя инструкциям из этих источников:

Я могу успешно сохранить либоТокен доступа или токен обновления, но независимо от того, какой из них я храню первым, при попытке сохранить другой я получаю следующее сообщение об ошибке: «Указанный элемент уже существует в цепочке для ключей.»

Я добавилCheckForExisting функция для удаления любых существующих элементов с такими же характеристиками, но когда я пытаюсь удалить существующий элемент цепочки для ключей, используя same query, я получаю статус errSecItemNotFound.Так что, к сожалению, мне говорят, что я не могу создать свой элемент, потому что он уже существует, но я не могу удалить существующий элемент, потому что не существует никакого существующего элемента.

Моя гипотеза состоит в том, что созданиеэлемента маркера доступа блокирует создание элемента маркера обновления, поэтому я надеюсь, что кто-то может пролить свет на следующее:

  1. Почему блокируется создание второго элемента?Есть ли в цепочке для ключей какие-то встроенные проверки первичных ключей, которые я нажимаю (например, не может хранить более одного kSecClassInternetPassword)?
  2. Как правильно различать два токена.Прямо сейчас я использую 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)
    }
}
...