Question

I have an iOS application which will submit a username and password to a website and to retrieve the HTML code of the webpage which loads after pressing 'Sign in': https://grades.bsd405.org/Pinnacle/Gradebook/Logon.aspx?.

The form data fields are the following:

__LASTFOCUS, __EVENTTARGET, __EVENTARGUMENT, __VIEWSTATE, __EVENTVALIDATION, ctl00$ContentPlaceHolder$Username, ctl00$ContentPlaceHolder$Password, ctl00$ContentPlaceHolder$lstDomains, ctl00$ContentPlaceHolder$LogonButton, PageUniqueId

This is the code that I have so far:

NSURL * url = [NSURL URLWithString:@"https://grades.bsd405.org/Pinnacle/Gradebook/Logon.aspx?ReturnUrl=%2fPinnacle%2fGradebook%2fDefault.aspx"];

NSString *post = @"__LASTFOCUS&__EVENTTARGET&__EVENTARGUMENT&__VIEWSTATE=/wEPDwUJNTkxNzI3MDIzD2QWAmYPZBYCAgMPZBYGAgEPZBYCAgkPZBYCAgEPZBYIAgMPFgIeB1Zpc2libGVoZAIFDxYCHwBoZAIHDxYCHwBoZAIJDxYCHgVzdHlsZQUjdmVydGljYWwtYWxpZ246bWlkZGxlO2Rpc3BsYXk6bm9uZTtkAgMPDxYCHwBoZGQCBQ9kFghmD2QWAgINDxYCHgVjbGFzcwUQc2luZ2xlU2Nob29sTGlzdBYCAgEPZBYCAgEPEGQPFgFmFgEQBQ5EZWZhdWx0IERvbWFpbgUIUGlubmFjbGVnZGQCAg9kFgICEw9kFgICAQ9kFgICAQ8QZGQWAGQCBw8PFgIeBFRleHQFIFBpbm5hY2xlIEdyYWRlIDIwMTIgV2ludGVyIEJyZWFrZGQCCA8PFgIfAwU3Q29weXJpZ2h0IChjKSAyMDEzIEdsb2JhbFNjaG9sYXIuICBBbGwgcmlnaHRzIHJlc2VydmVkLmRkZP/l6irI9peZfyqpKjk3fwLuEbos&__EVENTVALIDATION=/wEWBgKjnbqUCQLnksmgAQKTpbWbDgLB5+KIBAL4xb20BAK20ZqiCel6sQLBsF1W3XHOxpgq+tJj+Rx2&ctl00$ContentPlaceHolder$Username=TESTUSERNAME&ctl00$ContentPlaceHolder$Password=TESTPASSWORD&ctl00$ContentPlaceHolder$lstDomains=Pinnacle&ctl00$ContentPlaceHolder$LogonButton=Signin&PageUniqueId=2dacba26-bb0d-412f-b06a-e02caf039c4b";

NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

[self clearCookiesForURL:url];
[request setURL:url];

[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];

NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];

However, an error page is always returned. What am I doing wrong? Also I implemented the following:

-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"HEREEE");
if ([challenge previousFailureCount] == 0) {
    NSLog(@"received authentication challenge");
    NSURLCredential *newCredential = [NSURLCredential credentialWithUser:@"USERNAME"
                                                                password:@"PASSWORD"
                                                             persistence:NSURLCredentialPersistenceForSession];
    NSLog(@"credential created");
    [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
    NSLog(@"responded to authentication challenge");
}
else {
    NSLog(@"previous authentication failure");
}

}

But this method is never called!

My View controller conforms to :
NSURLConnectionDelegate,NSURLAuthenticationChallengeSender,NSURLConnectionDataDelegate

Thanks!

Was it helpful?

Solution

