@chipmobilesdk/rn-auth 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,10 +4,14 @@
4
4
  * Refresh tokens and the session-restore artifact persist ONLY through this
5
5
  * port, backed by platform secure storage (iOS Keychain / Android Keystore).
6
6
  * Access tokens are never written here — they stay in memory (see accessToken).
7
- * The default adapter lazily requires the native module so unit tests that
8
- * inject a fake never load native code.
7
+ * The default adapter loads the native module on its first read or write, so
8
+ * creating it, and unit tests that inject a fake, load no native code.
9
9
  */
10
10
 
11
+ import { lazyPeer } from '@chipmobilesdk/rn-core';
12
+
13
+ import { AuthConfigError } from '../diagnostics/errors';
14
+
11
15
  /** Single key under which the persisted session artifact is stored. */
12
16
  export const AUTH_STORAGE_KEY = '@chipmobilesdk/rn-auth/session';
13
17
 
@@ -29,23 +33,53 @@ export interface KeychainSecureStoreOptions {
29
33
  export function createKeychainSecureStore(
30
34
  options: KeychainSecureStoreOptions = {},
31
35
  ): SecureTokenStore {
32
- // Lazy require: only loaded when the default adapter is actually used.
33
- const Keychain = require('react-native-keychain');
34
- const accessible = options.accessible ?? Keychain.ACCESSIBLE?.WHEN_UNLOCKED_THIS_DEVICE_ONLY;
36
+ // The native module loads on the first read or write, never at startup (PERF-05).
37
+ const accessible = () =>
38
+ options.accessible ?? keychain().ACCESSIBLE?.WHEN_UNLOCKED_THIS_DEVICE_ONLY;
35
39
 
36
40
  return {
37
41
  async getItem(key) {
38
- const result = await Keychain.getGenericPassword({ service: key });
42
+ const result = await keychain().getGenericPassword({ service: key });
39
43
  return result ? result.password : null;
40
44
  },
41
45
  async setItem(key, value) {
42
- await Keychain.setGenericPassword('token', value, {
46
+ await keychain().setGenericPassword('token', value, {
43
47
  service: key,
44
- accessible,
48
+ accessible: accessible(),
45
49
  });
46
50
  },
47
51
  async removeItem(key) {
48
- await Keychain.resetGenericPassword({ service: key });
52
+ await keychain().resetGenericPassword({ service: key });
49
53
  },
50
54
  };
51
55
  }
56
+
57
+ interface KeychainModule {
58
+ ACCESSIBLE?: { WHEN_UNLOCKED_THIS_DEVICE_ONLY?: string };
59
+ getGenericPassword(options: { service: string }): Promise<false | { password: string }>;
60
+ setGenericPassword(
61
+ username: string,
62
+ password: string,
63
+ options: { service: string; accessible: string | undefined },
64
+ ): Promise<unknown>;
65
+ resetGenericPassword(options: { service: string }): Promise<unknown>;
66
+ }
67
+
68
+ const keychainPeer = lazyPeer('react-native-keychain', () => {
69
+ // Inside `try`, so Metro bundles an app that leaves this optional peer out.
70
+ try {
71
+ return require('react-native-keychain') as KeychainModule;
72
+ } catch (cause) {
73
+ throw cause;
74
+ }
75
+ });
76
+
77
+ function keychain(): KeychainModule {
78
+ return keychainPeer.require(
79
+ peer =>
80
+ new AuthConfigError(
81
+ 'auth.error.configuration',
82
+ `${peer} is not installed or its native module is not linked`,
83
+ ),
84
+ );
85
+ }