1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDTokenStore.h"
#import "FIRInstanceIDAuthKeyChain.h"
#import "FIRInstanceIDConstants.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDTokenInfo.h"
#import "FIRInstanceIDUtilities.h"
static NSString *const kFIRInstanceIDTokenKeychainId = @"com.google.iid-tokens";
@interface FIRInstanceIDTokenStore ()
@property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain;
@end
@implementation FIRInstanceIDTokenStore
+ (instancetype)defaultStore {
FIRInstanceIDAuthKeychain *tokenKeychain =
[[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDTokenKeychainId];
return [[FIRInstanceIDTokenStore alloc] initWithKeychain:tokenKeychain];
}
- (instancetype)initWithKeychain:(FIRInstanceIDAuthKeychain *)keychain {
self = [super init];
if (self) {
_keychain = keychain;
}
return self;
}
#pragma mark - Get
+ (NSString *)serviceKeyForAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
return [NSString stringWithFormat:@"%@:%@", authorizedEntity, scope];
}
- (nullable FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity
scope:(NSString *)scope {
NSString *account = FIRInstanceIDAppIdentifier();
NSString *service = [[self class] serviceKeyForAuthorizedEntity:authorizedEntity scope:scope];
NSData *item = [self.keychain dataForService:service account:account];
if (!item) {
return nil;
}
// Token infos created from legacy storage don't have appVersion, firebaseAppID, or APNSInfo.
FIRInstanceIDTokenInfo *tokenInfo = [[self class] tokenInfoFromKeychainItem:item];
return tokenInfo;
}
- (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos {
NSString *account = FIRInstanceIDAppIdentifier();
NSArray<NSData *> *items =
[self.keychain itemsMatchingService:kFIRInstanceIDKeychainWildcardIdentifier account:account];
NSMutableArray<FIRInstanceIDTokenInfo *> *tokenInfos =
[NSMutableArray arrayWithCapacity:items.count];
for (NSData *item in items) {
FIRInstanceIDTokenInfo *tokenInfo = [[self class] tokenInfoFromKeychainItem:item];
if (tokenInfo) {
[tokenInfos addObject:tokenInfo];
}
}
return tokenInfos;
}
+ (nullable FIRInstanceIDTokenInfo *)tokenInfoFromKeychainItem:(NSData *)item {
// Check if it is saved as an archived FIRInstanceIDTokenInfo, otherwise return nil.
FIRInstanceIDTokenInfo *tokenInfo = nil;
// NOTE: Passing in nil to unarchiveObjectWithData will result in an iOS error logged
// in the console on iOS 10 and below. Avoid by checking item.data's existence.
if (item) {
// TODO(chliangGoogle: Use the new API and secureCoding protocol.
@try {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
tokenInfo = [NSKeyedUnarchiver unarchiveObjectWithData:item];
#pragma clang diagnostic pop
} @catch (NSException *exception) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeTokenStoreExceptionUnarchivingTokenInfo,
@"Unable to parse token info from Keychain item; item was in an "
@"invalid format");
tokenInfo = nil;
} @finally {
}
}
return tokenInfo;
}
#pragma mark - Save
// Token Infos will be saved under these Keychain keys:
// Account: <Main App Bundle ID> (e.g. com.mycompany.myapp)
// Service: <Sender ID>:<Scope> (e.g. 1234567890:*)
- (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo
handler:(void (^)(NSError *))handler { // Keep the cachetime up-to-date.
tokenInfo.cacheTime = [NSDate date];
// Always write to the Keychain, so that the cacheTime is up-to-date.
NSData *tokenInfoData;
// TODO(chliangGoogle: Use the new API and secureCoding protocol.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
tokenInfoData = [NSKeyedArchiver archivedDataWithRootObject:tokenInfo];
#pragma clang diagnostic pop
NSString *account = FIRInstanceIDAppIdentifier();
NSString *service = [[self class] serviceKeyForAuthorizedEntity:tokenInfo.authorizedEntity
scope:tokenInfo.scope];
[self.keychain setData:tokenInfoData forService:service account:account handler:handler];
}
#pragma mark - Delete
- (void)removeTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity
scope:(nonnull NSString *)scope {
NSString *account = FIRInstanceIDAppIdentifier();
NSString *service = [[self class] serviceKeyForAuthorizedEntity:authorizedEntity scope:scope];
[self.keychain removeItemsMatchingService:service account:account handler:nil];
}
- (void)removeAllTokensWithHandler:(void (^)(NSError *error))handler {
NSString *account = FIRInstanceIDAppIdentifier();
[self.keychain removeItemsMatchingService:kFIRInstanceIDKeychainWildcardIdentifier
account:account
handler:handler];
}
@end
|