Intermittent errSecNotAvailable on some devices when storing keychain

I released an update to my app a few days ago, the user can add their account info, which gets sent to a SAML2 webservice, returns a refresh token, and stores it in the iOS Keychain using the following method:


+(void)saveString:(NSString *)inputString forKey:(NSString *)account {
    /
    /
   
    NSMutableDictionary *query=[NSMutableDictionary dictionary];
    [query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    [query setObject:account forKey:(__bridge id)kSecAttrAccount];
    [query setObject:(__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
   
    OSStatus error=SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
    if (error == errSecSuccess) {
        /
        NSDictionary *attributesToUpdate=@{(__bridge id)kSecAttrAccount: account, (__bridge id)kSecValueData: [inputString dataUsingEncoding:NSUTF8StringEncoding]};
       
        error = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate);
        /
        if (error != errSecSuccess) {
            NSLog(@"SecItemUpdate failed: %d", error);
        }
       
    }else if (error == errSecItemNotFound) {
        [query setObject:account forKey:(__bridge id)kSecAttrAccount];
        [query setObject:[inputString dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge id)kSecValueData];
        error=SecItemAdd((__bridge CFDictionaryRef)query, NULL);
        /
        if (error != errSecSuccess) {
            NSLog(@"SecItemUpdate failed: %d", error);
        }
    }else {
        /
        NSLog(@"SecItemCopyMatching failed: %d", error);
    }
}


Depending on who has the app installed, it seems like it's not storing the refresh token to the keychain, and silently failing. When I plugged in one of the affected user's phones into XCode and ran a debug build, it gave me an "errSecNotAvailable" message (code -25291).


When the user (via the process of logging into an authenticated app screen) attempt to pull the token out of the keychain using the following method, it pulls out a nil value, and doesn't log them in (as expected):


+(NSString *)getStringForKey:(NSString *)account {
    /
    if (account != nil) {
        NSMutableDictionary *query=[NSMutableDictionary dictionary];
        [query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
        [query setObject:account forKey:(__bridge id)kSecAttrAccount];
        [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
       
        CFDataRef dataFromKeychain=nil;
        OSStatus error=SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dataFromKeychain);
        NSData *dataResult=(__bridge_transfer NSData *)dataFromKeychain;
       
        NSString *stringToReturn=nil;
        if (error == errSecSuccess) {
            stringToReturn=[[NSString alloc] initWithData:dataResult encoding:NSUTF8StringEncoding];
        }
       
        return stringToReturn;
    }else {
        return nil;
    }
}


This has been very frustrating for both my users, as well as myself, since the keychain works on most devices (including my personal and test iPhones), but not on others. Is there a reason for this error to occur on some devices but not others? I couldn't even determine a pattern (i.e. device model, iOS Version, etc). It seems to be indiscriminate.


Thanks in advance!


- Scott

Replies

You should definitely file a bug about this. Make sure to include a sysdiagnose log (per the instructions on our Bug Reporting > Profiles and Logs page) taken shortly after reproducing the error. And please post your bug number, just for the record.

Does the problem clear itself when you restart the device?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
I am currently facing the same issue.

I managed to debug using an affected device from a customer and I receive -25291 error when the app tries to add a new item to the keychain using "secItemAdd".

I tried restarting the device as Quinn mentioned, but I am still receiving the same error with the same expected result of the user unable to login.

Since this thread was posted over 2 years ago, I was wondering if you managed to find a solution to this? And did you file a bug as Quinn instructed?