Some proxies and servers require authentication before allowing access to resources on the Internet. The Win32® Internet functions support server and proxy authentication for HTTP sessions. Authentication of Gopher and FTP servers must be handled by the InternetConnect function. Currently, there is no support available for FTP gateway authentication.
If authentication is required, the client program receives a status code of 401 (if the server requires authentication) or 407 (if the proxy requires authentication). Along with the status code, the proxy or server sends one or more authenticate response headersProxy-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 needed.
The client program 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:
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 re-sent with an Authorization header that includes a valid user name and password.
Challenge-response schemes allow for 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 then needs to re-send the request with a negotiate. The server would return an appropriate status code with a challenge, and the client would then need to re-send the request with the proper response to get the requested service.
The following table contains the authentication schemes that are used with the Win32 Internet functions, the authentication type, the DLLs that support 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 URI. Digest authentication support was introduced in 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 will also be used by the Microsoft Network. |
Remote Passphrase Authentication (RPA) | CompuServe | Rpawinet.dll, da.dll | CompuServe's authentication scheme. For more information, see http://www.compuserve.com/rpa/rpadoco.htm . |
For anything other than Basic authentication, the registry keys must be set up in addition to installing the appropriate DLL(s). For more information on setting these registry keys, see Registering Authentication Keys.
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 Win32 Internet functions InternetOpenUrl and HttpSendRequest complete successfully even when authentication is required. The difference is, the information returned in the header files and InternetReadFile would receive an HTML page informing the user of the status code.
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 needs to be added to the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. A string value, DLLFile, should contain the name of the DLL that supports the authentication scheme, and a DWORD value, Flags, should be set with the appropriate value. The following list shows the possible values for the Flags value.
For example, to add NTLM authentication, the key NTLM would need to 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, would need to be added. DLLFile would need to be set to Winsspi.dll, and Flags would need to be set to 0x08.
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. The Win32 Internet functions pick the first method they recognize.
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 information from the user, or a customized user interface can be designed to obtain the information.
A custom interface can use the InternetSetOption function to set the INTERNET_OPTION_PASSWORD and INTERNET_OPTION_USERNAME values and then re-send the request to the server.
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. The Win32 Internet functions pick the first method they recognize.
The InternetErrorDlg function can be used to obtain the user name and password information 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 re-send the request to the proxy.
If no proxy user name and password are set, the Win32 Internet functions attempt to use the user name and password for the server. This behavior allows clients to implement the same customized user interface that is used to handle server 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 the Win32 Internet API authentication.
The following example shows how InternetErrorDlg can be used to handle HTTP authentication.
HINTERNET hOpenHandle, hConnectHandle, hResourceHandle; DWORD dwError, dwErrorCode; hOpenHandle = InternetOpen("Example", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); hConnectHandle = InternetConnect(hOpenHandle, "www.server.com", INTERNET_INVALID_PORT_NUMBER, NULL, NULL, INTERNET_SERVICE_HTTP,0,0); hResourceHandle = HttpOpenRequest(hConnectHandle, "GET", "/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 information from the hResourceHandle // at this point.
In the example, dwErrorCode is used to store any errors associated with the call to HttpSendRequest. HttpSendRequest will complete 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 information. 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 shows how authentication could be handled using InternetSetOption.
HINTERNET hOpenHandle, hResourceHandle; DWORD dwError, dwStatus; DWORD dwStatusSize = sizeof(dwStatus); char strUsername[64], strPassword[64]; hOpenHandle = InternetOpen("Example", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); hConnectHandle = InternetConnect(hOpenHandle, "www.server.com", INTERNET_INVALID_PORT_NUMBER, NULL, NULL, INTERNET_SERVICE_HTTP,0,0); hResourceHandle = HttpOpenRequest(hConnectHandle, "GET", "/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) { case HTTP_STATUS_PROXY_AUTH_REQ: //Proxy Authentication Required // Insert code to set strUsername and strPassword. InternetSetOption(hResourceHandle, INTERNET_OPTION_PROXY_USERNAME, strUsername, strlen(strUsername)+1); InternetSetOption(hResourceHandle, INTERNET_OPTION_PROXY_PASSWORD, strPassword, strlen(strPassword)+1); goto resend; break; case HTTP_STATUS_DENIED: //Server Authentication Required // Insert code to set strUsername and strPassword. InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME, strUsername, strlen(strUsername)+1); InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD, strPassword, strlen(strPassword)+1); goto resend; break; } // Insert code to read the information from the hResourceHandle // at this point.