Handling Authentication

Some proxies and servers require authentication before granting access to resources on the Internet. The WinINet functions support server and proxy authentication for http sessions. Authentication of ftp servers must be handled by the InternetConnect function. Currently, FTP gateway authentication is not supported.

About HTTP Authentication

If authentication is required, the client application receives a status code 401, if the server requires authentication, or 407, if the proxy requires authentication. With the status code, the proxy or server sends one, or more, authenticate response headers—Proxy-Authenticate (for proxy authentication) or WWW-Authenticate (for server authentication).

Each authenticate response header contains an available authentication scheme and a realm. If multiple authentication schemes are supported, the server returns multiple authenticate response headers. The realm value is case-sensitive and defines a protection space on the proxy or server. For example, the header "WWW-Authenticate: Basic Realm="example"" would be an example of a header returned when server authentication is required.

The client application that sent the request can authenticate itself by including an Authorization header field with the request. The Authorization header would contain the authentication scheme and the appropriate response required by that scheme. For example, the header "Authorization: Basic <username:password>" would be added to the request and re-sent to the server if the client received the authenticate response header "WWW-Authenticate: Basic Realm="example"".

There are two general types of authentication schemes:

  • Basic authentication scheme, where the user name and password are sent in cleartext to the server.
  • Challenge-response schemes, which allow for a challenge-response format.

The Basic authentication scheme is based on the model that a client must authenticate itself with a user name and password for each realm. The server services the request if it is resent with an Authorization header that includes a valid user name and password.

Challenge-response schemes enable more secure authentication. If a request requires authentication using a challenge-response scheme, the appropriate status code and Authenticate headers are returned to the client. The client must then to resend the request with a negotiate. The server would return an appropriate status code with a challenge, and the client would then require to resend the request with the proper response to get the requested service.

The following table lists authentication schemes, the authentication type, the DLL that supports them, and a description of the scheme.

Scheme Type DLL Description
Basic (cleartext) basic Wininet.dll Uses a base64 encoded string that contains the user name and password.
Digest challenge-response Digest.dll A challenge-response scheme that challenges using a nonce (a server-specified data string) value. A valid response contains a checksum of the user name, the password, the given nonce value, the HTTP method, and the requested Uniform Resource Identifier (URI). Digest authentication support was introduced in Microsoft Internet Explorer 5.
NT LAN Manager (NTLM) challenge-response Winsspi.dll A challenge-response scheme that bases the challenge on the user name.
Microsoft Network (MSN) challenge-response Msnsspc.dll The Microsoft Network's authentication scheme.
Distributed Password Authentication (DPA) challenge-response Msapsspc.dll Similar to MSN authentication and is also used by the Microsoft Network.
Remote Passphrase Authentication (RPA) CompuServe Rpawinet.dll, da.dll CompuServe authentication scheme. For more information, see the RPA Mechanism Specifications.

 

For anything other than Basic authentication, the registry keys must be set up in addition to installing the appropriate DLL.

If authentication is required, the INTERNET_FLAG_KEEP_CONNECTION flag should be used in the call to HttpOpenRequest. The INTERNET_FLAG_KEEP_CONNECTION flag is required for NTLM and other types of authentication in order to maintain the connection while completing the authentication process. If the connection is not maintained, the authentication process must be restarted with the proxy or server.

The InternetOpenUrl and HttpSendRequest functions complete successfully even when authentication is required. The difference is, the data returned in the header files and InternetReadFile would receive an HTML page informing the user of the status code.

Registering Authentication Keys

INTERNET_OPEN_TYPE_PRECONFIG looks at the registry values ProxyEnable, ProxyServer, and ProxyOverride. These values are located under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings.

For authentication schemes other than Basic, a key must be added to the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. A DWORD value, Flags, should be set with the appropriate value. The following list shows the possible values for the Flags value.

  • PLUGIN_AUTH_FLAGS_UNIQUE_CONTEXT_PER_TCPIP (value=0x01)

    Each Transmission Control Protocol/Internet Protocol (TCP/IP) socket contains a different context. Otherwise, a new context is passed for each realm or block URL template.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI (value=0x02)

    This DLL can handle its own user input.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_NO_PASSWD (value=0x04)

    This DLL might be capable of doing an authentication without prompting the user for a password.

  • PLUGIN_AUTH_FLAGS_NO_REALM (value=0x08)

    This DLL does not use a standard http realm string. Any data that appears to be a realm is scheme-specific data.

  • PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED (value=0x10)

    This DLL does not require a persistent connection for its challenge-response sequence.

For example, to add NTLM authentication, the key NTLM must be added to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security\NTLM, the string value, DLLFile, and a DWORD value, Flags, must be added. DLLFile must be set to Winsspi.dll, and Flags must be set to 0x08.

Server Authentication

When a server receives a request that requires authentication, the server returns a 401 status code message. In that message, the server should include one or more WWW-Authenticate response headers. These headers include the authentication methods the server has available. WinINet chooses the first method it recognizes.

