Problem
We setup a new SQL Server Database in a new box that will serve us the production database server. It will also host the Sql Server Reporting Service (SSRS). After all are setup, SSRS returns "Unauthorized User (401)" when we access the reports from the application installed client workstations that consumes the reports.
Solution
This happens when the Reporting Service requires credentials in order for you to access the service. This does not normally happen in your development machine unless you are not member of the administrators group. As default BUILTIN\Administrators group is given administrative access to SSRS thats the reason why it does not prompts you for username and password when you access using Report Manager (http://localhost/Reports, the url ma change if you install SSRS in diffrent instance.) or when you call the report from the client application installed in the same machine.
For enterprise or client-server setup we need to supply credentials in order for the client application run reports hosted in separate server.
Passing Reporting Service Credentials to Fix Unauthorized User (401) Error
- Create a windows user account that we will use in Reporting Service.
- Open Computer Management.
- Add user account that we will use for the reporting service. Be sure to uncheck User must change password on next login checkbox. In our case create reportuser account.
- Open SQL Server Management Studio. Expand Security -> Logins.
- Right click the Logins folder and select New Login from the context menu.
- On the General section, enter the username of the user you created in step #1 in Login name field including the domain in this format <DOMAIN>\reportuser. Domain can be the machine name or Active Directory Domain of the machine where the SSRS is installed.
Image may be NSFW.
Clik here to view. - Select User Mapping from the left panel, check the database the report user will have access. After checking the database, select db_datareader from the database role. We only need read permission for the user who will access the reports. Always give report users the least privilege unless required by your system for security reason.
Image may be NSFW.
Clik here to view. - Click Ok button.
- If your report is using stored procedures be sure to grant user execute permission else skip this step.
GRANT EXECUTE TO [domain\reportuser]
- Now we have the windows account with permission to the database. Now we need to create Reporting Server Credentials object. Create a class named ReportCredetials and inherit IReportServerCredentials interface.
- Then create a constructor that pass the username, password, and domain as parameters.
public sealed class ReportCredentials : IReportServerCredentials { private string _username; private string _password; private string _domain; public ReportCredentials(string username, string password, string domain) { _username = username; _password = password; _domain = domain; } ... ... }
- Implement the NetworkCredentials property.
public System.Net.ICredentials NetworkCredentials { get { return new System.Net.NetworkCredential(_username, _password, _domain); } }
- Just let the ImpersonationUser property and GetFormsCredentials method return null.
public bool GetFormsCredentials(out System.Net.Cookie authCookie, out string userName, out string password, out string authority) { authCookie = null; userName = null; password = null; authority = null; return false; } public System.Security.Principal.WindowsIdentity ImpersonationUser { get { return null; } }
- Now we have the ReportCredentials class, we need to modify our report viewer to initialize the credentials before loading the report. See complete code of ReportCredentials class in ReportCredential Class section below.
IReportServerCredentials credential = new ReportCredentials( "myusername", //-- username "abc123", //-- password "MYMACHINENAME"); //-- domain. It can be machinename or active directory domain name reportViewerPreview.ServerReport.ReportServerCredentials.NetworkCredentials = credential.NetworkCredentials; //-- the server may change if your SSRS is not installed in default instance. reportViewerPreview.ServerReport.ReportServerUrl = new Uri("http://localhost/ReportServer/"); //-- this is the path where your reports are stored in Report Manager reportViewerPreview.ServerReport.ReportPath = "/MyReports/employeelist.rdl" //-- title of the report that will be shown in the header of your form this.Text = "List of Employees"; reportViewerPreview.ShowParameterPrompts = false; //-- loads and refreshes the report. reportViewerPreview.RefreshReport();
- Try again loading the report that retuns Unauthorized User (401) it should now be able to display the reports in the report viewer.
Note: Be sure to store the account in secured storage or you can add encryption to increase security. And always give the least privilege role and/or permissions.
ReportCredentials Class
Here is the complete code fo our ReportCredentials class
using System; using System.Collections.Generic; using System.Text; using Microsoft.Reporting.WinForms; namespace Accounting.Reports.UI { public sealed class ReportCredentials : IReportServerCredentials { private string _username; private string _password; private string _domain; public ReportCredentials(string username, string password, string domain) { _username = username; _password = password; _domain = domain; } #region IReportServerCredentials Members public bool GetFormsCredentials(out System.Net.Cookie authCookie, out string userName, out string password, out string authority) { authCookie = null; userName = null; password = null; authority = null; return false; } public System.Security.Principal.WindowsIdentity ImpersonationUser { get { return null; } } public System.Net.ICredentials NetworkCredentials { get { return new System.Net.NetworkCredential(_username, _password, _domain); } } #endregion } }