Eric Logsdon
Eric Logsdon

Reputation: 95

401 not Authorized with SSRS ReportViewer in Azure

We currently have a self-hosted ASP.NET Web Forms application that uses an SSRS reporting back end. The reports are accessed using the ReportViewer control 10.0.0.0. We have a business need to move this workload to Azure.

We migrated our databases to SQL Azure and set up an Azure Web Site (Web App) and the portion of the app used to maintain the data in the databases works fine. We set up a VM running SQL2016, configured SSRS and uploaded our report RDL and DataSources. The reports run fine through the SSRS web portal.

When we try and access the reports through the Web Application we are getting a 401: Unauthorized. We are using a local Windows login on the SSRS server for authentication (we tried a SQL server login, with the same results). The Windows login has been set up in the SSRS portal with access to the reports and has access to the database through SQL server. The Web application is not using impersonation (I explicitly turned it off to be sure).

I wrote a quick Windows Form application using the Windows version of the Report viewer control and this works fine. The setup code for the viewer control is essentially the same. I have tried the web version with and without specifying the "machine name" in the credentials for a local account.

I have included the setup code from the Web App below. At this point, I don't think it is a code problem as much as a Web setup issue. Any help or pointers will be greatly appreciated.

Eric.

Dim myCreds As System.Net.CredentialCache = New System.Net.CredentialCache
Dim storedParams As Microsoft.Reporting.WebForms.ReportParameterCollection = CType(Session("Current_ReportParams"), Microsoft.Reporting.WebForms.ReportParameterCollection)
With ReportViewer1
.Reset()
.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Remote
.ServerReport.ReportServerUrl = New Uri(ReportServer) 'Report Server URL
.ServerReport.ReportPath = String.Format("{0}/{1}", ReportPath, ReportFileName) 'Report Name
.ServerReport.SetParameters(storedParams)
myCreds.Add(New Uri(ReportServer), "Basic", New System.Net.NetworkCredential("user", "password", "machine"))
.ServerReport.ReportServerCredentials = myCreds
.ShowParameterPrompts = False
.SizeToReportContent = True
.ServerReport.Refresh()
End With

Upvotes: 0

Views: 1140

Answers (1)

Eric Logsdon
Eric Logsdon

Reputation: 95

I will post this as an answer instead of a comment. In working with Microsoft Support, I modified my code somewhat. I added a class that implemented IReportServerCredentials that returned a NetworkCredential with the user name and password. We still persisted in getting the 401 error. We started running network traces and noticed that the credentials were not showing up in the trace as we expected. We did see, however, NTLM negation taking place and failing.

We verified the rsReportServer.config had basic authentication enabled and disabled NTLM and Kerberos authentication just to be safe. Still failed with a 401.

After several back and forths with MS and many Bingle searches, we discovered that the .ServerReport.SetParameters method makes a call to the SSRS server. We were setting our .ServerReport.ReportServerCredentials after this. Simply moving the .ServerReport.ReportServerCredentials before the .ServerReport.SetParameters method solved the issue. The updated code is below.)

So, this was a programming error and not a configuration error. This will affect any SSRS instance that is not in the same domain as the website calling the ReportViewer control.

Imports Microsoft.Reporting.WebForms
Imports System.Net
Imports System.Security.Principal
Public Class ucReportPreview
    Inherits System.Web.UI.UserControl


    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load



        If Not IsPostBack Then
            'gets the report viewer control from the session if it exists
            If Not Session("Current_ReportFileName") Is Nothing Then
                Dim sysOpt As New SystemOption
                'Dim ReportServer As String = sysOpt.LookupValueByShortDescription("ReportServer")
                'Dim ReportPath As String = sysOpt.LookupValueByShortDescription("ReportPath")
                'Dim ReportFileName As String = Session("Current_ReportFileName").ToString
                Dim ReportServer As String = "http://logsdon-ssrs/ReportServer"
                'Dim ReportPath As String = "/InfoRail V5/IR-20141118"
                Dim ReportPath As String = "/userinfo"
                Dim ReportFileName As String = "Certification Requirements Due (By Employee)"
                Dim myCreds As IReportServerCredentials = New MyReportServerCredentials("<userName>", "<password>", "<machine-or-domainName>")

                If Not Session("Current_ReportParams") Is Nothing Then

                    Dim storedParams As Microsoft.Reporting.WebForms.ReportParameterCollection = CType(Session("Current_ReportParams"), Microsoft.Reporting.WebForms.ReportParameterCollection)
                    With ReportViewer1
                        .Reset()
                        .ServerReport.ReportServerCredentials = myCreds
                        .ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Remote
                        .ServerReport.ReportServerUrl = New Uri(ReportServer) 'Report Server URL
                        .ServerReport.ReportPath = String.Format("{0}/{1}", ReportPath, ReportFileName) 'Report Name
                        .ServerReport.SetParameters(storedParams)
                        .ShowParameterPrompts = False
                        .SizeToReportContent = True
                        .ServerReport.Refresh()
                    End With

                Else

                    Dim storedParams As Microsoft.Reporting.WebForms.ReportParameterCollection = CType(Session("Current_ReportParams"), Microsoft.Reporting.WebForms.ReportParameterCollection)
                    With ReportViewer1
                        .Reset()
                        .ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Remote
                        .ServerReport.ReportServerUrl = New Uri(ReportServer) 'Report Server URL
                        .ServerReport.ReportPath = String.Format("{0}/{1}", ReportPath, ReportFileName) 'Report Name
                        .ServerReport.ReportServerCredentials = myCreds
                        .ShowParameterPrompts = True
                        .SizeToReportContent = True
                        .ServerReport.Refresh()
                    End With

                End If
            End If




        End If



    End Sub

    Private Sub Page_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed

        'destroys the report viewer control in the session
        Session("Current_ReportParams") = Nothing

    End Sub

    Protected Function GetReportName(Reportid As Integer) As String
        Dim retval As String = String.Empty
        'Dim rt As New ReportTableBLL(System.Web.HttpContext.Current.Session("ConnectionString"))
        'retval = rt.GetReportfilenameByReportid(Reportid)
        Return retval
    End Function

End Class

Public NotInheritable Class MyReportServerCredentials
    Implements IReportServerCredentials
    Private _userName As String
    Private _password As String
    Private _domainName As String

    Public ReadOnly Property ImpersonationUser() As WindowsIdentity Implements IReportServerCredentials.ImpersonationUser
        Get
            Return Nothing

        End Get
    End Property
    Public ReadOnly Property NetworkCredentials() As ICredentials Implements IReportServerCredentials.NetworkCredentials
        Get
            Return New NetworkCredential(_userName, _password, _domainName)
        End Get
    End Property
    Public Function GetFormsCredentials(ByRef authCookie As Cookie, ByRef userName As String, ByRef password As String, ByRef authority As String) As Boolean Implements IReportServerCredentials.GetFormsCredentials

        authCookie = Nothing
        userName = Nothing
        password = Nothing
        authority = Nothing

        Return False

    End Function

    Public Sub New(userName As String, password As String, domainName As String)
        _userName = userName
        _password = password
        _domainName = domainName
    End Sub


End Class

Upvotes: 1

Related Questions