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 47 withOptions:(NSDictionary *)options 48 error:(NSError **)error __deprecated_msg("To be removed once SDK 41 is phased out") 49{ 50 NSError *searchError; 51 NSData *data = [self _searchKeychainWithKey:key 52 withOptions:options 53 error:&searchError]; 54 if (data) { 55 NSString *value = [[NSString alloc] initWithData:data 56 encoding:NSUTF8StringEncoding]; 57 return value; 58 } else if (_isStandaloneApp) { 59 NSString *scopedKey = [NSString stringWithFormat:@"%@-%@", _scopeKey, key]; 60 NSString *scopedValue = [self getValueWithScopedKey:scopedKey 61 withOptions:options]; 62 if (scopedValue) { 63 [self migrateValue:scopedValue 64 fromScopedKey:scopedKey 65 toNewKey:key 66 withOptions:options]; 67 return scopedValue; 68 } 69 // If we don't find anything under the scopedKey, we want to return 70 // the original error from searching for the unscoped key. 71 } 72 73 *error = searchError; 74 return nil; 75} 76 77- (NSString *)getValueWithScopedKey:(NSString *)scopedKey withOptions:(NSDictionary *)options 78{ 79 NSError *searchError; 80 NSData *data = [self _searchKeychainWithKey:scopedKey 81 withOptions:options 82 error:&searchError]; 83 if (data) { 84 NSString *value = [[NSString alloc] initWithData:data 85 encoding:NSUTF8StringEncoding]; 86 return value; 87 } 88 return nil; 89} 90 91- (void)migrateValue:(NSString *)value 92 fromScopedKey:(NSString *)scopedKey 93 toNewKey:(NSString *)newKey 94 withOptions:(NSDictionary *)options 95{ 96 // Migrate the value to unscoped storage, then delete the legacy 97 // value if successful. 98 NSError *error; 99 BOOL setValue = [self _setValue:value 100 withKey:newKey 101 withOptions:options 102 error:&error]; 103 if (setValue) { 104 [self _deleteValueWithKey:scopedKey 105 withOptions:options]; 106 } else { 107 EXLogWarn(@"Encountered an error while saving SecureStore data: %@.", [[super class] _messageForError:error]); 108 } 109} 110 111@end 112#endif 113