Files
firebase-ios-sdk/Firebase/InstanceID/FIRInstanceIDTokenOperation.m
T
Maksym MalyhinandChen Liang f62c7ddcec IID: version dependent on FirebaseInstallations (#4533)
* Firebase Installations: Instance ID now uses FIS under the hood. (#3398)

* Fix merge errors

* Use modular import for FIRInstanceID_Private.h

* Use modular imports for FirebaseInstanceID.h (#3509)

* Changes missing during the fix.

* Changes missing during merge.

* Fix header import.

* ./scripts/style.sh

* Fix FCM tests. (#3888)

* Fix IID FCM token check.

* FirebaseInstallation: merge master into FIS integration branch (#4076)

* Fix IID FCM token check.

* Unused constant deleted.

* FCM tests: fix bad merge.

* Remote Config test app: use local IID and FIS. (#4089)

* token request should pass through if token is not fresh (#4104)

* Merge errors fix.

* IID with FIS: send FIS auth token with IID token request (#4152)

* FIRInstanceIDTokenOperation: add FIS auth token to the IID token request.

* IID tests: don't use FIS private headers.

* IID: indication of compatibility with FIS (#4262)

* FIS: IID incompatible version exception.

* FirebaseInstallations.podspec: set preprocessor flags based on env vars.

* FIS Travis: set required env vars

* /scripts/style.sh

* IID: method to indicate FIS usege added.

* FIS: FIRInstantiationTimingEagerInDefaultApp

* FIS: FIRInstantiationTimingAlwaysEager

* IID: make compatibility method static.

* Make -[FIRInstanceID appInstanceID:] safe to be called from any thread (#4482)

* IID: use a cached value of FID for -[FIRInstanceID appInstanceID:]

* TODO comment

* TODO updated

* FirebaseInstallations: prepare IID with FIS dependency for release (#4522)

* Bump FIrebaseInstallations to 1.0.0

* Travis: remove mm/fis-integration-master branch

* Travis: if_changed script updated to reflect IID dependency on FIS.

* Revert "Travis: remove mm/fis-integration-master branch"

This reverts commit 534dff990a.

* Travis: remove mm/fis-integration-master branch (#4526)

* Formatting and spelling.

* Firebase example proj: pod deintegrate

Co-authored-by: Chen Liang <chliang@google.com>
2019-12-20 10:54:11 -05:00

259 lines
8.9 KiB
Objective-C

/*
* 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 "FIRInstanceIDTokenOperation.h"
#import <FirebaseInstallations/FirebaseInstallations.h>
#import "FIRInstanceIDCheckinPreferences.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDURLQueryItem.h"
#import "FIRInstanceIDUtilities.h"
#import "NSError+FIRInstanceID.h"
static const NSInteger kFIRInstanceIDPlatformVersionIOS = 2;
static NSString *const kFIRInstanceIDParamInstanceID = @"appid";
// Scope parameter that defines the service using the token
static NSString *const kFIRInstanceIDParamScope = @"X-scope";
// Defines the SDK version
static NSString *const kFIRInstanceIDParamFCMLibVersion = @"X-cliv";
@interface FIRInstanceIDTokenOperation () {
BOOL _isFinished;
BOOL _isExecuting;
}
@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *checkinPreferences;
@property(nonatomic, readwrite, strong) NSString *instanceID;
@property(atomic, strong) NSURLSessionDataTask *dataTask;
@property(readonly, strong)
NSMutableArray<FIRInstanceIDTokenOperationCompletion> *completionHandlers;
@property(atomic, strong, nullable) NSString *FISAuthToken;
// For testing only
@property(nonatomic, readwrite, copy) FIRInstanceIDURLRequestTestBlock testBlock;
@end
@implementation FIRInstanceIDTokenOperation
+ (NSURLSession *)sharedURLSession {
static NSURLSession *tokenOperationSharedSession;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForResource = 60.0f; // 1 minute
tokenOperationSharedSession = [NSURLSession sessionWithConfiguration:config];
tokenOperationSharedSession.sessionDescription = @"com.google.iid.tokens.session";
});
return tokenOperationSharedSession;
}
- (instancetype)initWithAction:(FIRInstanceIDTokenAction)action
forAuthorizedEntity:(NSString *)authorizedEntity
scope:(NSString *)scope
options:(NSDictionary<NSString *, NSString *> *)options
checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences
instanceID:(NSString *)instanceID {
self = [super init];
if (self) {
_action = action;
_authorizedEntity = [authorizedEntity copy];
_scope = [scope copy];
_options = [options copy];
_checkinPreferences = checkinPreferences;
_instanceID = instanceID;
_completionHandlers = [NSMutableArray array];
_isExecuting = NO;
_isFinished = NO;
}
return self;
}
- (void)dealloc {
_testBlock = nil;
_authorizedEntity = nil;
_scope = nil;
_options = nil;
_checkinPreferences = nil;
_instanceID = nil;
[_completionHandlers removeAllObjects];
_completionHandlers = nil;
}
- (void)addCompletionHandler:(FIRInstanceIDTokenOperationCompletion)handler {
[self.completionHandlers addObject:handler];
}
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isExecuting {
return _isExecuting;
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isFinished {
return _isFinished;
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_isFinished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)start {
if (self.isCancelled) {
[self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil];
return;
}
// Quickly validate whether or not the operation has all it needs to begin
BOOL checkinfoAvailable = [self.checkinPreferences hasCheckinInfo];
if (!checkinfoAvailable) {
FIRInstanceIDErrorCode errorCode = kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn;
[self finishWithResult:FIRInstanceIDTokenOperationError
token:nil
error:[NSError errorWithFIRInstanceIDErrorCode:errorCode]];
return;
}
[self setExecuting:YES];
[[FIRInstallations installations]
authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult,
NSError *_Nullable error) {
if (tokenResult.authToken.length > 0) {
self.FISAuthToken = tokenResult.authToken;
[self performTokenOperation];
} else {
[self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error];
}
}];
}
- (void)finishWithResult:(FIRInstanceIDTokenOperationResult)result
token:(nullable NSString *)token
error:(nullable NSError *)error {
// Add a check to prevent this finish from being called more than once.
if (self.isFinished) {
return;
}
self.dataTask = nil;
_result = result;
// TODO(chliangGoogle): Call these in the main thread?
for (FIRInstanceIDTokenOperationCompletion completionHandler in self.completionHandlers) {
completionHandler(result, token, error);
}
[self setExecuting:NO];
[self setFinished:YES];
}
- (void)cancel {
[super cancel];
[self.dataTask cancel];
[self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil];
}
- (void)performTokenOperation {
}
- (NSMutableURLRequest *)tokenRequest {
NSString *authHeader =
[FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:self.checkinPreferences];
return [[self class] requestWithAuthHeader:authHeader FISAuthToken:self.FISAuthToken];
}
#pragma mark - Request Construction
+ (NSMutableURLRequest *)requestWithAuthHeader:(NSString *)authHeaderString
FISAuthToken:(NSString *)FISAuthToken {
NSURL *url = [NSURL URLWithString:FIRInstanceIDRegisterServer()];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// Add HTTP headers
[request setValue:authHeaderString forHTTPHeaderField:@"Authorization"];
[request setValue:FIRInstanceIDAppIdentifier() forHTTPHeaderField:@"app"];
if (FISAuthToken) {
[request setValue:FISAuthToken forHTTPHeaderField:@"x-goog-firebase-installations-auth"];
}
request.HTTPMethod = @"POST";
return request;
}
+ (NSMutableArray<FIRInstanceIDURLQueryItem *> *)standardQueryItemsWithDeviceID:(NSString *)deviceID
scope:(NSString *)scope {
NSMutableArray<FIRInstanceIDURLQueryItem *> *queryItems = [NSMutableArray arrayWithCapacity:8];
// E.g. X-osv=10.2.1
NSString *systemVersion = FIRInstanceIDOperatingSystemVersion();
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"X-osv" value:systemVersion]];
// E.g. device=
if (deviceID) {
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"device" value:deviceID]];
}
// E.g. X-scope=fcm
if (scope) {
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamScope
value:scope]];
}
// E.g. plat=2
NSString *platform = [NSString stringWithFormat:@"%ld", (long)kFIRInstanceIDPlatformVersionIOS];
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"plat" value:platform]];
// E.g. app=com.myapp.foo
NSString *appIdentifier = FIRInstanceIDAppIdentifier();
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"app" value:appIdentifier]];
// E.g. app_ver=1.5
NSString *appVersion = FIRInstanceIDCurrentAppVersion();
[queryItems addObject:[FIRInstanceIDURLQueryItem queryItemWithName:@"app_ver" value:appVersion]];
// E.g. X-cliv=fiid-1.2.3
NSString *fcmLibraryVersion =
[NSString stringWithFormat:@"fiid-%@", FIRInstanceIDCurrentGCMVersion()];
if (fcmLibraryVersion.length) {
FIRInstanceIDURLQueryItem *gcmLibVersion =
[FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamFCMLibVersion
value:fcmLibraryVersion];
[queryItems addObject:gcmLibVersion];
}
return queryItems;
}
- (NSArray<FIRInstanceIDURLQueryItem *> *)queryItemsWithInstanceID:(NSString *)instanceID {
return @[ [FIRInstanceIDURLQueryItem queryItemWithName:kFIRInstanceIDParamInstanceID
value:instanceID] ];
}
#pragma mark - HTTP Header
+ (NSString *)HTTPAuthHeaderFromCheckin:(FIRInstanceIDCheckinPreferences *)checkin {
NSString *deviceID = checkin.deviceID;
NSString *secret = checkin.secretToken;
return [NSString stringWithFormat:@"AidLogin %@:%@", deviceID, secret];
}
@end