Skip to content

Commit f554332

Browse files
committed
feat: Enhance security options with biometric and strongbox support in storage functions
1 parent 0310140 commit f554332

3 files changed

Lines changed: 183 additions & 8 deletions

File tree

src/SensitiveInfo.nitro.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,23 @@ import type { HybridObject } from 'react-native-nitro-modules';
33
/**
44
* Security level for storage operations.
55
*/
6+
/**
7+
* Available security levels for storage operations.
8+
*/
69
export type SecurityLevel = 'standard' | 'biometric' | 'strongbox';
710

11+
/**
12+
* Constant helper for security levels with excellent IDE completion.
13+
*
14+
* Example:
15+
* setItem(key, value, { securityLevel: SecurityLevels.Biometric })
16+
*/
17+
export const SecurityLevels = {
18+
Standard: 'standard',
19+
Biometric: 'biometric',
20+
StrongBox: 'strongbox',
21+
} as const;
22+
823
/**
924
* Biometric authentication options.
1025
*/
@@ -24,13 +39,49 @@ export interface BiometricOptions {
2439
/**
2540
* Options for storage operations.
2641
*/
42+
/**
43+
* Options for storage operations when NOT using biometrics explicitly.
44+
* If provided, `securityLevel` can be 'standard' or 'strongbox'.
45+
* Note: `biometricOptions` are not accepted in this branch and will be a type error.
46+
*/
2747
export interface StorageOptions {
2848
/** Security level for the operation */
2949
securityLevel?: SecurityLevel;
3050
/** Biometric authentication options (when securityLevel is 'biometric') */
3151
biometricOptions?: BiometricOptions;
3252
}
3353

54+
/**
55+
* Narrowed variants for better TS DX (not used by Nitro codegen directly).
56+
*/
57+
export interface StandardOrStrongBoxOptions {
58+
securityLevel?: Exclude<SecurityLevel, 'biometric'>;
59+
biometricOptions?: never;
60+
}
61+
62+
export interface BiometricStorageOptions {
63+
securityLevel: 'biometric';
64+
biometricOptions?: BiometricOptions;
65+
}
66+
67+
/**
68+
* Small helpers to build strongly‑typed options with great autocompletion.
69+
*/
70+
export const withStandard = (): StandardOrStrongBoxOptions => ({
71+
securityLevel: SecurityLevels.Standard,
72+
});
73+
74+
export const withStrongBox = (): StandardOrStrongBoxOptions => ({
75+
securityLevel: SecurityLevels.StrongBox,
76+
});
77+
78+
export const withBiometrics = (
79+
biometricOptions?: BiometricOptions
80+
): BiometricStorageOptions => ({
81+
securityLevel: SecurityLevels.Biometric,
82+
biometricOptions,
83+
});
84+
3485
/**
3586
* Nitro hybrid object interface for secure storage APIs.
3687
*/

src/hooks/useSensitiveInfo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ export interface StoredItem {
2222
truncated?: boolean;
2323
}
2424

25-
interface SecurityCapabilities {
25+
export interface SecurityCapabilities {
2626
biometric: boolean;
2727
strongbox: boolean;
2828
hardwareSecurityModule: boolean;
2929
}
3030

31-
interface UseSensitiveInfoReturn {
31+
export interface UseSensitiveInfoReturn {
3232
storedItems: StoredItem[];
3333
isLoading: boolean;
3434
lastOperation: string;

src/index.tsx

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,34 @@ import type {
44
BiometricPromptMethods,
55
BiometricPromptProps,
66
} from './BiometricPromptView.nitro';
7-
import type { SensitiveInfo, StorageOptions } from './SensitiveInfo.nitro';
7+
import type {
8+
SensitiveInfo,
9+
StorageOptions,
10+
BiometricStorageOptions,
11+
StandardOrStrongBoxOptions,
12+
SecurityLevel,
13+
} from './SensitiveInfo.nitro';
14+
import { withBiometrics, withStrongBox } from './SensitiveInfo.nitro';
815

916
const SensitiveInfoHybridObject =
1017
NitroModules.createHybridObject<SensitiveInfo>('SensitiveInfo');
1118

1219
/**
1320
* Get a stored value by key.
1421
*/
22+
export function getItem(key: string): Promise<string | null>;
23+
export function getItem(
24+
key: string,
25+
options: StandardOrStrongBoxOptions
26+
): Promise<string | null>;
27+
export function getItem(
28+
key: string,
29+
options: BiometricStorageOptions
30+
): Promise<string | null>;
31+
export function getItem(
32+
key: string,
33+
options?: StorageOptions
34+
): Promise<string | null>;
1535
export function getItem(
1636
key: string,
1737
options?: StorageOptions
@@ -22,6 +42,22 @@ export function getItem(
2242
/**
2343
* Store a value under the specified key.
2444
*/
45+
export function setItem(key: string, value: string): Promise<void>;
46+
export function setItem(
47+
key: string,
48+
value: string,
49+
options: StandardOrStrongBoxOptions
50+
): Promise<void>;
51+
export function setItem(
52+
key: string,
53+
value: string,
54+
options: BiometricStorageOptions
55+
): Promise<void>;
56+
export function setItem(
57+
key: string,
58+
value: string,
59+
options?: StorageOptions
60+
): Promise<void>;
2561
export function setItem(
2662
key: string,
2763
value: string,
@@ -33,6 +69,19 @@ export function setItem(
3369
/**
3470
* Remove the value for the given key.
3571
*/
72+
export function removeItem(key: string): Promise<void>;
73+
export function removeItem(
74+
key: string,
75+
options: StandardOrStrongBoxOptions
76+
): Promise<void>;
77+
export function removeItem(
78+
key: string,
79+
options: BiometricStorageOptions
80+
): Promise<void>;
81+
export function removeItem(
82+
key: string,
83+
options?: StorageOptions
84+
): Promise<void>;
3685
export function removeItem(
3786
key: string,
3887
options?: StorageOptions
@@ -43,6 +92,16 @@ export function removeItem(
4392
/**
4493
* Retrieve all stored key-value pairs.
4594
*/
95+
export function getAllItems(): Promise<Record<string, string>>;
96+
export function getAllItems(
97+
options: StandardOrStrongBoxOptions
98+
): Promise<Record<string, string>>;
99+
export function getAllItems(
100+
options: BiometricStorageOptions
101+
): Promise<Record<string, string>>;
102+
export function getAllItems(
103+
options?: StorageOptions
104+
): Promise<Record<string, string>>;
46105
export function getAllItems(
47106
options?: StorageOptions
48107
): Promise<Record<string, string>> {
@@ -52,6 +111,10 @@ export function getAllItems(
52111
/**
53112
* Clear all stored items.
54113
*/
114+
export function clear(): Promise<void>;
115+
export function clear(options: StandardOrStrongBoxOptions): Promise<void>;
116+
export function clear(options: BiometricStorageOptions): Promise<void>;
117+
export function clear(options?: StorageOptions): Promise<void>;
55118
export function clear(options?: StorageOptions): Promise<void> {
56119
return SensitiveInfoHybridObject.clear(options);
57120
}
@@ -74,17 +137,19 @@ export function isStrongBoxAvailable(): Promise<boolean> {
74137
* Get the available security capabilities of the device.
75138
* This helps determine what security levels are actually supported.
76139
*/
77-
export async function getSecurityCapabilities(): Promise<{
140+
export type SecurityCapabilities = {
78141
biometric: boolean;
79142
strongbox: boolean;
80-
recommendedLevel: 'standard' | 'biometric' | 'strongbox';
81-
}> {
143+
recommendedLevel: SecurityLevel;
144+
};
145+
146+
export async function getSecurityCapabilities(): Promise<SecurityCapabilities> {
82147
const [biometric, strongbox] = await Promise.all([
83148
isBiometricAvailable(),
84149
isStrongBoxAvailable(),
85150
]);
86151

87-
let recommendedLevel: 'standard' | 'biometric' | 'strongbox' = 'standard';
152+
let recommendedLevel: SecurityLevel = 'standard';
88153

89154
if (strongbox) {
90155
recommendedLevel = 'strongbox';
@@ -101,7 +166,11 @@ export async function getSecurityCapabilities(): Promise<{
101166

102167
// Export React hooks
103168
export { useSensitiveInfo } from './hooks/useSensitiveInfo';
104-
export type { StoredItem } from './hooks/useSensitiveInfo';
169+
export type {
170+
StoredItem,
171+
UseSensitiveInfoReturn,
172+
SecurityCapabilities as HookSecurityCapabilities,
173+
} from './hooks/useSensitiveInfo';
105174
export { BiometricAuthenticator } from './utils/BiometricAuthenticator';
106175

107176
// Hybrid View: BiometricPromptView
@@ -137,3 +206,58 @@ export type {
137206
SecurityLevel,
138207
BiometricOptions,
139208
} from './SensitiveInfo.nitro';
209+
210+
export {
211+
SecurityLevels,
212+
withBiometrics,
213+
withStrongBox,
214+
withStandard,
215+
} from './SensitiveInfo.nitro';
216+
217+
// Ergonomic wrappers (opt-in) — avoids creating option objects on the call-site
218+
export function setItemBiometric(
219+
key: string,
220+
value: string,
221+
biometric?: BiometricStorageOptions['biometricOptions']
222+
): Promise<void> {
223+
return setItem(key, value, withBiometrics(biometric));
224+
}
225+
226+
export function getItemBiometric(
227+
key: string,
228+
biometric?: BiometricStorageOptions['biometricOptions']
229+
): Promise<string | null> {
230+
return getItem(key, withBiometrics(biometric));
231+
}
232+
233+
export function removeItemBiometric(
234+
key: string,
235+
biometric?: BiometricStorageOptions['biometricOptions']
236+
): Promise<void> {
237+
return removeItem(key, withBiometrics(biometric));
238+
}
239+
240+
export function setItemStrongBox(key: string, value: string): Promise<void> {
241+
return setItem(key, value, withStrongBox());
242+
}
243+
244+
export function getItemStrongBox(key: string): Promise<string | null> {
245+
return getItem(key, withStrongBox());
246+
}
247+
248+
export function removeItemStrongBox(key: string): Promise<void> {
249+
return removeItem(key, withStrongBox());
250+
}
251+
252+
export function getAllItemsStrongBox(): Promise<Record<string, string>> {
253+
return getAllItems(withStrongBox());
254+
}
255+
256+
export function clearStrongBox(): Promise<void> {
257+
return clear(withStrongBox());
258+
}
259+
260+
export type {
261+
BiometricStorageOptions,
262+
StandardOrStrongBoxOptions,
263+
} from './SensitiveInfo.nitro';

0 commit comments

Comments
 (0)