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];