6

Я использую keychain ios (keychainItemWrapper/SSKeychain), чтобы сохранить токен входа в приложение и поддерживать состояние входа. В настоящее время я храню простой NSDictionary в цепочке ключей, содержащей мой токен, истечение срока действия токена и токен обновления. Я сериализую его в NSData и сохраняю, используя kSecValueData. Я также устанавливаю kSecAttrAccount и kSecAttrService, но не использую их для auth.ios хранить лог входа в KeyChain не удается получить, редко и случайным образом, но последовательно

Это отлично работает примерно в 95% случаев. Проблема в том, что это случайным образом, непредсказуемо и спорадически, keychain не возвращает данные, когда я прошу его получить токен. Обычно после умеренного времени от приложения, при повторном открытии. Это не должно быть в фоновом режиме или после какой-либо конкретной задержки.

Неисправность возникает при запросе моего NSData ниже и возвращает <> вместо <ABCD EFGH IJKL ....>. Я думаю, что это нуль. Таким образом, код думает, что пользователь не зашел в систему и сразу же отключил его на моей целевой странице регистрации/входа в приложение, без ошибки выхода из системы, ошибки истечения токена и т. Д. Если я скрою приложение, а затем снова открою его, он почти всегда получит правильный брелок info, и пользователь снова войдет в систему.

Это создает путаницу при столкновении. Это также означает, что пользователь не может поддерживать это истинное 100% регистрируемое состояние, изредка случайно выходить из системы. Я не мог предсказать это или отладить его, и изменение библиотек ключей, как показано ниже, не исправило это для меня. Это случается для меня, и нескольких пользователей TestFlight, и в нашем производственном приложении.

Любые предложения по сохранению целостности связки ключей и загрузке 100% времени? Мы готовы реализовать хранилище резервных копий NSUserDefaults на токене, чтобы использовать в этих случаях, что-то, что я действительно не хочу делать, чтобы хранить токен аутентификации.

Хранение:

// load keychain 
KeychainItemWrapper *keychainItem = [KeychainItemWrapper keyChainWrapperForKeyID:kcIdentifier]; 
NSString *firstLaunch = [keychainItem objectForKey: (__bridge id)(kSecAttrAccount)]; 
if (firstLaunch == nil){ 
    // initialize if needed 
    [keychainItem setObject:email forKey: (__bridge id)(kSecAttrAccount)]; 
    [keychainItem setObject:kcIdentifier forKey: (__bridge id)kSecAttrService]; 
    [keychainItem setObject:(id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible]; 
} 

// serialize "auth" NSDictionary into NSData and store 
NSString *error; 
NSData *dictionaryData = [NSPropertyListSerialization dataFromPropertyList:auth format:NSPropertyListXMLFormat_v1_0 errorDescription:&error]; 
[keychainItem setObject:dictionaryData forKey:(id)kSecValueData]; 

Загрузка:

// after similar KeychainItemWrapper initialization as above 
NSData *dictionaryData = [keychainItem objectForKey:(id)kSecValueData]; 
NSString *error; 

NSDictionary *auth = [NSPropertyListSerialization propertyListFromData:dictionaryData mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error]; 
NSString *token = auth[@"access_token"]; 

Я также попытался с помощью библиотеки SSKeychain CocoaPod, который широко доступен, и обертку вокруг логики брелка. Это более чистый доступ, но с той же проблемой не удается. Здесь я просто храню значения NSString, так как не было прямого способа хранить NSData в lib.

// store in keychain 
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock]; 
[SSKeychain setPassword:auth[@"access_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN]; 
[SSKeychain setPassword:auth[@"expires_at"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT]; 
[SSKeychain setPassword:auth[@"refresh_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN]; 

// load from keychain 
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock]; 
NSString *token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN]; 
NSString *expires_at = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT]; 
NSString *refresh_token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN]; 
+0

Если брелок не работает, вы должны получить код ошибки. Что это? – Segev

+0

У меня такая же проблема, когда поиск случайно прерывается. Вот вспомогательный метод - код ошибки keychain для строки: https://gist.github.com/inorganik/f9971e65f71e037650b39b5f182e157e – inorganik

+0

Я попытаюсь зарегистрировать ошибки. это, вероятно, происходит внутри библиотек, которые я использую, которые не отображают параметр & error. – Miro

ответ

3

Брелок имеет проблемы на данный момент, и на некоторое время действительно. Похоже, что вы с легкостью выходите, как обычно, когда он нарушает силу выхода приложения, чтобы вернуть его к жизни.

Одна вещь, которая помогает - получить доступ к цепочке ключей только один раз по первому запросу и затем кэшировать результат в памяти, если он уже находится в памяти, а затем просто вернуть его оттуда.

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

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

+0

- это брелок в целом, безопасный поток для нескольких потоков, или я должен беспокоиться о @синхронизации точки доступа? Кажется, я помню, что это было обработано уже, возможно, по крайней мере по SSKeychain. – Miro

+0

Это само по себе безопасный поток, SSKeychain - это больше тип данных и обертка эффективности – Wain