Search code examples
asp.net-web-apiwindows-server-2012

What are the AppName, AppPublisher and AppVersion header values for a WSE 2012 R2 WebApi call?


I'm trying to query my Server 2012 Essentials R2 server to determine the most recent Client Backup time for a given Device, so I can display nag screens at signon for forgetful users. (They're on laptops, so I can't depend on the machine being available during the automatic window.)

The closest thing in the way of documentation I've been able to find is this: (https://msdn.microsoft.com/en-us/library/jj713757.aspx)

GET services/builtin/DeviceManagement.svc/devices/index/{index}/count/{count}

But it requires a preceding call to get the token: (https://msdn.microsoft.com/en-us/library/jj713753.aspx)

GET https://www.contoso.com/services/builtin/session.svc/login HTTP/1.1
Accept: application/xml
Host: servername
Authorization: Basic VXNlcjpQYXNzd29yZCE=
AppName: Sample App Name
AppPublisher: publisher
AppVersion: 1.0

Does anyone know what the values for those last three headers should be—or how to discover them—for a standard WSE 2012 R2 installation? The documentation provides no assistance here.

Or if someone knows a better way to accomplish this, please let me know.


Solution

  • OK, I got it working. The code is below.

    As it turns out, the value of the AppName header is irrelevant—it can be any string, but it can't be empty.

    I already knew it couldn't be empty from a look at the WSE source in Wssg.WebApi.Framework in the GAC, but the code is decoupled to the point that it's next to impossible to find out what process picks up the the RemoteConnectionClientInfo object once it gets dropped into the HTTP session.

    The part that was misleading me was—go figure—the documentation itself.

    There's a bang (!) after the password on the Authentication page, suggesting that it should trail the actual password prior to encoding. This was why I was getting an authentication error, which in turn I was (mistakenly) attributing to the statement in the documentation: "Add Appname, Apppublisher, and Appversion values in HTTP header fields. These values are also required to log on."

    So once I cleared all that up, I sailed right in.

    And there are other errors in the documentation. On the Devices page we are told that the Host header should be set to the domain name, and that a Content-Length header should be added.

    These are both incorrect. The Host header should be the server's hostname and there should be no Content-Length header (that's a response header, not a request header).

    AND...! After all this, I find that the Device info returned doesn't contain the most recent backup time. I'll have to dig further for that. But at least now I can connect.

    So Microsoft's incomplete, inaccurate and sloppy documentation has cost me a day's work. Hopefully somebody else can use this and avoid the pain I went through.

    Module Main
      Public Sub Main()
        Dim aCredentials() As Byte
    
        Dim _
          oAuthenticateUri,
          oDeviceListUri As Uri
    
        Dim _
          sCanary,
          sCookie,
          sDevices As String
    
        aCredentials = Encoding.ASCII.GetBytes($"{USERNAME}:{PASSWORD}")
    
        Using oClient As New HttpClient
          oAuthenticateUri = New Uri($"https://{HOST}/services/builtin/session.svc/login")
          oDeviceListUri = New Uri($"https://{HOST}/services/builtin/devicemanagement.svc/devices/index/0/count/99")
    
          oClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/xml"))
          oClient.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Basic", Convert.ToBase64String(aCredentials))
          oClient.DefaultRequestHeaders.Host = HOST
          oClient.DefaultRequestHeaders.Add("AppPublisher", String.Empty)
          oClient.DefaultRequestHeaders.Add("AppVersion", String.Empty)
          oClient.DefaultRequestHeaders.Add("AppName", "None")
    
          Using oAuthenticateResponse As HttpResponseMessage = oClient.GetAsync(oAuthenticateUri).Result
            If oAuthenticateResponse.IsSuccessStatusCode Then
              sCanary = oAuthenticateResponse.Headers.Single(Function(Pair) Pair.Key = CANARY_HEADER).Value(0)
              sCookie = Split(oAuthenticateResponse.Headers.Single(Function(Pair) Pair.Key = COOKIE_HEADER).Value(0), ";")(0)
    
              oClient.DefaultRequestHeaders.Clear()
              oClient.DefaultRequestHeaders.Host = HOST
              oClient.DefaultRequestHeaders.Add(CANARY_HEADER, sCanary)
              oClient.DefaultRequestHeaders.Add(COOKIE_HEADER, sCookie)
    
              Using oDeviceListResponse As HttpResponseMessage = oClient.GetAsync(oDeviceListUri).Result
                If oDeviceListResponse.IsSuccessStatusCode Then
                  sDevices = oDeviceListResponse.Content.ReadAsStringAsync.Result
                Else
                  Console.WriteLine("{0} ({1})", oDeviceListResponse.StatusCode, oDeviceListResponse.ReasonPhrase)
                End If
              End Using
            Else
              Console.WriteLine("{0} ({1})", oAuthenticateResponse.StatusCode, oAuthenticateResponse.ReasonPhrase)
            End If
          End Using
        End Using
      End Sub
    
    
    
      Private Const CANARY_HEADER As String = "Canary"
      Private Const COOKIE_HEADER As String = "Set-Cookie"
      Private Const USERNAME As String = "domain.admin"
      Private Const PASSWORD As String = "admin.password"
      Private Const HOST As String = "server"
    End Module