In my Xamarin.iOS project I used SecRecord/SecKeyChain to store my token values and app version. From production log I found keychain related exceptions with status code 'InteractionNotAllowed' when try to write/read items in keychain. Apple documents states that to resolve InteractionNotAllowed error we need to change the default kSecAttrAccessible attribute value from ‘WhenUnlocked' to ‘Always’. But in my existing code when I changed accessible attribute to 'Always' app log out because it failed to read token from keychain. It return’s 'Item not found' when read. But when I tried to save token again it returns 'Duplicate item'. So again I tried to remove same item but this time it again returns 'Item not found'. That’s really strange I can’t delete it and I can’t read it with same key.
Below is the code snippet -
private SecRecord CreateRecordForNewKeyValue(string accountName, string value)
{
return new SecRecord(SecKind.GenericPassword)
{
Service = App.AppName,
Account = accountName,
ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
Accessible = SecAccessible.Always //This line of code is newly added.
};
}
private SecRecord ExistingRecordForKey(string accountName)
{
return new SecRecord(SecKind.GenericPassword)
{
Service = App.AppName,
Account = accountName,
Accessible = SecAccessible.Always //This line of code is newly added.
};
}
public void SetValueForKeyAndAccount(string value, string accountName, string key)
{
var record = ExistingRecordForKey(accountName);
try
{
if (string.IsNullOrEmpty(value))
{
if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
RemoveRecord(record);
return;
}
// if the key already exists, remove it before set value
if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
RemoveRecord(record);
}
catch (Exception e)
{
//Log exception here -("RemoveRecord Failed " + accountName, e,);
}
//Adding new record values to keychain
var result = SecKeyChain.Add(CreateRecordForNewKeyValue(accountName, value));
if (result != SecStatusCode.Success)
{
if (result == SecStatusCode.DuplicateItem)
{
try
{
//Log exception here -("Error adding record: {0} for Account-" + accountName, result), "Try Remove account");
RemoveRecord(record);
}
catch (Exception e)
{
//Log exception here -("RemoveRecord Failed after getting error SecStatusCode.DuplicateItem for Account-" + accountName, e);
}
}
else
throw new Exception(string.Format("Error adding record: {0} for Account-" + accountName, result));
}
}
public string GetValueFromAccountAndKey(string accountName, string key)
{
try
{
var record = ExistingRecordForKey(accountName);
SecStatusCode resultCode;
var match = SecKeyChain.QueryAsRecord(record, out resultCode);
if (resultCode == SecStatusCode.Success)
{
if (match.ValueData != null)
{
string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
if (string.IsNullOrEmpty(valueData))
return string.Empty;
return valueData;
}
else if (match.Generic != null)
{
string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
if (string.IsNullOrEmpty(valueData))
return string.Empty;
return valueData;
}
else
return string.Empty;
}
}
catch (Exception e)
{
// Exception logged here -("iOS Keychain Error for account-" + accountName, e);
}
return string.Empty;
}
Any help would be great! Thanks