@oxy.so/core 7.27.0 → 7.29.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.
- package/dist/cjs/crypto/index.js +19 -1
- package/dist/cjs/crypto/keyManager.js +106 -106
- package/dist/cjs/crypto/keychainGroupMigration.js +353 -0
- package/dist/cjs/crypto/keychainItems.js +70 -0
- package/dist/cjs/crypto/organizationKeys.js +80 -72
- package/dist/cjs/crypto/secureStoreOptions.js +2 -0
- package/dist/cjs/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/cjs/i18n/locales/en-US.js +14 -0
- package/dist/cjs/i18n/locales/es-ES.js +14 -0
- package/dist/cjs/session/index.js +11 -1
- package/dist/cjs/utils/keychainAccessGroups.js +248 -0
- package/dist/esm/crypto/index.js +3 -0
- package/dist/esm/crypto/keyManager.js +63 -63
- package/dist/esm/crypto/keychainGroupMigration.js +348 -0
- package/dist/esm/crypto/keychainItems.js +61 -0
- package/dist/esm/crypto/organizationKeys.js +54 -46
- package/dist/esm/crypto/secureStoreOptions.js +1 -0
- package/dist/esm/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/esm/i18n/locales/en-US.js +14 -0
- package/dist/esm/i18n/locales/es-ES.js +14 -0
- package/dist/esm/session/index.js +3 -0
- package/dist/esm/utils/keychainAccessGroups.js +231 -0
- package/dist/types/crypto/index.d.ts +4 -0
- package/dist/types/crypto/keyManager.d.ts +9 -1
- package/dist/types/crypto/keychainGroupMigration.d.ts +27 -0
- package/dist/types/crypto/keychainItems.d.ts +41 -0
- package/dist/types/crypto/organizationKeys.d.ts +11 -9
- package/dist/types/crypto/secureStoreOptions.d.ts +15 -0
- package/dist/types/i18n/locales/en-US.d.ts +14 -0
- package/dist/types/i18n/locales/es-ES.d.ts +14 -0
- package/dist/types/models/interfaces.d.ts +6 -1
- package/dist/types/session/index.d.ts +2 -0
- package/dist/types/utils/keychainAccessGroups.d.ts +104 -0
- package/package.json +2 -2
- package/src/crypto/index.ts +29 -0
- package/src/crypto/keyManager.ts +84 -99
- package/src/crypto/keychainGroupMigration.ts +440 -0
- package/src/crypto/keychainItems.ts +73 -0
- package/src/crypto/organizationKeys.ts +72 -57
- package/src/crypto/secureStoreOptions.ts +32 -0
- package/src/i18n/locales/compact/es-ES.generated.ts +1 -1
- package/src/i18n/locales/en-US.ts +14 -0
- package/src/i18n/locales/es-ES.ts +14 -0
- package/src/models/interfaces.ts +6 -0
- package/src/session/index.ts +11 -0
- package/src/utils/keychainAccessGroups.ts +308 -0
- package/src/types/expo-secure-store.d.ts +0 -21
|
@@ -695,6 +695,20 @@ const dict = {
|
|
|
695
695
|
month: 'Month',
|
|
696
696
|
year: 'Year',
|
|
697
697
|
},
|
|
698
|
+
pronouns: {
|
|
699
|
+
title: 'Pronouns',
|
|
700
|
+
subtitle: 'How people should refer to you',
|
|
701
|
+
label: 'Pronouns',
|
|
702
|
+
options: {
|
|
703
|
+
unspecified: 'Not set',
|
|
704
|
+
he_him: 'He/him',
|
|
705
|
+
she_her: 'She/her',
|
|
706
|
+
they_them: 'They/them',
|
|
707
|
+
he_they: 'He/they',
|
|
708
|
+
she_they: 'She/they',
|
|
709
|
+
it_its: 'It/its',
|
|
710
|
+
},
|
|
711
|
+
},
|
|
698
712
|
locations: {
|
|
699
713
|
title: 'Locations',
|
|
700
714
|
subtitle: "Places you've been or live",
|
|
@@ -397,6 +397,20 @@ const dict = {
|
|
|
397
397
|
month: 'Mes',
|
|
398
398
|
year: 'Año',
|
|
399
399
|
},
|
|
400
|
+
pronouns: {
|
|
401
|
+
title: 'Pronombres',
|
|
402
|
+
subtitle: 'Cómo deben referirse a ti las personas',
|
|
403
|
+
label: 'Pronombres',
|
|
404
|
+
options: {
|
|
405
|
+
unspecified: 'Sin definir',
|
|
406
|
+
he_him: 'He/him',
|
|
407
|
+
she_her: 'She/her',
|
|
408
|
+
they_them: 'They/them',
|
|
409
|
+
he_they: 'He/they',
|
|
410
|
+
she_they: 'She/they',
|
|
411
|
+
it_its: 'It/its',
|
|
412
|
+
},
|
|
413
|
+
},
|
|
400
414
|
locations: {
|
|
401
415
|
title: 'Ubicaciones',
|
|
402
416
|
subtitle: 'Lugares donde has estado o vives',
|
package/src/models/interfaces.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
ThemePreference,
|
|
8
8
|
OxyNotificationEntityType,
|
|
9
9
|
OxyNotificationType,
|
|
10
|
+
Pronouns,
|
|
10
11
|
} from '@oxy.so/contracts';
|
|
11
12
|
|
|
12
13
|
export interface OxyConfig {
|
|
@@ -154,6 +155,11 @@ export interface User {
|
|
|
154
155
|
image?: string;
|
|
155
156
|
id?: string;
|
|
156
157
|
}>;
|
|
158
|
+
/**
|
|
159
|
+
* How a person account names its pronouns, from the closed list. An account
|
|
160
|
+
* that is not a person (organization, channel, bot, …) is always `unspecified`.
|
|
161
|
+
*/
|
|
162
|
+
pronouns?: Pronouns;
|
|
157
163
|
// Social counts
|
|
158
164
|
_count?: {
|
|
159
165
|
followers?: number;
|
package/src/session/index.ts
CHANGED
|
@@ -166,6 +166,17 @@ export type {
|
|
|
166
166
|
SharedDevicePublishOutcome,
|
|
167
167
|
SharedDevicePublishSkipReason,
|
|
168
168
|
} from './sharedDeviceCredential';
|
|
169
|
+
// Where the shared device credential (and an app's own items) live on iOS:
|
|
170
|
+
// docs/identity/ios-keychain-access-groups.md, ADR 0062.
|
|
171
|
+
export {
|
|
172
|
+
KeychainAccessUnavailableError,
|
|
173
|
+
keychainAccessGroupValue,
|
|
174
|
+
ownKeychainItemPlan,
|
|
175
|
+
readThroughPlan,
|
|
176
|
+
deleteThroughPlan,
|
|
177
|
+
resolveKeychainAccess,
|
|
178
|
+
} from '../utils/keychainAccessGroups';
|
|
179
|
+
export type { KeychainAccess, KeychainGroupOption } from '../utils/keychainAccessGroups';
|
|
169
180
|
|
|
170
181
|
// Identity-bound sessions (the identity vault). The pin is the durable
|
|
171
182
|
// `{publicKey, accountId}` binding between this device's PRIMARY identity key
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iOS keychain access groups: which group each Oxy keychain item lives in, and
|
|
3
|
+
* which apps may read it (docs/identity/ios-keychain-access-groups.md, ADR 0062).
|
|
4
|
+
*
|
|
5
|
+
* | Group | Held by | Item class |
|
|
6
|
+
* |-------------------------------|------------------------------|-------------------|
|
|
7
|
+
* | `<TEAM>.so.oxy.identity` | Commons only (listed FIRST) | `identity-secret` |
|
|
8
|
+
* | `<TEAM>.so.oxy.wallet` | Commons, the Peable wallet | `spending-key` |
|
|
9
|
+
* | `<TEAM>.group.so.oxy.shared` | every Oxy app | `shared` |
|
|
10
|
+
*
|
|
11
|
+
* - `identity-secret`: the personal root (primary and backup slots), the
|
|
12
|
+
* personal and organization recovery phrases, the organization index. Only
|
|
13
|
+
* Commons may read or write them.
|
|
14
|
+
* - `spending-key`: private keys an app other than Commons signs or derives
|
|
15
|
+
* with in process — the personal key's shared slot and organization pairs
|
|
16
|
+
* (`deriveScopedSeed`, `signSocialReceive`). Commons writes them; the Peable
|
|
17
|
+
* wallet reads them.
|
|
18
|
+
* - `shared`: what every Oxy app may read: the shared public key and the
|
|
19
|
+
* device-session credential.
|
|
20
|
+
*
|
|
21
|
+
* The ROLE of this binary comes from the keychain itself: an item written
|
|
22
|
+
* without a group lands in the app's default group, the first entry of its
|
|
23
|
+
* `keychain-access-groups` entitlement, and `@oxy.so/app-preset` lets only
|
|
24
|
+
* Commons put the identity group first and only the Peable wallet the wallet
|
|
25
|
+
* group. The same probe gives the team prefix, which `kSecAttrAccessGroup`
|
|
26
|
+
* needs and JavaScript cannot otherwise see.
|
|
27
|
+
*
|
|
28
|
+
* Android and web have no access groups: there every helper here answers the
|
|
29
|
+
* single-store behaviour (`ungrouped`), and Android's narrowing is Commons'
|
|
30
|
+
* identity host. An iOS build whose native module predates the probe is also
|
|
31
|
+
* `ungrouped` — exactly what that build's entitlements (one shared group) do.
|
|
32
|
+
*
|
|
33
|
+
* This module never imports the identity stack: services' device-session store
|
|
34
|
+
* and Peable's wallet storage use it too.
|
|
35
|
+
*/
|
|
36
|
+
import { type KeychainDefaultAccessGroup, loadKeychainDefaultAccessGroup } from '@oxy.so/protocol';
|
|
37
|
+
import { isIOS } from './platform';
|
|
38
|
+
|
|
39
|
+
/** The group names after the team prefix, exactly as the entitlements list them. */
|
|
40
|
+
export const KEYCHAIN_ACCESS_GROUPS = {
|
|
41
|
+
identity: 'so.oxy.identity',
|
|
42
|
+
wallet: 'so.oxy.wallet',
|
|
43
|
+
shared: 'group.so.oxy.shared',
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
export type KeychainAccessGroupName = keyof typeof KEYCHAIN_ACCESS_GROUPS;
|
|
47
|
+
|
|
48
|
+
/** What a keychain item IS, which decides its group. See the table above. */
|
|
49
|
+
export type KeychainItemClass = 'identity-secret' | 'spending-key' | 'shared';
|
|
50
|
+
|
|
51
|
+
/** Which holder this binary is, from its default access group. */
|
|
52
|
+
export type KeychainRole = 'holder' | 'wallet' | 'member';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* - `grouped`: iOS with the probe answered. `prefix` ends with its dot
|
|
56
|
+
* (`ABCDE12345.`); `defaultGroup` is the full default group string.
|
|
57
|
+
* - `ungrouped`: Android, web, or an iOS native build without the probe.
|
|
58
|
+
*/
|
|
59
|
+
export type KeychainAccess =
|
|
60
|
+
| { mode: 'grouped'; prefix: string; defaultGroup: string; role: KeychainRole }
|
|
61
|
+
| { mode: 'ungrouped' };
|
|
62
|
+
|
|
63
|
+
/** The `expo-secure-store` option this module sets (its real name since 58). */
|
|
64
|
+
export interface KeychainGroupOption {
|
|
65
|
+
accessGroup?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Thrown while the probe cannot answer (before first unlock). Transient. */
|
|
69
|
+
export class KeychainAccessUnavailableError extends Error {
|
|
70
|
+
override readonly name = 'KeychainAccessUnavailableError';
|
|
71
|
+
constructor(readonly cause?: unknown) {
|
|
72
|
+
super('The keychain cannot say which access groups this app holds right now.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The single place each item class is assigned a group. A new kind of item
|
|
78
|
+
* (F1's retained keys, for one) picks its class and reuses everything below.
|
|
79
|
+
*/
|
|
80
|
+
export function keychainGroupFor(item: KeychainItemClass): KeychainAccessGroupName {
|
|
81
|
+
switch (item) {
|
|
82
|
+
case 'identity-secret':
|
|
83
|
+
return 'identity';
|
|
84
|
+
case 'spending-key':
|
|
85
|
+
return 'wallet';
|
|
86
|
+
case 'shared':
|
|
87
|
+
return 'shared';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Whether `role` holds the group `item` lives in. */
|
|
92
|
+
export function mayHoldKeychainItem(role: KeychainRole, item: KeychainItemClass): boolean {
|
|
93
|
+
switch (role) {
|
|
94
|
+
case 'holder':
|
|
95
|
+
return true;
|
|
96
|
+
case 'wallet':
|
|
97
|
+
return item !== 'identity-secret';
|
|
98
|
+
case 'member':
|
|
99
|
+
return item === 'shared';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Parse the default access group the probe reported. Exported for tests. */
|
|
104
|
+
export function keychainAccessFromDefaultGroup(defaultGroup: string): KeychainAccess {
|
|
105
|
+
const dot = defaultGroup.indexOf('.');
|
|
106
|
+
if (dot <= 0 || dot === defaultGroup.length - 1) {
|
|
107
|
+
// Not `<prefix>.<group>`: an unsigned simulator build. Nothing to split.
|
|
108
|
+
return { mode: 'ungrouped' };
|
|
109
|
+
}
|
|
110
|
+
const prefix = defaultGroup.slice(0, dot + 1);
|
|
111
|
+
const group = defaultGroup.slice(dot + 1);
|
|
112
|
+
const role: KeychainRole =
|
|
113
|
+
group === KEYCHAIN_ACCESS_GROUPS.identity
|
|
114
|
+
? 'holder'
|
|
115
|
+
: group === KEYCHAIN_ACCESS_GROUPS.wallet
|
|
116
|
+
? 'wallet'
|
|
117
|
+
: 'member';
|
|
118
|
+
return { mode: 'grouped', prefix, defaultGroup, role };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let resolvedAccess: KeychainAccess | null = null;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* This binary's keychain access, memoized once known. THROWS
|
|
125
|
+
* {@link KeychainAccessUnavailableError} while the probe cannot answer, which
|
|
126
|
+
* every caller already treats as "storage unavailable, retry".
|
|
127
|
+
*/
|
|
128
|
+
export async function resolveKeychainAccess(): Promise<KeychainAccess> {
|
|
129
|
+
if (resolvedAccess) {
|
|
130
|
+
return resolvedAccess;
|
|
131
|
+
}
|
|
132
|
+
if (!isIOS()) {
|
|
133
|
+
// Not memoized: until `@oxy.so/services` registers the platform, a phone
|
|
134
|
+
// reads as "not iOS", and caching that would keep the whole process on the
|
|
135
|
+
// pre-split behaviour.
|
|
136
|
+
return { mode: 'ungrouped' };
|
|
137
|
+
}
|
|
138
|
+
let probe: KeychainDefaultAccessGroup;
|
|
139
|
+
try {
|
|
140
|
+
probe = await loadKeychainDefaultAccessGroup();
|
|
141
|
+
} catch (cause) {
|
|
142
|
+
throw new KeychainAccessUnavailableError(cause);
|
|
143
|
+
}
|
|
144
|
+
if (probe.state === 'unavailable') {
|
|
145
|
+
throw new KeychainAccessUnavailableError(probe.cause);
|
|
146
|
+
}
|
|
147
|
+
resolvedAccess =
|
|
148
|
+
probe.state === 'resolved'
|
|
149
|
+
? keychainAccessFromDefaultGroup(probe.accessGroup)
|
|
150
|
+
: { mode: 'ungrouped' };
|
|
151
|
+
return resolvedAccess;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Forget the memoized access. Tests only. @internal */
|
|
155
|
+
export function resetKeychainAccessForTests(): void {
|
|
156
|
+
resolvedAccess = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** The full `kSecAttrAccessGroup` value of `group`, or `undefined` when ungrouped. */
|
|
160
|
+
export function keychainAccessGroupValue(
|
|
161
|
+
access: KeychainAccess,
|
|
162
|
+
group: KeychainAccessGroupName,
|
|
163
|
+
): string | undefined {
|
|
164
|
+
return access.mode === 'grouped' ? `${access.prefix}${KEYCHAIN_ACCESS_GROUPS[group]}` : undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function option(value: string | undefined): KeychainGroupOption {
|
|
168
|
+
return value === undefined ? {} : { accessGroup: value };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The option bags a READ of `item` tries, in order: its own group, then the
|
|
173
|
+
* shared group, where every item lived before the split (release 4 of the doc
|
|
174
|
+
* drops that fallback). Empty when this app may not hold the item: the caller
|
|
175
|
+
* answers `null` without touching the keychain.
|
|
176
|
+
*/
|
|
177
|
+
export function keychainReadPlan(
|
|
178
|
+
access: KeychainAccess,
|
|
179
|
+
item: KeychainItemClass,
|
|
180
|
+
): KeychainGroupOption[] {
|
|
181
|
+
if (access.mode === 'ungrouped') {
|
|
182
|
+
return [{}];
|
|
183
|
+
}
|
|
184
|
+
if (!mayHoldKeychainItem(access.role, item)) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const own = keychainGroupFor(item);
|
|
188
|
+
const plan = [option(keychainAccessGroupValue(access, own))];
|
|
189
|
+
if (own !== 'shared') {
|
|
190
|
+
plan.push(option(keychainAccessGroupValue(access, 'shared')));
|
|
191
|
+
}
|
|
192
|
+
return plan;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Every place a DELETE of `item` must reach: the read plan, so a delete never
|
|
197
|
+
* leaves a pre-split copy behind in the shared group.
|
|
198
|
+
*/
|
|
199
|
+
export function keychainDeletePlan(
|
|
200
|
+
access: KeychainAccess,
|
|
201
|
+
item: KeychainItemClass,
|
|
202
|
+
): KeychainGroupOption[] {
|
|
203
|
+
return keychainReadPlan(access, item);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The option a WRITE of `item` carries. THROWS when this app may not hold the
|
|
208
|
+
* item: an identity secret written by any app but Commons would land in that
|
|
209
|
+
* app's default group, a second holder nobody knows about.
|
|
210
|
+
*/
|
|
211
|
+
export function keychainWriteOption(
|
|
212
|
+
access: KeychainAccess,
|
|
213
|
+
item: KeychainItemClass,
|
|
214
|
+
): KeychainGroupOption {
|
|
215
|
+
if (access.mode === 'ungrouped') {
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
if (!mayHoldKeychainItem(access.role, item)) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`This app (keychain role "${access.role}") may not hold a ${item} keychain item; only Commons writes the identity.`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return option(keychainAccessGroupValue(access, keychainGroupFor(item)));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The plan for an app's OWN items (its session credential, its settings, an
|
|
228
|
+
* imported wallet's phrase) in Commons and the Peable wallet, whose default
|
|
229
|
+
* group the split changed: written to the default group explicitly — where a
|
|
230
|
+
* write without a group goes anyway — and read from it FIRST, then from every
|
|
231
|
+
* group (`{}`), where all of them were written before. Without the explicit
|
|
232
|
+
* order a read could return whichever of two copies the keychain met first.
|
|
233
|
+
* Deletes reach every group, as they always did.
|
|
234
|
+
*
|
|
235
|
+
* Every other app (`member`) and Android/web keep the calls they always made.
|
|
236
|
+
*/
|
|
237
|
+
export function ownKeychainItemPlan(access: KeychainAccess): {
|
|
238
|
+
read: KeychainGroupOption[];
|
|
239
|
+
write: KeychainGroupOption;
|
|
240
|
+
delete: KeychainGroupOption[];
|
|
241
|
+
} {
|
|
242
|
+
if (access.mode === 'ungrouped' || access.role === 'member') {
|
|
243
|
+
return { read: [{}], write: {}, delete: [{}] };
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
read: [option(access.defaultGroup), {}],
|
|
247
|
+
write: option(access.defaultGroup),
|
|
248
|
+
delete: [{}],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** The subset of `expo-secure-store` a grouped store wraps. */
|
|
253
|
+
export interface KeychainStoreLike {
|
|
254
|
+
getItemAsync(key: string, options?: object): Promise<string | null>;
|
|
255
|
+
setItemAsync(key: string, value: string, options?: object): Promise<void>;
|
|
256
|
+
deleteItemAsync(key: string, options?: object): Promise<void>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Read `key` through `plan`: the first non-null answer. A thrown read propagates. */
|
|
260
|
+
export async function readThroughPlan(
|
|
261
|
+
store: KeychainStoreLike,
|
|
262
|
+
key: string,
|
|
263
|
+
options: object,
|
|
264
|
+
plan: readonly KeychainGroupOption[],
|
|
265
|
+
): Promise<string | null> {
|
|
266
|
+
for (const group of plan) {
|
|
267
|
+
const value = await store.getItemAsync(key, { ...options, ...group });
|
|
268
|
+
if (value !== null && value !== undefined) {
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Delete `key` from every location in `plan`. A thrown delete propagates. */
|
|
276
|
+
export async function deleteThroughPlan(
|
|
277
|
+
store: KeychainStoreLike,
|
|
278
|
+
key: string,
|
|
279
|
+
options: object,
|
|
280
|
+
plan: readonly KeychainGroupOption[],
|
|
281
|
+
): Promise<void> {
|
|
282
|
+
for (const group of plan) {
|
|
283
|
+
await store.deleteItemAsync(key, { ...options, ...group });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* `store` with every call routed to `item`'s groups for this app: reads walk
|
|
289
|
+
* the read plan, writes carry the write option (throwing for a role that may
|
|
290
|
+
* not hold the item), deletes reach every location. The constants of the
|
|
291
|
+
* wrapped store are kept, so `store.WHEN_UNLOCKED_THIS_DEVICE_ONLY` still reads.
|
|
292
|
+
*/
|
|
293
|
+
export function groupedKeychainStore<S extends KeychainStoreLike>(
|
|
294
|
+
store: S,
|
|
295
|
+
access: KeychainAccess,
|
|
296
|
+
item: KeychainItemClass,
|
|
297
|
+
): S {
|
|
298
|
+
const plan = keychainReadPlan(access, item);
|
|
299
|
+
const wrapped: KeychainStoreLike = {
|
|
300
|
+
getItemAsync: (key, options) => readThroughPlan(store, key, options ?? {}, plan),
|
|
301
|
+
setItemAsync: async (key, value, options) => {
|
|
302
|
+
const group = keychainWriteOption(access, item);
|
|
303
|
+
await store.setItemAsync(key, value, { ...(options ?? {}), ...group });
|
|
304
|
+
},
|
|
305
|
+
deleteItemAsync: (key, options) => deleteThroughPlan(store, key, options ?? {}, plan),
|
|
306
|
+
};
|
|
307
|
+
return Object.assign(Object.create(store) as S, wrapped);
|
|
308
|
+
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
declare module 'expo-secure-store' {
|
|
2
|
-
export interface SecureStoreOptions {
|
|
3
|
-
keychainAccessible?: number;
|
|
4
|
-
keychainAccessGroup?: string;
|
|
5
|
-
keychainService?: string;
|
|
6
|
-
requireAuthentication?: boolean;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const WHEN_UNLOCKED: number;
|
|
10
|
-
export const AFTER_FIRST_UNLOCK: number;
|
|
11
|
-
export const WHEN_UNLOCKED_THIS_DEVICE_ONLY: number;
|
|
12
|
-
export const AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: number;
|
|
13
|
-
|
|
14
|
-
export function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
|
|
15
|
-
export function setItemAsync(
|
|
16
|
-
key: string,
|
|
17
|
-
value: string,
|
|
18
|
-
options?: SecureStoreOptions,
|
|
19
|
-
): Promise<void>;
|
|
20
|
-
export function deleteItemAsync(key: string, options?: SecureStoreOptions): Promise<void>;
|
|
21
|
-
}
|