@turnkey/sdk-react-native
Version:
React Native SDK
270 lines (267 loc) • 9.29 kB
JavaScript
import * as Keychain from 'react-native-keychain';
import { TurnkeyReactNativeError } from './errors.mjs';
import { StorageKeys } from './constants.mjs';
// This package leverages `react-native-keychain` to securely store session data.
// We use `Keychain.getGenericPassword()` to retrieve stored values, where:
// - `service` is used as a key to reference the stored data.
// - `credentials.password` is used to hold sensitive data, such as session keys or encrypted session objects.
//
// Note: `credentials.password` does not need to contain a literal password;
// it is simply a secure storage location for sensitive string data.
/**
* Retrieves the embedded key from secure storage.
*
* - Attempts to retrieve the stored embedded private key using Keychain.
* - Optionally deletes the key from storage after retrieval if `deleteKey` is true.
*
* @param deleteKey - Whether to remove the embedded key after retrieval (defaults to false).
* @param storageKey - The service key for the embedded key.
* @returns The embedded private key if found, otherwise null.
* @throws {TurnkeyReactNativeError} If retrieving or deleting the key fails.
*/
const getEmbeddedKey = async (deleteKey = false, storageKey) => {
try {
const credentials = await Keychain.getGenericPassword({
service: storageKey,
});
if (credentials) {
if (deleteKey) {
await Keychain.resetGenericPassword({
service: storageKey,
});
}
return credentials.password;
}
return null;
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to get embedded key", error);
}
};
/**
* Saves the provided embedded key (private key) securely in storage.
*
* - Uses Keychain to store the private key for the given service key.
*
* @param key - The private key to store securely.
* @param storageKey - The service key under which to store the embedded key.
* @throws {TurnkeyReactNativeError} If saving the key fails.
*/
const saveEmbeddedKey = async (key, storageKey) => {
try {
await Keychain.setGenericPassword(storageKey, key, {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
service: storageKey,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Could not save the embedded key.", error);
}
};
/**
* Retrieves a stored session from secure storage.
*
* @param sessionKey The unique key identifying the session.
* @returns The session object if found, otherwise `null`.
* @throws If retrieving the session fails.
*/
const getSession = async (sessionKey) => {
try {
const credentials = await Keychain.getGenericPassword({
service: sessionKey,
});
return credentials ? JSON.parse(credentials.password) : null;
}
catch (error) {
throw new TurnkeyReactNativeError(`Failed to get session for sessionKey "${sessionKey}"`, error);
}
};
/**
* Saves a session securely in storage.
*
* @param session The session object to store securely.
* @param sessionKey The unique key under which the session is stored.
* @throws If saving the session fails.
*/
const saveSession = async (session, sessionKey) => {
try {
await Keychain.setGenericPassword(sessionKey, JSON.stringify(session), {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
service: sessionKey,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Could not save the session", error);
}
};
/**
* Deletes a session from secure storage.
*
* @param sessionKey The unique key identifying the session to reset.
* @throws If deleting the session fails.
*/
const deleteSession = async (sessionKey) => {
try {
await Keychain.resetGenericPassword({ service: sessionKey });
}
catch (error) {
throw new TurnkeyReactNativeError("Could not delete the session.", error);
}
};
/**
* Retrieves the selected session key from secure storage.
*
* @returns The selected session key as a string, or `null` if not found.
* @throws If retrieving the session key fails.
*/
const getSelectedSessionKey = async () => {
try {
const credentials = await Keychain.getGenericPassword({
service: StorageKeys.SelectedSession,
});
return credentials ? credentials.password : null;
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to get selected session key", error);
}
};
/**
* Saves the selected session key to secure storage.
*
* @param sessionKey The session key to mark as selected.
* @throws If saving the session key fails.
*/
const saveSelectedSessionKey = async (sessionKey) => {
try {
await Keychain.setGenericPassword(StorageKeys.SelectedSession, sessionKey, {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
service: StorageKeys.SelectedSession,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to save selected session key", error);
}
};
/**
* Clears the selected session key from secure storage.
*
* @throws If deleting the session key fails.
*/
const clearSelectedSessionKey = async () => {
try {
await Keychain.resetGenericPassword({
service: StorageKeys.SelectedSession,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to clear selected session key", error);
}
};
/**
* Adds a session key to the session list in secure storage.
*
* - Retrieves the existing session list.
* - Appends the new session key if it does not already exist.
* - Stores the updated session list.
*
* @param sessionKey The session key to add.
* @throws If the session key already exists or saving fails.
*/
const addSessionKey = async (sessionKey) => {
try {
const credentials = await Keychain.getGenericPassword({
service: StorageKeys.SessionKeys,
});
let keys = [];
if (credentials) {
try {
keys = JSON.parse(credentials.password);
if (!Array.isArray(keys)) {
throw new Error("Session list is corrupted.");
}
}
catch {
throw new TurnkeyReactNativeError("Failed to parse session list.");
}
}
if (keys.includes(sessionKey)) {
return;
}
keys.push(sessionKey);
await Keychain.setGenericPassword(StorageKeys.SessionKeys, JSON.stringify(keys), {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
service: StorageKeys.SessionKeys,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to add session key.", error);
}
};
/**
* Retrieves all session keys stored in secure storage.
*
* @returns An array of session keys.
* @throws If retrieving the session list fails.
*/
const getSessionKeys = async () => {
try {
const credentials = await Keychain.getGenericPassword({
service: StorageKeys.SessionKeys,
});
if (!credentials) {
return [];
}
try {
const keys = JSON.parse(credentials.password);
if (!Array.isArray(keys)) {
throw new Error("Session list is corrupted.");
}
return keys;
}
catch {
throw new TurnkeyReactNativeError("Failed to parse session list.");
}
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to retrieve session list.", error);
}
};
/**
* Removes a session key from the session list in secure storage.
*
* - Fetches the existing session list.
* - Removes the specified session key.
* - Saves the updated session list back to secure storage.
*
* @param sessionKey The session key to remove.
* @throws If removing the session key fails.
*/
const removeSessionKey = async (sessionKey) => {
try {
const credentials = await Keychain.getGenericPassword({
service: StorageKeys.SessionKeys,
});
let keys = [];
if (credentials) {
try {
keys = JSON.parse(credentials.password);
if (!Array.isArray(keys)) {
throw new Error("Session list is corrupted.");
}
}
catch {
throw new TurnkeyReactNativeError("Failed to parse session list.");
}
}
const updatedKeys = keys.filter((key) => key !== sessionKey);
await Keychain.setGenericPassword(StorageKeys.SessionKeys, JSON.stringify(updatedKeys), {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
service: StorageKeys.SessionKeys,
});
}
catch (error) {
throw new TurnkeyReactNativeError("Failed to remove session key.", error);
}
};
export { addSessionKey, clearSelectedSessionKey, deleteSession, getEmbeddedKey, getSelectedSessionKey, getSession, getSessionKeys, removeSessionKey, saveEmbeddedKey, saveSelectedSessionKey, saveSession };
//# sourceMappingURL=storage.mjs.map