The idea is to minimize the number of 'LogonUser' requests sent to the report server. This can be achieved by authenticating once using the soap API ‘LogonUser’ call and saving the authentication cookie for further requests.

To elaborate some more:

Windows Azure SQL Reporting is using its own security extension and allows only form authentication, this means that when accessing the server through a browser (URL access), a logon page will appear that will ask you to specify your username and password.

When configuring the Report Viewer control (aka RVC) to work against a report server that requires form authentication one must implementthe IReportServerCredentials.GetFormCredentials method.

Usually the implementation looks something similar to this: 

public bool GetFormsCredentials(out Cookie authCookie, out string userName, 
out string password, out string authority)
{
userName = ConfigurationManager.AppSettings["ReportServerUsername"];
password = ConfigurationManager.AppSettings["ReportServerPassword"];
authority = null;
authCookie = null;
return true;
}

What happens in the above implementation is that every request to the report server that will be initiated by the RVC will be preceded with a ‘LogonUser’ request that will use the given implementation to specify the correct credentials. While this solution might be reasonable for the box product, where the site hosting the RVC might be fairly close to the report server, it may not be the best solution for the cloud scenario, where any request might take longer.

Here are some code samples one may use to reduce the LogonUser calls:

ReportServerConnection – This class configures the RVC against the remote report server, more information about setting connection details can be found here.

[Serializable]
public class ReportServerConnection : IReportServerConnection
{
public Uri ReportServerUrl
{
get { return new Uri(ConfigurationManager.AppSettings["ReportServerUrl"]); }
}

public int Timeout
{
get { return int.Parse(ConfigurationManager.AppSettings["ReportServerTimeout"]); }
}

public bool GetFormsCredentials(out System.Net.Cookie authCookie,
out string userName, out string password, out string authority)
{
authCookie = AuthenticationHelper.ReportServerAuthCookie;
userName = password = authority = null;

return true;
}

public System.Security.Principal.WindowsIdentity ImpersonationUser
{
get { return null; }
}

public System.Net.ICredentials NetworkCredentials
{
get { return null; }
}
}

  

AuthenticateHelper – This class is used for authenticating directly through the ReportServer soap APIs, and save the authentication cookie for further requests.

public class AuthenticationHelper
{
private static Cookie m_authCookie;

public static Cookie ReportServerAuthCookie
{
get
{
if (m_authCookie == null)
{
Authenticate();
}

return m_authCookie;
}
set
{
m_authCookie = value;
}
}

private static void Authenticate()
{
using (var client = new MyReportingService()
{
Url = Path.Combine(ConfigurationManager.AppSettings["ReportServerUrl"],
"ReportService2010.asmx")
})
{
client.LogonUser(ConfigurationManager.AppSettings["ReportServerUsername"],
ConfigurationManager.AppSettings["ReportServerPassword"],
null);

m_authCookie = client.AuthCookie;
}
}
}

 

MyReportingService – Extends the ReportService2010 class in order to intercept the authentication cookie. The ReportService2010 class is auto generated when web referencing the ReportService2010.asmx endpoint, the URL looks something like this https://xxxxxxxxxx.reportservice.mscds.com/reportserver/reportservice2010.asmx

More information on how to reference a web service can be found here.

public class MyReportingService : ReportingService2010
{
private Cookie m_authCookie;

public Cookie AuthCookie
{
get
{
return m_authCookie;
}
}

protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.CookieContainer = new CookieContainer();
if (m_authCookie != null)
{
request.CookieContainer.Add(m_authCookie);
}

return request;
}

protected override WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
string cookieName = response.Headers["RSAuthenticationHeader"];
if (cookieName != null)
{
HttpWebResponse webResponse = (HttpWebResponse)response;
m_authCookie = webResponse.Cookies[cookieName];
}

return response;
}
}
 

 

Please note that the above code doesn’t handle cookie expiration, in Windows Azure SQL Reporting the cookie is configured to expire after 60 minutes.

Handling the cookie expiration can be done in several ways:

  1. Create a timer that will fire every 60 minutes or less and will set the ReportServiceAuthCookie property to null, this way next time a user will enter the page the Authenticate() method will be called.
  2. If your website also handles session state you could save a single ReportServer cookie for each session and make the session expiration shorter than 60 minutes.