Skip to main content
‹ /compat
Functions

Generic passwords

setGenericPassword and the functions around it, as react-native-keychain 10 has them: a user name and a password per service, kept as the credentials of the store of that service.

Import
import { setGenericPassword, getGenericPassword, hasGenericPassword, resetGenericPassword, getAllGenericPasswordServices } from '@apexnative/keychain/compat';

A generic password is a user name and a password under a service. Here it is the credentials of the store of that service: the password is the secret, and the user name is the item's label, readable without a prompt. Without a service, the store is the default one, keychain.

Usage​

src/auth/session.ts
import * as Keychain from '@apexnative/keychain/compat';

export async function saveSession(username: string, refreshToken: string) {
await Keychain.setGenericPassword(username, refreshToken, {
service: 'auth',
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
}

export async function restoreSession() {
if (!(await Keychain.hasGenericPassword({ service: 'auth' }))) return null; // no prompt
const credentials = await Keychain.getGenericPassword({
service: 'auth',
authenticationPrompt: { title: 'Sign in to Example', cancel: 'Use password' },
});
return credentials ? credentials.password : null;
}

export async function signOut() {
await Keychain.resetGenericPassword({ service: 'auth' });
}

setGenericPassword​

function setGenericPassword(
username: string,
password: string,
options?: SetOptions
): Promise<Result>;
ReturnsPromise<Result>PromptNever: a write shows no prompt, whatever its accessControlThrowsINVALID_ARGUMENT, UNSUPPORTED, HARDWARE_UNAVAILABLE, and what the device can't do: BIOMETRY_NOT_ENROLLED, PASSCODE_NOT_SET, …PlatformsiOSAndroid

Writes the user name and the password of the service, in place of what was there. It calls setCredentials in the store of service, and adds the service to the list of getAllGenericPasswordServices unless it is the default one.

ParameterTypeDescription
usernameRequiredstringKept as the label of the item, outside the secret. Not empty, without U+0000.
passwordRequiredstringThe secret. Any string without U+0000; for binary data use the core's setBytes.
optionsSetOptionsWhich service, and how to protect the item. See SetOptions.
OptionHere
serviceThe store of that service; without it, the default store
accessibleACCESSIBLE, mapped to the core's Accessible; ALWAYS is UNSUPPORTED
accessControlACCESS_CONTROL, mapped to the core's AccessControl; APPLICATION_PASSWORD is UNSUPPORTED
cloudSynciOS: synchronizable, the item goes to iCloud Keychain
accessGroupiOS: the access group of the store
securityLevelAndroid: SECURE_HARDWARE fails with HARDWARE_UNAVAILABLE on a device that keeps keys in software; ANY and SECURE_SOFTWARE change nothing
storageUNSUPPORTED, any value
authenticationPromptIgnored: a write shows no prompt

It resolves with the service and the storage, as rnk does: { service, storage }, where storage is 'keychain' on iOS and 'KeystoreAESGCM' on Android.

await Keychain.setGenericPassword('ivan', 'secret');
// { service: 'com.example.app', storage: 'keychain' }

The core's checks apply: an empty user name, U+0000 in a string, an empty service, or cloudSync with an accessControl is INVALID_ARGUMENT. See Options and checks.

getGenericPassword​

function getGenericPassword(options?: GetOptions): Promise<UserCredentials | false>;
ReturnsPromise<UserCredentials | false>PromptWhen the item was written with an accessControlThrowsWhat the prompt ends with: USER_CANCELED, LOCKOUT, KEY_INVALIDATED, …; UNSUPPORTED for APPLICATION_PASSWORDPlatformsiOSAndroid

Reads the user name and the password of the service, or false when there are none. It calls getCredentials in the store of service.

ParameterTypeDescription
optionsGetOptionsWhich service, and the texts of the prompt. See GetOptions.
const credentials = await Keychain.getGenericPassword({ authenticationPrompt: 'Unlock your account' });
// { service: 'com.example.app', username: 'ivan', password: 'secret', storage: 'keychain' } or false
  • authenticationPrompt as a string is the title. As an object it takes title, subtitle, description (Android) and cancel. Without it, the prompt has rnk's texts: Authenticate to retrieve secret and Cancel.
  • The accessControl of the options is checked and then ignored: the item keeps the protection it was written with. authenticationType is ignored too.
  • A prompt that doesn't end in success rejects with a KeychainError and its code: USER_CANCELED, LOCKOUT, KEY_INVALIDATED. See Errors.

hasGenericPassword​

function hasGenericPassword(options?: BaseOptions): Promise<boolean>;
ReturnsPromise<boolean>PromptNeverPlatformsiOSAndroid

Whether the service has a generic password. It calls hasItem('credentials') in the store of service, so a protected item is found without a prompt.

if (await Keychain.hasGenericPassword({ service: 'auth' })) showFaceIdButton();

resetGenericPassword​

function resetGenericPassword(options?: BaseOptions): Promise<boolean>;
ReturnsPromise<boolean>PromptNeverPlatformsiOSAndroid

Deletes the generic password of the service and takes the service off the list of getAllGenericPasswordServices. It calls removeCredentials in the store of service. It resolves true, also when there was nothing to delete; the other items of the store stay.

await Keychain.resetGenericPassword({ service: 'auth' }); // true

getAllGenericPasswordServices​

function getAllGenericPasswordServices(options?: GetAllOptions): Promise<string[]>;
ReturnsPromise<string[]>PromptNeverPlatformsiOSAndroid

The services that have a generic password now: the app's default service first, when the default store has credentials, then the others, sorted.

await Keychain.setGenericPassword('a', '1');
await Keychain.setGenericPassword('b', '2', { service: 'work' });
await Keychain.getAllGenericPasswordServices(); // ['com.example.app', 'work']

The package can't list every store on the device, so the other services come from a list it keeps: the item generic-services in the store apexnative.keychain.compat. setGenericPassword and the migration add a service to it; resetGenericPassword removes it. A service on the list is answered only while its credentials are still there, so credentials removed through the core API drop out too.

What this list can't know:

  • A store whose credentials were written by the core's setCredentials, not through the layer or the migration, is not listed, except the default one.
  • rnk's own items aren't listed before they are migrated: the layer reads only this package's items.

skipUIAuth is accepted and changes nothing: listing never shows a prompt.

See also​