Skip to content

Commit 85978a1

Browse files
authored
feat(secure-storage): Add Support for Access/Application groups on IOS.
Allow the passing of the access group identifier so that secrets can be shared between apps/widgets etc.
1 parent 1f84eea commit 85978a1

File tree

5 files changed

+155
-1
lines changed

5 files changed

+155
-1
lines changed

packages/secure-storage/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,40 @@ Currently this plugin defaults to using `NSUserDefaults` on **iOS Simulators**.
117117

118118
If you're running into issues similar to [issue_10](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/10), consider using the default behaviour again.
119119

120+
## iOS Keychain Access/App Groups
121+
122+
You can share secrets between iOS apps/extensions via Keychain access groups, or App Groups, see [here](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps#) for details.
123+
124+
To setup:
125+
126+
* Add a keychain access group entitlement to your app
127+
by adding an entry in the ```app/App_Resources/iOS/<someName>.entitlements``` file.
128+
129+
e.g.
130+
```xml
131+
<key>keychain-access-groups</key>
132+
<array>
133+
<string>$(AppIdentifierPrefix)com.my.app.sharedgroup</string>
134+
</array>
135+
```
136+
* Then in your app specify the ```accessGroup``` property when getting/setting values.
137+
e.g.
138+
139+
```typescript
140+
import { SecureStorage } from "nativescript-secure-storage";
141+
export class MyComponent {
142+
secureStorage = new SecureStorage();
143+
// a method that can be called from your view
144+
setSecureValue() {
145+
this.secureStorage.set({
146+
accessGroup:"<TeamID>.com.my.app.sharedgroup",
147+
key: 'myKey',
148+
value: 'my value'
149+
}).then(success => { console.log(success)});
150+
}
151+
}
152+
```
153+
120154
## Credits
121155
* On __iOS__ we're leveraging the KeyChain using the [SAMKeychain](https://github.com/soffes/SAMKeychain) library (on the Simulator `NSUserDefaults`),
122156
* On __Android__ we're using [Hawk](https://github.com/orhanobut/hawk) library which internally uses [Facebook conceal](https://github.com/facebook/conceal).

packages/secure-storage/common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { ApplicationSettings } from '@nativescript/core';
22

33
export interface SetOptions {
4+
accessGroup?: string;
45
service?: string;
56
key: string;
67
value: string;
78
}
89

910
export interface GetOptions {
11+
accessGroup?: string;
1012
service?: string;
1113
key: string;
1214
}
1315

1416
export interface RemoveOptions {
17+
accessGroup?: string;
1518
service?: string;
1619
key: string;
1720
}

packages/secure-storage/index.ios.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ export class SecureStorage extends SecureStorageCommon {
4343
let query = SAMKeychainQuery.new();
4444
query.service = arg.service || SecureStorage.defaultService;
4545
query.account = arg.key;
46-
46+
if (arg.accessGroup) {
47+
query.accessGroup = arg.accessGroup;
48+
}
4749
try {
4850
query.fetch();
4951
resolve(query.password);
@@ -61,6 +63,9 @@ export class SecureStorage extends SecureStorageCommon {
6163
let query = SAMKeychainQuery.new();
6264
query.service = arg.service || SecureStorage.defaultService;
6365
query.account = arg.key;
66+
if (arg.accessGroup) {
67+
query.accessGroup = arg.accessGroup;
68+
}
6469
try {
6570
query.fetch();
6671
return query.password;
@@ -82,6 +87,9 @@ export class SecureStorage extends SecureStorageCommon {
8287
query.service = arg.service || SecureStorage.defaultService;
8388
query.account = arg.key;
8489
query.password = arg.value;
90+
if (arg.accessGroup) {
91+
query.accessGroup = arg.accessGroup;
92+
}
8593
resolve(query.save());
8694
});
8795
}
@@ -97,6 +105,9 @@ export class SecureStorage extends SecureStorageCommon {
97105
query.service = arg.service || SecureStorage.defaultService;
98106
query.account = arg.key;
99107
query.password = arg.value;
108+
if (arg.accessGroup) {
109+
query.accessGroup = arg.accessGroup;
110+
}
100111
return query.save();
101112
}
102113

@@ -111,6 +122,9 @@ export class SecureStorage extends SecureStorageCommon {
111122
let query = SAMKeychainQuery.new();
112123
query.service = arg.service || SecureStorage.defaultService;
113124
query.account = arg.key;
125+
if (arg.accessGroup) {
126+
query.accessGroup = arg.accessGroup;
127+
}
114128
try {
115129
resolve(query.deleteItem());
116130
} catch (e) {
@@ -128,6 +142,9 @@ export class SecureStorage extends SecureStorageCommon {
128142
let query = SAMKeychainQuery.new();
129143
query.service = arg.service || SecureStorage.defaultService;
130144
query.account = arg.key;
145+
if (arg.accessGroup) {
146+
query.accessGroup = arg.accessGroup;
147+
}
131148
try {
132149
return query.deleteItem();
133150
} catch (e) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/// <reference path="../../references.d.ts" />
2+
/// <reference path="./typings/objc!SAMKeychain.d.ts" />
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
declare class SAMKeychain extends NSObject {
2+
static accessibilityType(): any;
3+
4+
static accountsForService(serviceName: string): NSArray<NSDictionary<string, any>>;
5+
6+
static accountsForServiceError(serviceName: string): NSArray<NSDictionary<string, any>>;
7+
8+
static allAccounts(): NSArray<NSDictionary<string, any>>;
9+
10+
static alloc(): SAMKeychain; // inherited from NSObject
11+
12+
static deletePasswordForServiceAccount(serviceName: string, account: string): boolean;
13+
14+
static deletePasswordForServiceAccountError(serviceName: string, account: string): boolean;
15+
16+
static new(): SAMKeychain; // inherited from NSObject
17+
18+
static passwordDataForServiceAccount(serviceName: string, account: string): NSData;
19+
20+
static passwordDataForServiceAccountError(serviceName: string, account: string): NSData;
21+
22+
static passwordForServiceAccount(serviceName: string, account: string): string;
23+
24+
static passwordForServiceAccountError(serviceName: string, account: string): string;
25+
26+
static setAccessibilityType(accessibilityType: any): void;
27+
28+
static setPasswordDataForServiceAccount(password: NSData, serviceName: string, account: string): boolean;
29+
30+
static setPasswordDataForServiceAccountError(password: NSData, serviceName: string, account: string): boolean;
31+
32+
static setPasswordForServiceAccount(password: string, serviceName: string, account: string): boolean;
33+
34+
static setPasswordForServiceAccountError(password: string, serviceName: string, account: string): boolean;
35+
}
36+
37+
declare const enum SAMKeychainErrorCode {
38+
BadArguments = -1001,
39+
}
40+
41+
declare class SAMKeychainQuery extends NSObject {
42+
static alloc(): SAMKeychainQuery; // inherited from NSObject
43+
44+
static isSynchronizationAvailable(): boolean;
45+
46+
static new(): SAMKeychainQuery; // inherited from NSObject
47+
48+
accessGroup: string;
49+
50+
account: string;
51+
52+
label: string;
53+
54+
password: string;
55+
56+
passwordData: NSData;
57+
58+
passwordObject: NSCoding;
59+
60+
service: string;
61+
62+
synchronizationMode: SAMKeychainQuerySynchronizationMode;
63+
64+
deleteItem(): boolean;
65+
66+
fetch(): boolean;
67+
68+
fetchAll(): NSArray<NSDictionary<string, any>>;
69+
70+
save(): boolean;
71+
}
72+
73+
declare const enum SAMKeychainQuerySynchronizationMode {
74+
Any = 0,
75+
76+
No = 1,
77+
78+
Yes = 2,
79+
}
80+
81+
declare var SAMKeychainVersionNumber: number;
82+
83+
declare var SAMKeychainVersionString: interop.Reference<number>;
84+
85+
declare var kSAMKeychainAccountKey: string;
86+
87+
declare var kSAMKeychainClassKey: string;
88+
89+
declare var kSAMKeychainCreatedAtKey: string;
90+
91+
declare var kSAMKeychainDescriptionKey: string;
92+
93+
declare var kSAMKeychainErrorDomain: string;
94+
95+
declare var kSAMKeychainLabelKey: string;
96+
97+
declare var kSAMKeychainLastModifiedKey: string;
98+
99+
declare var kSAMKeychainWhereKey: string;

0 commit comments

Comments
 (0)