Some forms return a data source error when Forms Based Authentication is enabled

Vrijedi za: InfoPath 2010Microsoft Office InfoPath 2007

Symptoms


Consider the following scenario. You have a SharePoint site that uses Forms Based Authentication. You build a browser-enabled InfoPath template that calls the GetUserProfileByName method of the SharePoint UserProfileService web service. You try to open the form in the browser. When the form makes a call to that data connection in this scenario, you receive one of the following error messages:
An error occurred querying a data source

An error occurred while trying to connect to a web service
An entry has been added to the Windows event log of the service LogID 5566

An error occurred accessing a data source

An error occurred while trying to connect to a web service
An entry has been added to the Windows event log of the service LogID 5566

Cause


For a site to use Forms Based Authentication, anonymous access must be enabled at the web application level in Internet Information Services because unauthenticated users must be able to reach the logon page to sign in. However, if you allow anonymous access to the web application, this prevents the Forms Services from successfully querying the user profile web service.

For data security, the UserProfileService web service requires the caller to be authenticated before the service will return any data. This occurs because the UserProfileService web service returns personally identifiable information that is considered sensitive in most organizations. Anonymous access to this data is usually classified as a security breach. Because the UserProfileService web service has no user interface to request a user name and a password, the web service rejects an anonymous request.

In the scenario that is described in "Symptoms," this problem occurs when Forms Services tries to call the GetUserProfileByName method of the SharePoint UserProfileService. Forms Services makes the call anonymously, which is the convention for all web calls. If anonymous access is not allowed in the web application, the SharePoint server returns an authentication error (code 401). That response indicates which authentication methods the server supports. Forms Services then resends the request, including the requested authentication information.

When anonymous access is enabled for the web application, the anonymous request is accepted by the web application and is passed to the UserProfileService web service. The web service rejects the request because the request is not associated with a valid user. Forms Services then returns a server error (code 500), which indicates an internal failure. Forms Services cannot know that the failure occurred because of an authentication problem. Therefore, it cannot return a more specific error.

Resolution


There is no configuration change that will resolve this problem while enabling you to maintain a Forms Based Authentication environment. However, you can write managed code that retrieves the user name or other user information, and you can use that managed code in your form template.

The easiest case is to retrieve the current user's logon name. You can retrieve this when the form loads by adding a call to the Application.User.LoginName property to the FormEvents_Loading event. Here is an example:

        public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
XPathNavigator codeUserNameXPN = this.CreateNavigator().SelectSingleNode(
"/my:myFields/my:CodeRetrievedUserName", this.NamespaceManager);
codeUserNameXPN.SetValue(this.Application.User.LoginName);
}
If you need more profile information, you must use managed code to pre-authenticate, and then call the user profile web service directly. This requires you to hard-code or prompt for the credentials of a user who has rights to view the requested user profile. In most cases, this user will be a site administrator. If you decide to hard-code the credentials, you should consider security and maintenance issues. The code will be run on the server, which can help you limit security exposure. But when the user name or the password changes, you will have to have update the code. Prompting for credentials does not present these issues but will only work if the user of the form has the rights to view the requested user profile.

Here is sample code that obtains the properties of the current user:

        public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
//A place to write the results
XPathNavigator codeUserNameXPN = this.CreateNavigator().SelectSingleNode(
"/my:myFields/my:CodeRetrievedUserName", this.NamespaceManager);
//codeUserNameXPN.SetValue(this.Application.User.LoginName);

try
{
//TailSpinToysAuthentication is the web service reference to the
//https://tailspintoys:23456/_vti_bin/authentication.asmx web service
GetUserName.TailSpinToysAuthentication.Authentication authenticationWS =
new GetUserName.TailSpinToysAuthentication.Authentication();

//Call the web service's Login method and pass the username and password of a site
//administrator so we have rights to read all user profiles
authenticationWS.Url = "https://tailspintoys:23456/_vti_bin/authentication.asmx";
authenticationWS.CookieContainer = new System.Net.CookieContainer();
GetUserName.TailSpinToysAuthentication.LoginResult result = authenticationWS.Login(
"Admin", "Password");

if (result.ErrorCode == GetUserName.TailSpinToysAuthentication.LoginErrorCode.NoError)
{
//If we authenticated correctly, then set up a call to the user profile service
//TailSpinToysUserProfileService is the web service reference to the
//https://tailspintoys:23456/_vti_bin/userprofileservice.asmx web service
GetUserName.TailSpinToysUserProfileService.UserProfileService userProfileWS =
new GetUserName.TailSpinToysUserProfileService.UserProfileService();

//Pass the authentication cookies we got back from the authentication web service
userProfileWS.Url = "https://tailspintoys:23456/_vti_bin/userprofileservice.asmx";
userProfileWS.CookieContainer = authenticationWS.CookieContainer;

//Try to find the user profile information of the current
GetUserName.TailSpinToysUserProfileService.PropertyData[] resultData =
userProfileWS.GetUserProfileByName(this.Application.User.LoginName);

//Enumerate through the properties
foreach (GetUserName.TailSpinToysUserProfileService.PropertyData property in resultData)
{
//Pick out the "AccountName" property and display it
if (property.Name == "AccountName")
codeUserNameXPN.SetValue(property.Values[0].Value.ToString());
}
}
else
{
//If we failed to authenticate properly, display the reason why
codeUserNameXPN.SetValue(result.ErrorCode.ToString());
}
}
catch (System.Exception ex)
{
//If an exception occurred, report it.
codeUserNameXPN.SetValue(ex.Message);
}
}

Note To use managed code in a form template, you must make the form template fully-trusted in the Security section of Form Options, and then publish the form template to SharePoint as an Administrator-Approved form template.