Sapan Diwakar

Software developer

Follow me on Twitter Check out my code on GitHub View some of my designs on Dribbble Take a look at my Linked In profile

Automatically Refresh Auth Token using RestKit

When using OAuth for authenticating your users for your app, you simply need to store the auth token (e.g. in NSUserDefaults) and you can make authenticated requests in the future. But what if the auth token expires in the future and you need to refresh it? When using RestKit to communicate with the backend, we only need to extend the RKObjectManager so that it automatically refreshes the auth token when it detects an unauthorized request and retries the request again.

@implementation SDObjectManager

#pragma mark - RKObjectManager Overrides
- (void)getObjectsAtPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, 
RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {  
    [super getObjectsAtPath:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super getObjectsAtPath:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)getObjectsAtPathForRelationship:(NSString *)relationshipName ofObject:(id)object parameters:(NSDictionary *)parameters 
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation,  
NSError *error))failure {  
    [super getObjectsAtPathForRelationship:relationshipName ofObject:object parameters:parameters success:success 
failure:^(RKObjectRequestOperation *operation, NSError *error) {  
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super getObjectsAtPathForRelationship:relationshipName ofObject:object parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)getObjectsAtPathForRouteNamed:(NSString *)routeName object:(id)object parameters:(NSDictionary *)parameters 
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {  
    [super getObjectsAtPathForRouteNamed:routeName object:object parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super getObjectsAtPathForRouteNamed:routeName object:object parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)getObject:(id)object path:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {
    [super getObject:object path:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super getObject:object path:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)postObject:(id)object path:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {
    [super postObject:object path:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super postObject:object path:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)putObject:(id)object path:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {
    [super putObject:object path:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super putObject:object path:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)patchObject:(id)object path:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {
    [super patchObject:object path:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super patchObject:object path:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

- (void)deleteObject:(id)object path:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure {
    [super deleteObject:object path:path parameters:parameters success:success failure:^(RKObjectRequestOperation *operation, NSError *error) {
        [self tryRefreshingTokenForOperation:operation].then(^() {
            [super deleteObject:object path:path parameters:parameters success:success failure:failure];
        }).catch(^() {
            failure(operation, error);
        });
    }];
}

#pragma mark - Authentication Token Refresh
- (PMKPromise *)tryRefreshingTokenForOperation:(RKObjectRequestOperation *)operation {
    return [PMKPromise promiseWithResolver:^(PMKResolver resolve) {
        if (operation.HTTPRequestOperation.response.statusCode == 401 && [SDAppDelegate instance].authToken) {
            // Try refreshing the auth token
            resolve([[SDAppDelegate instance].authToken refresh].then(^(IQOAuthToken *token) {
                [[SDAppDelegate instance] didUpdateAuthToken:token];
            }));
        } else {
            @throw @"Failure not due to auth token";
        }
    }];
}

@end

We can now use this object manager instead of the default one for making our requests and we are good to go.

RKObjectManager *objectManager = [SDObjectManager managerWithBaseURL:[NSURL URLWithString:kSDApiBaseUrl]];  
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;  
[RKObjectManager setSharedManager:objectManager];