Working with the ArcGIS Server SOAP API - Implementation  

Using secured Web services and tokens


ArcGIS Server Web services may be secured to permit only authorized users. The way to work with a secured service depends on how the service authenticates users. At 9.3, an ArcGIS Server instance can use one of two authentication methods: HTTP authentication (including Windows authentication) or token-based authentication. See the ArcGIS Server Help for information on authentication methods for Web services, or contact your server administrator to determine which approach is used for your services.


HTTP/Windows authentication

Services using this method issue a challenge in response to a request, and the client must respond with appropriate credentials to authenticate the client. One common way this is implemented is with Internet Information Services (IIS), where the application is set to not allow anonymous access. The client may be authenticated in one of several ways, including Basic, Digest, or Integrated Windows Authentication. To authenticate the request, as a developer you must set the identity within the request. When using the Web ADF service proxies, you can set the Identity property of the server proxy. When using the dynamic Web service proxy, you can set the identity by creating a new System.Net.NetworkCredential and assigning it to Credentials property of the web service proxy.

[C#]
System.Net.CredentialCache credentialCache = new System.Net.CredentialCache();
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("user1", "user1");
// In following line, 2nd arg is auth method - Basic & Digest the only options
credentialCache.Add(new Uri(mapProxy.Url), "Basic", credentials);
// Use the credentials for the proxy
mapserver.Credentials = credentialCache;
			
[VB]
Dim credentialCache As New System.Net.CredentialCache()
Dim credentials As New System.Net.NetworkCredential("user1", "user1")
' In following line, 2nd arg is auth method - Basic & Digest the only options
credentialCache.Add(New Uri(mapProxy.Url), "Basic", credentials)
' Use the credentials for the proxy
mapserver.Credentials = credentialCache


Token-based authentication

This method is typically used when users are stored in a database or file, rather than as operating system users. To authenticate the request, you must obtain a token from the token service recognized by the ArcGIS Server instance. The token is appended to the query string of the Web service URL. If you have access to the user name and password in your server-side code, you should request the token dynamically. It is also possible to pre-create the token and embed it within the application, but dynamically created tokens are safer because they generally time out and hence will not be as useful to someone who might intercept the token.

To determine whether the server accepts or requires tokens, you can use the RequiresTokens method of the Catalog. Alternatively, if you make a request to a secured service without a token, you will receive an HTTP response with a code of 403 (access denied). If RequiresTokens is true, you can then obtain the URL of the token service with the GetTokenServiceURL() method.

[C#]
wsCatalog.Catalog myCatalog = new wsCatalog.Catalog();
myCatalog.Url = "http://MyWebServer/arcgis/services";
if (myCatalog.RequiresTokens())
{
    string tokenServiceUrl = myCatalog.GetTokenServiceURL();
}

[VB]
Dim myCatalog As New wsCatalog.Catalog()
myCatalog.Url = "http://MyWebServer/arcgis/services"
If myCatalog.RequiresTokens() Then
    Dim tokenServiceUrl As String = myCatalog.GetTokenServiceURL()
End If

Of course, you may already know the URL of the token service from the server's administrator. The token service is at a URL such as "http://MyWebServer/arcgis/tokens". Once you know the token service URL, you can request a token, assuming you have a valid user name and password for the ArcGIS Server instance. You can use the WebRequest class to make a request for the token. Note that the request may encounter problems such as an unresponsive server, incorrect password, etc. You should wrap the request in a try-catch block to deal with errors.

[C#]
string url = tokenServiceUrl + "?request=getToken&username=
myuser&password=secret"; System.Net.WebRequest request = System.Net.WebRequest.Create(url);

string myToken, errorMsg;
try {
   System.Net.WebResponse response = request.GetResponse();
   System.IO.Stream responseStream = response.GetResponseStream();
   System.IO.StreamReader readStream = new System.IO.StreamReader(responseStream);

   myToken = readStream.ReadToEnd();
}
catch (WebException we) {
   if (we.Message.Contains("403")
      errorMsg = "Server returned forbidden (403) code."
}

[VB]
Dim url As String = tokenServiceUrl + "?request=getToken&username=myuser&password=
secret" Dim request As System.Net.WebRequest = System.Net.WebRequest.Create(url)

Dim myToken, errorMsg As String
Try
   Dim response As System.Net.WebResponse = request.GetResponse()
   Dim responseStream As System.IO.Stream = response.GetResponseStream()
   Dim readStream As System.IO.StreamReader = new System.IO.StreamReader(responseStream)

   myToken = readStream.ReadToEnd()

Catch we As WebException
   If we.Message.Contains("403") Then
      errorMsg = "Server returned forbidden (403) code."
   End If
End Try

If incorrect credentials are sent, or HTTP was used when HTTPS was required, an HTTP response code of 403 (forbidden) will be received. This will be in an exception thrown as shown in the sample code above. Otherwise, the token may be read from the response.

The token will be a long string of characters. It must be appended to the URL of the web service endpoint with each request. You do not need to include it within the request itself. The example above for the map server would be modified to include the token:

[C#]
wsmap.AMapServiceName_MapServer mapserver = new wsmap.AMapServiceName_MapServer();
mapserver.Url = "http://MyWebServer/arcgis/services/MyMapServiceName/MapServer?token=" + myToken;

[VB]
wsmap.AMapServiceName_MapServer mapserver = New wsmap.AMapServiceName_MapServer()
mapserver.Url = "http://MyWebServer/arcgis/services/MyMapServiceName/MapServer?token=" + myToken

Tokens expire within a time period designated by the server administrator. The expiration timeout window may vary from a few minutes to several days. Currently there is no programmatic method to ascertain the token timeout. Therefore you must account for token expiration in your code, and obtain a new token when required.

Currently the way to detect timeout of a token is to catch the exception thrown and to check the response code. A code of 498 indicates an expired or otherwise invalid token. A code of 403 or 499 indicates that a token is required (if no token was submitted). Once you determine that a new token is needed, you can request one, update the server's URL with the token, and repeat the request.

[C#]
wsmap.MapImage mapimg = null;
try {
    mapimg = mapserver.ExportMapImage(mapdesc, imgdesc);
}
catch (System.Net.WebException webExc) {
    System.Net.HttpWebResponse webResp = webExc.Response as System.Net.HttpWebResponse;
    if (webResp != null) {
        int statusCode = (int)webResp.StatusCode;
        if (statusCode == 498) {
            // call a method (not shown here) that obtains a new token
            string newToken = getToken();
	        
            mapserver.Url = "http://MyWebServer/arcgis/services/MyMapServiceName/MapServer?token=" + newToken;
            mapimg = mapserver.ExportMapImage(mapdesc, imgdesc);
        }
    }
}

[VB]
Dim mapimg As wsmap.MapImage = Nothing 
Try
    mapimg = mapserver.ExportMapImage(mapdesc, imgdesc)
Catch webExc As System.Net.WebException
    Dim webResp As System.Net.HttpWebResponse = webExc.Response as System.Net.HttpWebResponse 
    If webResp IsNot Nothing Then
        Dim statusCode As Integer = CType(webResp.StatusCode, Integer)
        If statusCode = 498 Then
            ' call a method (not shown here) that obtains a new token
            Dim NewToken As String = getToken() 
 
            mapserver.Url = "http://MyWebServer/arcgis/services/MyMapServiceName/MapServer?token=" + NewToken
            mapimg = mapserver.ExportMapImage(mapdesc, imgdesc)
        End If
    End If
End Try

See the sample ArcGIS_SOAP_MapServerBrowser, under the Desktop Client Applications samples for ArcGIS Server, for a complete example of  obtaining, using and renewing tokens. This sample includes a TokenGenerator class that may be used in your application for retrieving and renewing tokens.