Finding and auto-filling HTML login forms in a UIWebView using JavaScript

14,216

Solution 1

OK, so there's no one-size-fits-all for this. You can get pretty close, but it will never work on every website, since a website could possibly have multiple login forms, or multiple fields that make up the username (some banks have that).

But this will get you in the right direction. To get all the password fields (mostly just one), use this:

document.querySelectorAll("input[type='password']")

To get all the text input fields, use this:

document.querySelectorAll("input[type='text']")

For the text input fields, you'll most likely get multiple results (maybe search fields and stuff). So you'll have to iterate over them and look for common IDs or names, like "username", "user", "user_name", "UID" and so on.

This is how you could use it in Objective-C:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *savedUsername = @"peter";
    NSString *savedPassword = @"Pan123";

    if (savedUsername.length != 0 && savedPassword.length != 0) { 
        //create js strings
        NSString *loadUsernameJS = [NSString stringWithFormat:@"var inputFields = document.querySelectorAll(\"input[type='text']\"); \
        for (var i = inputFields.length >>> 0; i--;) { inputFields[i].value = '%@';}", savedUsername];
        NSString *loadPasswordJS = [NSString stringWithFormat:@"document.querySelectorAll(\"input[type='password']\").value ='%@'", savedPassword];

        //autofill the form
        [self.webView stringByEvaluatingJavaScriptFromString: loadUsernameJS];
        [self.webView stringByEvaluatingJavaScriptFromString: loadPasswordJS];
    }
}

Please note: It fills every textfield with the username and only the first password field with the password.

Enjoy.

Solution 2

Based on a previous answer I created this 3 methods.

This method will fill the username/email fields it should work on a lot of cases :

- (void)completeUserFieldsForWebView:(UIWebView *)webView withUsername:(NSString *)username {

    NSString *loadUsernameJS =
    [NSString stringWithFormat:@"var inputFields = document.querySelectorAll(\"input[type='email']\"); \
     for (var i = inputFields.length >>> 0; i--;) { inputFields[i].value = '%@';}", username];
    NSString *loadText =
    [NSString stringWithFormat:@"var inputFields = document.querySelectorAll(\"input[type='text']\"); \
     for (var i = inputFields.length >>> 0; i--;) { inputFields[i].value = '%@';}", username];
    [webView stringByEvaluatingJavaScriptFromString: loadUsernameJS];
    [webView stringByEvaluatingJavaScriptFromString: loadText];

}

This one will fill the password fields:

- (void)completePasswordFieldsForWebView:(UIWebView *)webView withPassword:(NSString *)password {

    NSString *loadPasswordJS =
    [NSString stringWithFormat:@"var passFields = document.querySelectorAll(\"input[type='password']\"); \
     for (var i = passFields.length>>> 0; i--;) { passFields[i].value ='%@';}", password];
    [webView stringByEvaluatingJavaScriptFromString: loadPasswordJS];

}

And to perfom the sign-in :

- (void)clickOnSubmitButtonForWebView:(UIWebView *)webView {

    NSString *performSubmitJS = @"var passFields = document.querySelectorAll(\"input[type='submit']\"); \
    passFields[0].click()";
    [webView stringByEvaluatingJavaScriptFromString:performSubmitJS];

} 
Share:
14,216
at0m87
Author by

at0m87

Updated on June 05, 2022

