I am trying to do Basic Authentication using RestKit but I need some help with figuring out why I get status code 401(Unauthorized). I tried this, but it did not work for me. I will appreciate if someone can point out my mistake.
This is what my data looks like on the server side: Sample
{
"Id": "baec6f47",
"TokenValidity": "00:00:00.1234567",
"ValidTo": "2013-10-28T23:54:21.2934278+00:00",
"Parameters": {},
"Token": "sample string 3"
}
I have a NSObject class called TokenObject, and that where I am need to store this data.
TokenObject.h
#import <Foundation/Foundation.h>
@interface TokenObject : NSObject
@property (nonatomic,strong) NSNumber *tokenId;
@property (nonatomic,strong) NSDate *TokenValidity;
@property (nonatomic,strong) NSDate *ValidTo;
@property (nonatomic,strong)NSString *Parameters;
@property (nonatomic,strong)NSString *Token;
@end
Quick Overview:
In the login screen I only ask for the username, and once the user clicks on sign up, I sent a Post request to create the account and send a get request for the token(and use the RestKit's setAuthorizationHeaderWithUsername right before the get request). For the username:password, I am using the the nickname and hardware id.
This is the method where I am posting the account(works), encode the username and password to base 64(works) and trying to get the token(which is the part i am having problem with).
LoginViewController.h
#import <UIKit/UIKit.h>
@interface LoginViewController : UIViewController<UITextFieldDelegate,NSURLConnectionDelegate>
@property (strong, nonatomic) IBOutlet UITextField *usernameTextField;
@property (strong, nonatomic) IBOutlet UIButton *submitButton;
@property (nonatomic,readonly) NSUUID *identifierForVendor;
@property(nonatomic, readonly, retain) NSString *model;
@property (nonatomic,readonly,retain)NSString *StoreIdentifierForVendor;
@property (nonatomic,readonly,retain)NSString *StoreTheModel;
- (IBAction)submit:(id)sender;
@property (nonatomic,strong)NSString *nickname;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property(nonatomic,strong)NSString *encodedName;
@property(nonatomic,strong)NSString *encodedHardwareId;
@property(nonatomic,strong)NSData *UserNameEncoding;
@property(nonatomic,strong)NSData *HardwareIdEncoding;
@end
LoginViewController.m
//url for creating the account /Accounts
//url for getting the token /Accounts/Token
-(void)loadPostRequest
{
_StoreIdentifierForVendor = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
_StoreTheModel = [UIDevice currentDevice].model;
_nickname = usernameTextField.text;
// AccountsClass is where I store the login information.
AccountsClass *AccountInfo = [[AccountsClass alloc] init];
AccountInfo.NickName = _nickname;
AccountInfo.HardwareId =[[[UIDevice currentDevice]identifierForVendor]UUIDString];
AccountInfo.DeviceType =[UIDevice currentDevice].model;
// NSLog(@"HardwareId test %@",AccountInfo.HardwareId);
/// ********* I can create accounts successfully ********* ///
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[AccountsClass class]];
[responseMapping addAttributeMappingsFromArray:@[@"NickName", @"HardwareId", @"DeviceType",@"AccountId"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *AccountDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:statusCodes];
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping]; // objectClass == NSMutableDictionary
[requestMapping addAttributeMappingsFromArray:@[@"NickName", @"HardwareId", @"DeviceType",@"AccountId"]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[AccountInfo class] rootKeyPath:nil method:RKRequestMethodAny];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"https://picquizstage.azurewebsites.net"]];
[manager addRequestDescriptor:requestDescriptor];
[manager addResponseDescriptor:AccountDescriptor];
// POST to create
[manager postObject:AccountInfo path:@"/Accounts" parameters:nil success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
/// This is where I setup data for base 64 encoding ///
// NSData *UserNameEncoding =[_nickname dataUsingEncoding:NSUTF8StringEncoding];
// NSData *HardwareIdEncoding = [_StoreIdentifierForVendor dataUsingEncoding:NSUTF8StringEncoding];
UserNameEncoding =[_nickname dataUsingEncoding:NSUTF8StringEncoding];
HardwareIdEncoding = [_StoreIdentifierForVendor dataUsingEncoding:NSUTF8StringEncoding];
//encoding
// NSString *encodedName = [UserNameEncoding base64EncodedString];
// NSString *encodedHardwareId = [HardwareIdEncoding base64EncodedString];
encodedName =[UserNameEncoding base64EncodedString];
encodedHardwareId =[HardwareIdEncoding base64EncodedString];
//encoding test
// NSAssert([encodedName isEqualToString:@"WVlZWQ=="], @"output id for the nickname is failed");
// NSAssert([encodedHardwareId isEqualToString:@"OUU0RDY0NzUtN0UwOS00RDY3LUJBNEItRTcyRjEyMzg5QUZC"], @"output test for the hardware id is failed");
// RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
/*
******* TOKEN OBJECT MAPPING BEGINS HERE (PROBLEMATIC PART) *******
*/
RKObjectMapping *TokenMapping = [RKObjectMapping mappingForClass:[TokenObject class]];
[TokenMapping addAttributeMappingsFromDictionary:@{@"tokenId":@"Id",@"TokenValidity":@"TokenValidity",@"ValidTo":@"ValidTo",@"Parameters":@"Parameters",@"Token":@"Token"}];
NSIndexSet *statusCodes2 = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor =[RKResponseDescriptor responseDescriptorWithMapping:TokenMapping method:RKRequestMethodGET pathPattern:@"/Accounts/Token" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKObjectMapping *errorMapping =[RKObjectMapping mappingForClass:[RKErrorMessage class]];
// The entire value at the source key path containing the errors maps to the message
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"message"]];
// Any response in the 4xx status code range with an "errors" key path uses this mapping
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:TokenMapping method:RKRequestMethodAny pathPattern:nil keyPath:@"errors" statusCodes:statusCodes2];
// Add our descriptors to the manager
RKObjectManager *manager2 = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"https://picquizstage.azurewebsites.net"]];
[manager2 addResponseDescriptorsFromArray:@[ responseDescriptor, errorDescriptor ]];
[manager2.HTTPClient setAuthorizationHeaderWithUsername:encodedName password:encodedHardwareId];
[manager2 getObjectsAtPath:@"/Accounts/Token" parameters:nil success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
TokenObject *myObjects =[[TokenObject alloc]init];
NSLog(@"The tokenId is successful %@",myObjects.tokenId);
NSLog(@"The ValidTo is successful %@",myObjects.ValidTo);
NSLog(@"The TokenValidity is successful %@",myObjects.TokenValidity);
RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
}
];
} failure:nil];
}
Console Output
2013-10-29 09:46:47.641 GuessTheImage[9651:70b] I restkit:RKLog.m:33 RestKit logging initialized...
2013-10-29 09:47:20.035 GuessTheImage[9651:70b] LoginViewController - Submit Action
2013-10-29 09:47:20.108 GuessTheImage[9651:70b] I restkit.network:RKObjectRequestOperation.m:150 POST 'https://picquizstage.azurewebsites.net/Accounts'
2013-10-29 09:47:20.572 GuessTheImage[9651:4d0f] I restkit.network:RKObjectRequestOperation.m:220 POST 'https://picquizstage.azurewebsites.net/Accounts' (201 Created / 1 objects) [request=0.4625s mapping=0.0015s total=0.5414s]
2013-10-29 09:47:20.580 GuessTheImage[9651:70b] T restkit.network:RKObjectRequestOperation.m:148 GET 'https://picquizstage.azurewebsites.net/Accounts/Token':
request.headers={
Accept = "application/json";
"Accept-Language" = "en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5";
Authorization = "Basic Vm05c2EyOW1aZz09Ok5rTkJOa015UXpVdE5qVTJOaTAwTVRFekxVSXpSVGt0TUVWRFJUQTBOak5EUWpJMg==";
"User-Agent" = "GuessTheImage/1.0 (iPhone Simulator; iOS 7.0.3; Scale/2.00)";
}
request.body=(null)
2013-10-29 09:47:20.677 GuessTheImage[9651:4d0f] E restkit.network:RKObjectRequestOperation.m:547 Object request failed: Underlying HTTP request operation failed with error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x8c546f0 {NSLocalizedRecoverySuggestion=, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x8ba00e0> { URL: https://quizstage.azurewebsites.net/Acounts/Tokn }, NSErrorFailingURLKey=https://quizstage.azurewebsites.net/Acounts/Tokn, NSLocalizedDescription=Expected status code in (200-299), got 401, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x8c6fb90> { URL: https://quizstage.azurewebsites.net/Acounts/Tokn } { status code: 401, headers {
"Cache-Control" = "no-cache";
"Content-Length" = 0;
Date = "Tue, 29 Oct 2013 16:47:20 GMT";
Expires = "-1";
Pragma = "no-cache";
Server = "Microsoft-IIS/8.0";
"Www-Authenticate" = "Basic Scheme='PizQuiz' location=http://{0}/Acount/Tokn";
"X-AspNet-Version" = "4.0.30319";
"X-Powered-By" = "ASP.NET";
} }}
2013-10-29 09:47:20.679 GuessTheImage[9651:4d0f] E restkit.network:RKObjectRequestOperation.m:208 GET 'https://picquizstage.azurewebsites.net/Acounts/Tokn' (401 Unauthorized / 0 objects) [request=0.0968s mapping=0.0000s total=0.0986s]:
error=Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x8c546f0 {NSLocalizedRecoverySuggestion=, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x8ba00e0> { URL: https://picquizstage.azurewebsites.net/Acounts/Tokn }, NSErrorFailingURLKey=https://picquizstage.azurewebsites.net/Acounts/Tokn, NSLocalizedDescription=Expected status code in (200-299), got 401, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x8c6fb90> { URL: https://picquizstage.azurewebsites.net/Acounts/Tokn } { status code: 401, headers {
"Cache-Control" = "no-cache";
"Content-Length" = 0;
Date = "Tue, 29 Oct 2013 16:47:20 GMT";
Expires = "-1";
Pragma = "no-cache";
Server = "Microsoft-IIS/8.0";
"Www-Authenticate" = "Basic Scheme='PizQuiz' location=http://{0}/Acount/Tokn";
"X-AspNet-Version" = "4.0.30319";
"X-Powered-By" = "ASP.NET";
} }}
response.body=