Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the hardcoding of the JWT calls + switch to session fetcher fo… #9

Merged
merged 1 commit into from
Mar 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ios/BEMCommunicationHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import <Foundation/Foundation.h>
#import "GTMSessionFetcherService.h"

@interface CommunicationHelper : NSObject
// Wrappers for our specific functionality
Expand All @@ -31,4 +32,5 @@
@property (nonatomic, strong) NSURL* mUrl;
@property (nonatomic, strong) NSMutableDictionary* mJsonDict;
@property (nonatomic, strong) void (^mCompletionHandler)(NSData *data, NSURLResponse *response, NSError *error);
@property (nonatomic, strong) GTMSessionFetcherService* fetcherService;
@end
118 changes: 15 additions & 103 deletions src/ios/BEMCommunicationHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
#import "BEMConnectionSettings.h"
#import "BEMConstants.h"
#import "LocalNotificationManager.h"

#import <GoogleOpenSource/GoogleOpenSource.h>
#import "GTMSessionFetcher.h"

// This is the base URL
// We need to append the username to it, and then we need to authenticate the user as well
Expand Down Expand Up @@ -135,6 +134,7 @@ -(id)initPost:(NSURL*)url data:(NSMutableDictionary*)jsonDict completionHandler:
self.mUrl = url;
self.mJsonDict = jsonDict;
self.mCompletionHandler = completionHandler;
self.fetcherService = [[GTMSessionFetcherService alloc] init];

return [super init];
}
Expand All @@ -155,112 +155,16 @@ -(void)execute {
return;
}

// Next, we need to check whether the user is logged in. If not, we need to re-login
// This will call the finishedWithAuth callback
GTMOAuth2Authentication* currAuth = [AuthCompletionHandler sharedInstance].currAuth;
if (currAuth == NULL) {
[self tryToAuthenticate:jsonData];
currAuth = [AuthCompletionHandler sharedInstance].currAuth;
}

if (currAuth != NULL) {
BOOL expired = ([currAuth.expirationDate compare:[NSDate date]] == NSOrderedAscending);
// The access token may not have expired, but the id token may not be available because the app has been restarted,
// so it is not in memory, and the ID token is not stored in the keychain. It is a real pain to store the ID token
// in the keychain through subclassing, so let's just try to refresh the token anyway
expired = expired || ([AuthCompletionHandler sharedInstance].getIdToken == NULL);
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"currAuth = %@, canAuthorize = %@, expiresIn = %@, expirationDate = %@, expired = %@",
currAuth, NSStringFromBOOL(currAuth.canAuthorize), currAuth.expiresIn, currAuth.expirationDate,
NSStringFromBOOL(expired)] showUI:FALSE];
if (currAuth.canAuthorize != YES) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Unable to refresh token, trying to re-authenticate"] showUI:FALSE];
// Not sure why we would get canAuthorize be null, but I assume that we re-login in that case
[self tryToAuthenticate:jsonData];
} else {
if (expired) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Existing auth token expired, refreshing"] showUI:FALSE];
// Need to refresh the token
[currAuth authorizeRequest:NULL completionHandler:^(NSError *error) {
[[AuthCompletionHandler sharedInstance] getValidAuth:^(GTMOAuth2Authentication *auth,NSError* error) {
if (error != NULL) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Error %@ while refreshing token, need to notify user", error] showUI:FALSE];
// modify some kind of error count and notify that user needs to sign in again
self.mCompletionHandler(jsonData, nil, error);
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Refresh completion block called, refreshed token is %@", currAuth] showUI:FALSE];
BOOL stillExpired = ([currAuth.expirationDate compare:[NSDate date]] == NSOrderedAscending);
if (stillExpired) {
// Although we called refresh, the token is still expired. Let's try to call a different
// refresh method
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Existing auth token still expired after first refresh attempt, notifying user"] showUI:FALSE];
/*
[currAuth authorizeRequest:NULL
delegate:self
didFinishSelector:@selector(finishRefreshSelector:)];
*/
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Refresh token still expired.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Unknown.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Sign out and Sign in again.", nil)
};
// TODO: Make a domain and error class
NSError *refreshError = [NSError errorWithDomain:errorDomain code:authFailedNeedUserInput userInfo:userInfo];
self.mCompletionHandler(NULL, NULL, refreshError);
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Refresh is realy done, posting to host"] showUI:FALSE];
assert(error == NULL);
[self postToHost];
}
}
}];
self.mCompletionHandler(jsonData, NULL, error);
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Existing auth token not expired, posting to host"] showUI:FALSE];
assert(expired == FALSE);
// TODO: have postToHost take the auth token as input instead of re-reading it
[self postToHost];
}
}
}
} forceRefresh:FALSE];
}

- (BOOL)tryToAuthenticate:(NSData*)jsonData {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"tryToAuthenticate called"] showUI:FALSE];
[[AuthCompletionHandler sharedInstance] registerFinishDelegate:self];
BOOL silentAuthResult = [[AuthCompletionHandler sharedInstance] trySilentAuthentication];
if (silentAuthResult == NO) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Need user input for authentication, need to signal user somehow"] showUI:FALSE];
[[AuthCompletionHandler sharedInstance] unregisterFinishDelegate:self];
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"User authentication failed.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"User information not available in keychain.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Need to login and authorize access to email address.", nil)
};
// TODO: Make a domain and error class
NSError *authError = [NSError errorWithDomain:errorDomain code:authFailedNeedUserInput userInfo:userInfo];
self.mCompletionHandler(jsonData, NULL, authError);
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"ready to authenticate, checking expiration"]
showUI:FALSE];
// So far, callback has not taken a long time...
// But callback may take a long time. In that case, we may want to return early.
// Also, callback will invoke mCompletionHandler in a separate thread, which won't
// work because all callbacks have to be in the main thread for this to succeed
// So we say that we are done here with no data
// However, in the callback handler, we set the backgroundFetchInterval to 10 mins
// So we will be called again, and won't have to invoke this call then
// mCompletionHandler(NULL, NULL, NULL);
}
return silentAuthResult;
}

- (void)postToHost {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
Expand Down Expand Up @@ -293,12 +197,20 @@ - (void)postToHost {

I have run this through xcode and refreshed multiple times, and the memory consumption does not appear to increase.
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
GTMSessionFetcher *myFetcher = [_fetcherService fetcherWithRequest:request];
myFetcher.retryEnabled = YES;
myFetcher.bodyData = jsonData;
[myFetcher beginFetchWithCompletionHandler:^(NSData * _Nullable data, NSError * _Nullable error) {
self.mCompletionHandler(data, myFetcher.response, error);
}];
/*
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:request.URL.path]
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSLog(@"session queue = %@, mainQueue = %@", session.delegateQueue, [NSOperationQueue mainQueue]);
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:jsonData completionHandler:self.mCompletionHandler];
[task resume];
*/
}
}

Expand Down