I have an application with fairly simple authorization/authentication and need a bit of help on how to handle the authorization life cycle. Here's the jist of it:
- The user enters their credentials to log in and the server responds with a token and stores in the keychain
- The token is used in the "Authorization" header for every subsequent URL request (REST calls)
- The token eventually expires and the server will respond with 401 status code
- At this point the user needs to re-authenticate via the login screen so we can get a new token and retry the request
Here's some code that represents the best I could come up with right now.
static NSString * _authorizationToken;
@implementation MyRestfulModel
+ (id) sharedRestfulModel
{
// singleton model shared across view controllers
static MyRestfulModel * _sharedModel = nil;
@synchronized(self) {
if (_sharedModel == nil)
_sharedModel = [[self init] alloc];
}
return _sharedModel;
}
+ (NSString *) authorizationToken
{
if (!_authorizationToken)
_authorizationToken = @"";
return _authorizationToken;
}
+ (void) setAuthorizationToken: (NSString *token)
{
_authorizationToken = token;
}
-(void) doSomeRestfulCall: (NSString *) restURL
completionHandler: (void (^)(NSData * data)) callback
{
// construct url path and request
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:restURL];
[request setValue:MyRestfulModel.authorizationToken forHTTPHeaderField:@"Authorization"];
[[[NSURLSession sharedSession] dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse * response, NSError * error) {
NSHTTPResponse * httpResponse = (NSHTTPResponse *) response;
if([httpResponse statusCode] == 401) { // WHAT TO DO HERE ?
dispatch_sync(dispatch_get_main_queue(), ^{
MyAppDelegate * delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate showLoginViewController callback:^(NSString * username, NSString * newToken) {
// recreate the NSURLSession and NSURLConfiguration with the new token and retry
MyRestfulModel.authenticationToken = token;
[self doSomeRestfulCall:callback];
}];
}
return;
} else {
callback(data);
}
}] resume];
}
I hope to do it like this so that the view controllers never need to worry about retrying a call due to a token expiration, all of the scary session handling and authentication can be done by one object.
I was considering trying to use NSURLSessionDelegate but I couldn't figure out the didReceiveChallenge part w.r.t. popping up the login view controller. The other option I was considering is adding a "retryHandler" similar to the completion handler which would be called after the user re-authenticates.