Basic authentication provides weak security unless the channel is first link-encrypted with SSL or PCT.

The InternetErrorDlg function can be used to obtain the user name and password data from the user, or a customized user interface can be designed to obtain the data.

A custom interface can use the InternetSetOption function to set the INTERNET_OPTION_PASSWORD and INTERNET_OPTION_USERNAME values and then resend the request to the server.

Proxy Authentication

When a client attempts to use a proxy that requires authentication, the proxy returns a 407 status code message to the client. In that message, the proxy should include one or more Proxy-Authenticate response headers. These headers include the authentication methods available from the proxy. WinINet chooses the first method it recognizes.

The InternetErrorDlg function can be used to obtain the user name and password data from the user, or a customized user interface can be designed.

A custom interface can use the InternetSetOption function to set the INTERNET_OPTION_PROXY_PASSWORD and INTERNET_OPTION_PROXY_USERNAME values and then resend the request to the proxy.

If no proxy user name and password are set, WinINet attempts to use the user name and password for the server. This behavior enables clients to implement the same customized user interface used to handle server authentication.

Handling HTTP Authentication

HTTP authentication can be handled with either InternetErrorDlg or a customized function that uses InternetSetOption or adds its own authentication headers. InternetErrorDlg can examine the headers associated with an HINTERNET handle to find hidden errors, such as status codes from a proxy or server. InternetSetOption can be used to set the user name and password for the proxy and server. For MSN and DPA authentication, InternetErrorDlg must be used to set the user name and password.

For any customized function that adds its own WWW-Authenticate or Proxy-Authenticate headers, the INTERNET_FLAG_NO_AUTH flag should be set to disable authentication.

The following example shows how InternetErrorDlg can be used to handle HTTP authentication.

HINTERNET hOpenHandle,  hConnectHandle, hResourceHandle;
DWORD dwError, dwErrorCode;
HWND hwnd = GetConsoleWindow();

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG, 
                           NULL, NULL, 0);

hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"), 
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL, 
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL, 
                                  INTERNET_FLAG_KEEP_CONNECTION, 0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

// dwErrorCode stores the error code associated with the call to
// HttpSendRequest.  

dwErrorCode = hResourceHandle ? ERROR_SUCCESS : GetLastError();

dwError = InternetErrorDlg(hwnd, hResourceHandle, dwErrorCode, 
                           FLAGS_ERROR_UI_FILTER_FOR_ERRORS | 
                           FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
                           FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
                           NULL);

if (dwError == ERROR_INTERNET_FORCE_RETRY)
    goto resend;

// Insert code to read the data from the hResourceHandle
// at this point.

In the example, dwErrorCode is used to store any errors associated with the call to HttpSendRequest. HttpSendRequest completes successfully, even if the proxy or server requires authentication. When the FLAGS_ERROR_UI_FILTER_FOR_ERRORS flag is passed to InternetErrorDlg, the function checks the headers for any hidden errors. These hidden errors would include any requests for authentication. InternetErrorDlg displays the appropriate dialog box to prompt the user for the necessary data. The FLAGS_ERROR_UI_FLAGS_GENERATE_DATA and FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS flags should also be passed to InternetErrorDlg, so that the function constructs the appropriate data structure for the error and stores the results of the dialog box in the HINTERNET handle.

The following example code shows how authentication could be handled using InternetSetOption.

HINTERNET hOpenHandle,  hResourceHandle, hConnectHandle;
DWORD dwStatus;
DWORD dwStatusSize = sizeof(dwStatus);
char strUsername[64], strPassword[64];

// Normally, hOpenHandle, hResourceHandle,
// and hConnectHandle need to be properly assigned.

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG,
                           NULL, NULL, 0);
hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"),
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL,
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL,
                                  INTERNET_FLAG_KEEP_CONNECTION,
                                  0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

HttpQueryInfo(hResourceHandle, HTTP_QUERY_FLAG_NUMBER |
              HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL);

switch (dwStatus)
{
    // cchUserLength is the length of strUsername and
    // cchPasswordLength is the length of strPassword.
    DWORD cchUserLength, cchPasswordLength;

    case HTTP_STATUS_PROXY_AUTH_REQ: // Proxy Authentication Required
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert appropriate error handling code.
        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_USERNAME,
                          strUsername,
                          cchUserLength+1);

        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_PASSWORD,
                          strPassword,
                          cchPasswordLength+1);
        goto resend;
        break;

    case HTTP_STATUS_DENIED:     // Server Authentication Required.
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert error handling code as
        // appropriate.
        InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME,
                          strUsername, cchUserLength+1);
        InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD,
                          strPassword, cchPasswordLength+1);
        goto resend;
        break;
}

// Insert code to read the data from the hResourceHandle
// at this point.

Note

WinINet does not support server implementations. In addition, it should not be used from a service. For server implementations or services use Microsoft Windows HTTP Services (WinHTTP).