A couple of thoughts:

  1. I'm surprised by the presence of reserved characters in __VIEWSTATE and __EVENTVALIDATION. Per the x-www-form-urlencoded spec, you should be percent escaping characters other than *, -, ., 0-9, A-Z, _ and a-z.

    My standard percent-escaping routine for POST values is:

    - (NSString *)percentEscapeString:(NSString *)string
    { 
        NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                     (CFStringRef)string,
                                                                                     (CFStringRef)@" ",
                                                                                     (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                     kCFStringEncodingUTF8));
        return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    }
    
  2. I'm not sure it's critical, but you're missing = characters. So, rather than __LASTFOCUS&..., I'd expect __LASTFOCUS=&....

    Pulling these two points together, the resulting payload should look something like:

    __LASTFOCUS=&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwUJNTkxNzI3MDIzD2QWAmYPZBYCAgMPZBYGAgEPZBYCAgkPZBYCAgEPZBYIAgMPFgIeB1Zpc2libGVoZAIFDxYCHwBoZAIHDxYCHwBoZAIJDxYCHgVzdHlsZQUjdmVydGljYWwtYWxpZ246bWlkZGxlO2Rpc3BsYXk6bm9uZTtkAgMPDxYCHwBoZGQCBQ9kFghmD2QWAgINDxYCHgVjbGFzcwUQc2luZ2xlU2Nob29sTGlzdBYCAgEPZBYCAgEPEGQPFgFmFgEQBQ5EZWZhdWx0IERvbWFpbgUIUGlubmFjbGVnZGQCAg9kFgICEw9kFgICAQ9kFgICAQ8QZGQWAGQCBw8PFgIeBFRleHQFIFBpbm5hY2xlIEdyYWRlIDIwMTIgV2ludGVyIEJyZWFrZGQCCA8PFgIfAwU3Q29weXJpZ2h0IChjKSAyMDEzIEdsb2JhbFNjaG9sYXIuICBBbGwgcmlnaHRzIHJlc2VydmVkLmRkZP%2Fl6irI9peZfyqpKjk3fwLuEbos&__EVENTVALIDATION=%2FwEWBgKjnbqUCQLnksmgAQKTpbWbDgLB5%2BKIBAL4xb20BAK20ZqiCel6sQLBsF1W3XHOxpgq%2BtJj%2BRx2&ctl00%24ContentPlaceHolder%24Username=a&ctl00%24ContentPlaceHolder%24Password=b&ctl00%24ContentPlaceHolder%24lstDomains=Pinnacle&ctl00%24ContentPlaceHolder%24LogonButton=Sign+in&PageUniqueId=a3113b01-cf2f-4081-8a1a-d8557e7347de
    

    Note the occurrences of percent escaping throughout the body of that request. I determined the body by looking at the request in Charles.

  3. I'm surprised to see __VIEWSTATE, __EVENTVALIDATION, and PageUniqueId hard-coded like this. I could easily imagine that it would be problematic to use fixed values for these variables. It depends entirely upon how the web site was programmed. I'm gathering that you're trying to programmatically access a web site that isn't a web service, but rather is a HTML interface, and that can be problematic.

    You probably have to perform GET request with Logins.aspx first, before trying to log in, and parse these values from that initial login HTML. When trying to programmatically interact with web server, you have to be careful to call all of the pages a web browser would, as there are going to be server state variables like this that your app should be using, too.

  4. Unrelated, but you don't need to specify Content-length, as NSURLConnection will supply that for you based upon the size of your the httpBody.

  5. If you get to the point of using your didReceiveAuthenticationChallenge, note that you should ensure that the else clause calls cancelAuthenticationChallenge or continueWithoutCredentialForAuthenticationChallenge. You always have to call one of these three challenge methods.

    Having said that, it's not clear that didReceiveAuthenticationChallenge would be called at all. Your server could be employing some server app-level authentication that is not a challenge-based.

Bottom line, it's hard for us to diagnose the root of the problem on the basis of what's been provided thus far. I would contact the author of the HTML interface and inquire about specifics of the authentication process.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top