Comments

  • at0m87
    at0m87 almost 2 years

    i have a UIWebView which acts like an internet browser and loads the HTML of the webpages that it is at.

    in the webViewController, the method webViewDidFinishLoad, would have loaded the HTML from the webpage when the webpage finish loading on the UIWebView.

    From the HTML i would like to sieve out textfields to facilitate the auto population of that textfield with values stored in my database.

    Any methods to do that? The method should be able to work on all websites.

    Set text for a textfield in UIWebView has almost what might help me, but i have tried it and the text field never got filled.

    In a login page there will be two text fields so i tried using

    document.getElementsByTagName('INPUT')[0].value
    document.getElementsByTagName('INPUT')[1].value 
    

    to input in the values but no magic.

    Edit: i have tried other position in the array. the username and password for Facebook is [3] and[4] whereas for amazon it is [11] and [14]. so the position of where the fields are using the above method is kinda random. Any other suggestion that will work for all website?

    Edit2: i could try

    document.getElementsById('id_name').value
    

    but the ID method is not going to work for me as i need a universal method that will identify textfields on any websites(all websites uses different ID names)

    Also it seems to me that some of the websites have consistently set this tabindex="1" for username and tabindex="2" for password.

    for instance in the webpage like Facebook:

    <input type="text" class="inputtext" name="email" id="email" value="" tabindex="1" />
    <input type="password" class="inputtext" name="pass" id="pass" tabindex="2" /> 
    

    amazon:

    <input id="ap_email" name="email" value="" type="email" size="30" maxlength="128" tabindex="1" autocorrect="off" autocapitalize="off" />
    <input id="ap_password" name="password" type="password" maxlength="1024" size="20"  tabindex="2" onkeypress="displayCapsWarning(event,'ap_caps_warning', this);" class="password"/> 
    

    dbs bank:

    <input type="text" tabindex="1" maxlength="20" size="32" name="UID" id="UID">
    <input type="password" onkeyup="keyUp(event)" onkeydown="return onlyNumerics(event)" tabindex="2" maxlength="9" size="32" name="PIN" id="PIN" autocomplete="off">
    

    but i didn't see this tabindex in google:

    <input type="text" spellcheck="false" name="Email" id="Email" value="">
    <input type="password" name="Passwd" id="Passwd">
    

    any suggestion?

    The Ultimate goal is to be able to sieve out Username and Password Text field for all/any websites. =)

  • at0m87
    at0m87 about 12 years
    How should i apply this code? NSString *idontknow = [NSString stringWithFormat:@"document.querySelectorAll("input [type='password']")"]; NSLog(@"idontknow %@", idontknow); Error: expected ":" -> input":"[type....
  • at0m87
    at0m87 about 12 years
    i have placed this code in the method webViewDidFinishLoad, but the last part where you said load the amended HTML back into the UIWebView, it is calling the web view to load again and its stuck in a cycle. Should i not place this code in webViewDidFinishLoad and somewhere else instead?
  • at0m87
    at0m87 about 12 years
    May i also ask, in firstInputRange, are you finding the very first "<input" in the document?
  • Johannes Fahrenkrug
    Johannes Fahrenkrug about 12 years
    I don't think that't the most effective way if all you want to do is auto-fill some form elements.
  • Ian L
    Ian L about 12 years
    @Johannes - you're right. I was unaware that you could inject the javascript back into the page using this method. By setting the element's value attribute, it does indeed update the HTML. Well, you live and learn :)
  • Johannes Fahrenkrug
    Johannes Fahrenkrug about 12 years
    @IanL no problem :) Your code might come in handy in certain situations too :)
  • Johannes Fahrenkrug
    Johannes Fahrenkrug about 12 years
    @at0m87 Please don't forget to up-vote and accept answers if they are helpful to you. Thanks!
  • at0m87
    at0m87 about 12 years
    Thanks Johannes! your answer was very helpful and pointed me in the right direction. I would like to point out that the password fields are not autofilled unless you change the code as so: NSString *loadPassPassword = [NSString stringWithFormat:@"var passFields = document.querySelectorAll(\"input[type='password']\"); \ for (var i = passFields.length>>> 0; i--;) { passFields[i].value ='%@';}", savedPassword];
  • at0m87
    at0m87 about 12 years
    you were saying about using common IDs or names to iterate for more accurate result. Is there any way i can search using like a wildcard value? say if the string contains mail and such?
  • at0m87
    at0m87 about 12 years
    i tried something like this, but it doesn't seem to be able to catch anything. am i doing it wrongly? for (var i = inputFields.length >>> 0; i--;) { \ if ( inputFields[i].getAttribute('name') == 'email'){ \ inputFields[i].value = '%@';}}", savedUsername];
  • at0m87
    at0m87 about 12 years
    i found out for the above code, it is case sensitive, 'email' != 'Email'
  • Johannes Fahrenkrug
    Johannes Fahrenkrug about 12 years
    For that you'll have to learn regular expressions. You can start here: stackoverflow.com/questions/4736/learning-regular-expression‌​s and I really like rubular.com
  • at0m87
    at0m87 about 12 years
    Interesting! i tried this on Rubular: (?<name>\w(e|m|a|i|l|u|s|e|r|n|a|m|e||I|D)) and it can match most of the names used for the username field. So the question again is how to apply this to objectiveC?
  • Martijn Mellens
    Martijn Mellens about 10 years
    NSString *loadUsernameJS = [NSString stringWithFormat:@"var inputFields = document.getElementsByTagName('input'); \ for (var i = inputFields.length >>> 0; i--;) { if(inputFields[i].name == 'username') inputFields[i].value = '%@';}", savedUsername]; NSString *loadPasswordJS = [NSString stringWithFormat:@" \ for (var i = inputFields.length >>> 0; i--;) { if(inputFields[i].name == 'password') inputFields[i].value = '%@';}", savedPassword]; worked for me!
  • colin lamarre
    colin lamarre about 10 years
    why is there >>> ? kinda hard to search for it :) but i tried.
  • Yoko
    Yoko almost 9 years
    Is it possible to just read a certain piece of text from the page? I want a database of countries and their dial codes and I know you can probably download that somewhere, but what do you learn by doing that? Is it possible to write a script that loads countrycodes.org, searches a new country every 2 seconds and saves that country name and dial code? Thx
  • Naman Vaishnav
    Naman Vaishnav over 7 years
    can you please help me in how to Put icon inside input element . . .