-
Notifications
You must be signed in to change notification settings - Fork 351
Migrating from AccountStore to Xamarin.Essentials SecureStorage
AccountStore
has been deprecated and we recommend using Xamarin.Essentials Secure Storage as a replacement.
It is best practice to never store actual OAuth tokens on device as all devices are considered inherently insecure. Instead you should only ever persist Refresh Tokens which can be used to fetch OAuth tokens which can be kept in memory as needed. It's always best practice to limit the expiration time of OAuth tokens.
We've made a simple class to help with the migration of both data and your code from using AccountStore
to Xamarin.Essentials SecureStorage
:
using Newtonsoft.Json;
using Xamarin.Auth;
using Xamarin.Essentials;
public class SecureStorageAccountStore
{
public static async Task SaveAsync(Account account, string serviceId)
{
// Find existing accounts for the service
var accounts = await FindAccountsForServiceAsync(serviceId);
// Remove existing account with Id if exists
accounts.RemoveAll(a => a.Username == account.Username);
// Add account we are saving
accounts.Add(account);
// Serialize all the accounts to javascript
var json = JsonConvert.SerializeObject(accounts);
// Securely save the accounts for the given service
await SecureStorage.SetAsync(serviceId, json);
}
public static async Task<List<Account>> FindAccountsForServiceAsync(string serviceId)
{
// Get the json for accounts for the service
var json = await SecureStorage.GetAsync(serviceId);
try {
// Try to return deserialized list of accounts
return JsonConvert.DeserializeObject<List<Account>>(json);
} catch { }
// If this fails, return an empty list
return new List<Account>();
}
public static async Task MigrateAllAccountsAsync(string serviceId, IEnumerable<Account> accountStoreAccounts)
{
var wasMigrated = await SecureStorage.GetAsync($"XamarinAuthAccountStoreMigrated{serviceId}");
if (wasMigrated == "1")
return;
await SecureStorage.SetAsync($"XamarinAuthAccountStoreMigrated{serviceId}", "1");
// Just in case, look at existing 'new' accounts
var accounts = await FindAccountsForServiceAsync(serviceId);
foreach (var account in accountStoreAccounts) {
// Check if the new storage already has this account
// We don't want to overwrite it if it does
if (accounts.Any(a => a.Username == account.Username))
continue;
// Add the account we are migrating
accounts.Add(account);
}
// Serialize all the accounts to javascript
var json = JsonConvert.SerializeObject(accounts);
// Securely save the accounts for the given service
await SecureStorage.SetAsync(serviceId, json);
}
}
To use the migration helper you should run this code to fetch existing AccountStore
accounts and move them to Xamarin.Essentials:
var accountStoreAccounts = AccountStore.Create().FindAccountsForService("Facebook");
await SecureStorageAccountStore.MigrateAllAccountsAsync("Facebook", accountStoreAccounts);
With AccountStore
, you would have stored account information like this:
AccountStore.Create ().Save (account, "Facebook");
Now, instead, using the helper class you'd write:
await SecureStorageAccountStore.SaveAsync(account, "Facebook");
With AccountStore
, you would have retrieved accounts for a service like this:
var accounts = AccountStore.Create (this).FindAccountsForService ("Facebook");
Using the helper class you can now write:
var accounts = await SecureStorageAccountStore.FindAccountsForServiceAsync("Facebook");