Search code examples
asp.netregexmobilemobile-websiteuser-agent

ASP.net User Agent detection of mobile apps loading the site


We've successfully used the following asp.net regex to carry out server side detection of mobile browsers visiting our site (taken from http://detectmobilebrowsers.com/

Public Shared Function IsMobileBrowser(httpUserAgentString As String) As Boolean

    ' set up user agent sniffing (http://detectmobilebrowsers.com/)

    Dim b As New Regex("(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", RegexOptions.IgnoreCase)
    Dim v As New Regex("1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-", RegexOptions.IgnoreCase)

    If (b.IsMatch(httpUserAgentString) Or v.IsMatch(Left(httpUserAgentString, 4))) Then
        Return True
    Else
        Return False
    End If

End Function

However, we've had reports back of this failing when users opened the site from QR reading apps (Qrafter). This seems to be because of the app (in the case of Qrafter on iOS at least) 'testing' the site by loading a page in code, inside the app. The User Agent string passed when doing this didn't match the mobile browser test, and so we set a session variable storing the site mode as desktop/non-mobile, and then when the full load of the site occurred on the via the app, it started in its desktop mode rather than mobile.

The User Agent string the app presented to our ASP.net code was:

Qrafter/6.3+CFNetwork/609+Darwin/13.0.0 200 0 0 178

So, we worked around this with the following regex method to detect and ignore these 'in-app' loads of the site

Public Shared Function IsAppSniffer(httpUserAgentString As String) As Boolean

    Dim b As New Regex("\bCFNetwork\b.+\bDarwin\b", RegexOptions.IgnoreCase)

    If (b.Matches(httpUserAgentString).Count > 0) Then
        Return True
    Else
        Return False
    End If

End Function

Basically, I wondered if this was a reliable way to carry out this test, and what we'd have to look for to detect similar issues on Android / Windows / other phones running QRafter equivalents? What would be their app based User Agent strings? Or, is this possibly an issue specific to the QRafter app on iOS due to its 'URL test' feature?

Feel free to (constructively if poss!) criticise our method of storing the mobile / desktop site mode in the session object, but it's probably a little late for us to re-engineer that on this project.

Apologies for the long windedness of this question, hopefully it is still just about a Q&A fit & thanks very much in advance for any useful info.


Edit: As pointed out by Stephen below, in a nutshell my question is essentially, 'Is there a practical way to detect user agent strings sent directly from mobile devices (of any flavour) along with the ones from their mobile browsers?'

e.g. (for an iOS / Safari pairing) direct from an app

AppName/CFNetwork/609+Darwin/13.0.0 200 0 0 178

the Safari user agent string

Mozilla/5.0+(iPhone;+CPU+iPhone+OS+6_0_1+like+Mac+OS+X)+AppleWebKit/536.26+(KHTML,+like+Gecko)+Mobile/10A523 200 0 0 20

Solution

  • Basically, your issue sums up as: you can USUALLY detect the browser, but sometimes, you don't get an expected User Agent string, and you send out the desktop-version to a mobile device.

    The problem with your solution to this issue, is that you would need to modify the detection regex every time this situation pops up with a new program. While this could be done in a loop and a table in a database, it's still a lot of maintenance and hassle. Though, to be honest, that's the same with the other solutions as well. Someone has to maintain the listing of 'mobile' browsers. That can be yourself, or it can be a service which you pay for.

    What I would do, is have the desktop side of things perform a JavaScript detection of the browser and its version (How can you detect the version of a browser?). If it detects that it happens to be displaying itself on a mobile device, provide a link to take the user to the related mobile page.

    You could go a step farther, and even have this logic added to the page, if, and only if, the user agent is not in the list provided by the Detect Mobile Browsers site. Of course, that's probably a bit of overkill.

    Sadly, at some point, you'll just have to say "We gave it our best shot at getting someone to the proper view of the site" and be happy with how its working. There's only so much automated detection that can be performed in this manner, and program designers will happily try ways to break said logic, just because they can.