1// Copyright © 2019-present 650 Industries. All rights reserved. 2 3#if __has_include(<EXSecureStore/EXSecureStore.h>) 4#import "EXScopedSecureStore.h" 5 6@interface EXSecureStore (Protected) 7 8- (NSString *)validatedKey:(NSString *)key; 9- (NSData *)_searchKeychainWithKey:(NSString *)key withOptions:(NSDictionary *)options error:(NSError **)error; 10- (BOOL)_setValue:(NSString *)value withKey:(NSString *)key withOptions:(NSDictionary *)options error:(NSError **)error; 11- (void)_deleteValueWithKey:(NSString *)key withOptions:(NSDictionary *)options; 12+ (NSString *) _messageForError:(NSError *)error; 13 14@end 15 16@interface EXScopedSecureStore () 17 18@property (strong, nonatomic) NSString *scopeKey; 19@property (nonatomic) BOOL isStandaloneApp; 20 21@end 22 23@implementation EXScopedSecureStore 24 25- (instancetype)initWithScopeKey:(NSString *)scopeKey 26 andConstantsBinding:(EXConstantsBinding *)constantsBinding 27{ 28 if (self = [super init]) { 29 _scopeKey = scopeKey; 30 _isStandaloneApp = ![@"expo" isEqualToString:constantsBinding.appOwnership]; 31 } 32 return self; 33} 34 35- (NSString *)validatedKey:(NSString *)key { 36 if (![super validatedKey:key]) { 37 return nil; 38 } 39 40 return _isStandaloneApp ? key : [NSString stringWithFormat:@"%@-%@", _scopeKey, key]; 41} 42 43// We must override this method so that items saved in standalone apps on SDK 40 and below, 44// which were scoped by prefixing the validated key with the scopeKey, can still be 45// found in SDK 41 and up. This override can be removed in SDK 45. 46- (NSString *)_getValueWithKey:(NSString *)key withOptions:(NSDictionary *)options error:(NSError **)error 47{ 48 NSError *searchError; 49 NSData *data = [self _searchKeychainWithKey:key 50 withOptions:options 51 error:&searchError]; 52 if (data) { 53 NSString *value = [[NSString alloc] initWithData:data 54 encoding:NSUTF8StringEncoding]; 55 return value; 56 } else if (_isStandaloneApp) { 57 NSString *scopedKey = [NSString stringWithFormat:@"%@-%@", _scopeKey, key]; 58 NSString *scopedValue = [self getValueWithScopedKey:scopedKey 59 withOptions:options]; 60 if (scopedValue) { 61 [self migrateValue:scopedValue 62 fromScopedKey:scopedKey 63 toNewKey:key 64 withOptions:options]; 65 return scopedValue; 66 } 67 // If we don't find anything under the scopedKey, we want to return 68 // the original error from searching for the unscoped key. 69 } 70 71 *error = searchError; 72 return nil; 73} 74 75- (NSString *)getValueWithScopedKey:(NSString *)scopedKey withOptions:(NSDictionary *)options 76{ 77 NSError *searchError; 78 NSData *data = [self _searchKeychainWithKey:scopedKey 79 withOptions:options 80 error:&searchError]; 81 if (data) { 82 NSString *value = [[NSString alloc] initWithData:data 83 encoding:NSUTF8StringEncoding]; 84 return value; 85 } 86 return nil; 87} 88 89- (void)migrateValue:(NSString *)value 90 fromScopedKey:(NSString *)scopedKey 91 toNewKey:(NSString *)newKey 92 withOptions:(NSDictionary *)options 93{ 94 // Migrate the value to unscoped storage, then delete the legacy 95 // value if successful. 96 NSError *error; 97 BOOL setValue = [self _setValue:value 98 withKey:newKey 99 withOptions:options 100 error:&error]; 101 if (setValue) { 102 [self _deleteValueWithKey:scopedKey 103 withOptions:options]; 104 } else { 105 EXLogWarn(@"Encountered an error while saving SecureStore data: %@.", [[super class] _messageForError:error]); 106 } 107} 108 109@end 110#endif 111