1

I'm trying to print out a pdf invoice from a page that requires authentication (https://localhost:44362/invoice). All of this works neatly when authorization is off, but when I turn it on, the whole thing falls apart. I'm using .NET Core 2.1 MVC with Identity authentication. I've tried two different solutions, which both fail different ways.

Attempt 1:
The rendering works, but the login fails, and the resulting pdf is the login page. This, I understand, is normal behaviour when authentication fails.

var uri = new Uri("https://localhost:44362/invoice");
var newPdf = new HtmlToPdf()
{
    PrintOptions = new PdfPrintOptions()
    {
        //..omitted for brevity
    },
    LoginCredentials = new HttpLoginCredentials()
    {
        EnableCookies = true,
        LoginFormPostVariables = new Dictionary<string, string>()
        {
            { "Email", "username@domain.com" },
            { "Password", "secretysecret" }
        },
        LoginFormUrl = new Uri("https://localhost:44362/login")
    }
};
var result = newPdf.RenderUrlAsPdf(uri);

Attempt 2:
I get the authorization cookie from the original request. This works, at least so far that I can actually get the cookie and set it to the LoginCredentials.CustomCookies. I've compared it with the browser request, and the cookie seems to be correct. The collection Request.Cookies holds two cookies, one Antiforgery, and one for Identity.
The rendering fails, because newPdf.RenderUrlAsPdf(uri) returns null and I don't understand why. The reason must be with the CustomCookies set in the LoginCredentials failing somehow, but the stack trace doesn't give any information about what fails. Just to be clear, this method fails even when the authorization is not in use.

var uri = new Uri("https://localhost:44362/invoice");
var cookies = Request.Cookies;
Dictionary<string, string> customCookies = cookies.ToDictionary(c => c.Key, c => c.Value);
var newPdf = new HtmlToPdf()
{
    PrintOptions = new PdfPrintOptions()
    {
        //..omitted for brevity
    },
    LoginCredentials = new HttpLoginCredentials()
    {
        EnableCookies = true,
        CustomCookies = customCookies
        //LoginFormPostVariables = new Dictionary<string, string>()
        //{
        //    { "Email", "username@domain.com" },
        //    { "Password", "secretysecret" }
        //},
        //LoginFormUrl = new Uri("https://localhost:44362/login")
    }
};
var result = newPdf.RenderUrlAsPdf(uri);
Sami
  • 2,050
  • 1
  • 13
  • 25

1 Answers1

0

Because this is the only real search result that comes up when searching for IronPDF authentication/cookie issues, I wanted to add a little info that has taken me WAAAY longer to figure out than I'd care to admit...

  1. This approach of leveraging the custom cookies functionality and using the render url method DOES in fact work. It will use the copied auth cookie and successfully authenticate to get the markup and any linked resources. Note that you'll want to use the newer static methods however as the old instance method shown here is legacy/deprecated.

  2. I was beating my head against the wall assuming the render html method IronPdf.ChromePdfRenderer.StaticRenderHtmlAsPdfAsync worked just like the render url method IronPdf.ChromePdfRenderer.StaticRenderUrlAsPdfAsync. Sadly, the render html method simply does NOT work this way. You can set custom cookies all you want, but any requests for linked resources won't be using them.

This is particularly key in an Azure Web App environment where you are leveraging an Identity provider and require authentication for the entire site. This method of copying the AppServiceAuthSession cookie and using it on IronPDF's Url method call is treated as already authenticated.

It's unfortunate that the IronPDF doc tends to steer you toward the html method as being more reliable when needing to render auth-protected content. In their defense, the custom cookie doc does explicitly state it's intended for use with the url methods - which makes sense when you think about it - I just kept skimming over that little nugget, and apparently support wasn't aware either.

I'd still prefer to render my view to an html string myself, and use the html method so I'm going to see what support can tell me. At least now I have an option besides opening up the linked resources to anonymous access.

Edit: Another little tidbit to watch out for is regarding the use of the HtmlHeader/HtmlFooter properties. I (stupidly) assumed any markup I added there could easily be styled via a stylesheet and not have to be hardcoded. It probably can if you don't have to worry about authentication, but otherwise good luck. The header and footer are literally treated as separate html documents, so if you want the styles applied to them that are applied to your main doc content, there's a property for that - LoadStylesAndCSSFromMainHtmlDocument. Sadly, this property clearly states it only works for the html methods, not the url methods. Because I had to use the url methods because of the previously mentioned auth/cookie issues, I was again out of luck. Then I found the CustomCssUrl property that says it's used to apply the specified stylesheet to the html prior to rendering. After trying it though, I quickly realized that the calls to this stylesheet apparently don't use the custom cookies, so now we're back to not being able to pull the stylesheet because of authentication. Ultimately I just hardcoded the styles in the markup I was passing to the HtmlHeader/HtmlFooter because I was starting to question my life choices at this point - LOL.

kman
  • 2,184
  • 3
  • 23
  • 42
  • Thanks! I'll look into this. The project regarding the original post has been rafically changed since, but I'll be needing this in another one soon, so it's much appreciated. – Sami Dec 29 '22 at 08:16