Background
I have an ASP.net MVC 4 web application with Forms authentication and a custom AuthorizeAttribute controlling access to all controllers minus the login screen. I am adding to some of the controllers, an action that allows the user to download a server generated PDF whose content and layout is being defined in a Razor View.
To carry out the conversion between Html and PDF, im using a trial version of ABCPdf9 and the bulk of the conversion works perfectly with all CSS, Text, static images, etc being displayed as required.
The problem is that I have images in the HTML that must also be rendered into the PDF file, but these images come from a controller which requires authentication.
Since the Html to PDF conversion takes place in ABCPdf using the Gecko engine, the existing user authentication cookies, etc are not available and as such the GET requests to the Image Controller are not authenticated and will not return anything. (This is the problem).
What I've discovered
From the research I have done, I came across the HttpAdditionalHeaders Property of ABCPdf which (from my understanding) is there to allow you to set header cookies, etc that will be sent with the requests made by the Gecko engine when fetching resources.
I have spent a good few hours trying to set the existing cookies from the originating request and pass them through to ABCPdf but this does not appear to work. Nor does creating a new set of authenticated cookies and pass them either.
So from what I gather, this solution is not possible....
My question
Does anyone know if it is possible to modify the Url.Action helper so it will generate and include a one-time authentication key in the url? Then implement some code in my custom AuthorizeAttribute that strip the key parameter and if valid, provide an alternative method of authentication to access the necessary image controller. This way, I can run the ABCPdf conversion process and it will be able to access the normally restricted resources in the image controller.
For example:
<img src="http://somesite.com/Image/Retrieve/1234?key=WFD6312DFV154WHSF3B1SGB69SB" />
The custom AuthorizeAttribute code should then recognise the key parameter passed in the requests query string and then bypass existing authentication processes.
Any help or even suggestions for where to look would be greatly appreciated!
Related
My rails API application allows a user to make POST API requests to create a credential through a namespaced endpoint (i.e. ~/api/v1/credentials). I'm working now to allow the creation of a credential through the HTML view (~/credential/new), but for business reasons, we need a specific parameter for credentials created through the HTML view that isn't necessarily required through the API.
For example, let's say the parameter in question is cell_number. An API user may not need to provide a cell number, but every user who creates a credential with the HTML view must provide one.
Since both the API and the HTML version rely on the same model, how would one go about doing validations for different criteria through the API vs the HTML version? Or am I over complicating this and should look at another option entirely?
I have an ASP.net MVC application where I can't get the MVC Views to render as a PDF after the introduction of the ADFS authentication.
Earlier, the application had Forms authentication and PDF rendering using Rotativa (which uses WkhtmltoPDF library) worked like a charm. After the introduction of the ADFS it just won't work.
What I get is a blank PDF with the title: "Sign In" without any elements on the page/PDF itself.
As it's pretty indicative that it tries to connect somewhere I believe the problem lies somewhere in the authentication parameters of the WkhtmltoPDF i.e. I probably have to add some additional parameters in order to support ADFS, but I'm not sure which ones. I've already tried using --username and --password parameters but they didn't help.
The question is: What to do to make it work (again)?
Try using the [AllowAnonymous] attribute on the action that calls the view you turn into PDF
Example:
For the action that calls rotativa you'd have something like this maybe
return new Rotativa.ViewAsPdf("ViewToPdf", model);
and the action called would look like
[AllowAnonymous]
public ActionResult ViewToPdf(ViewModel model)
{
return View(model);
}
I hope you understand my formatting
I wasn't able to fix the problem the way I wanted it to, but for those interested in the solution the next steps describe what I did.
As the page I was trying to render with Rotativa required authentication I added the logic to create an "one-time access token" which redirects the user to the another application that renders the same page but without any ADFS authentication check. In this case you have to modify Rotativa's logic to return UrlAsPdf instead of ActionAsPdf.
The only authorization thing I did was checking the validity of the token. Since the token has a fast expiration time (it is valid only for the next couple of minutes) I don't think the security of the web app was negatively affected since no one can access the version without authentication without the access token.
I'm using angular 1.0.4 with an ASP.NET MVC4 + Web API project. I'm trying to leverage angular's CSRF protection to no avail. I can see that I'm passing along a cookie named XSRF-TOKEN, but when angular tries to add the value as a header named X-XSRF-TOKEN in the response, the value appears as undefined. I tried following the advice here, but the HTML has yet to render, so no element is found.
What might I be missing? Is the RequestVerificationToken cookie generated by ASP.NET MVC protected from javascript access?
Also, is it possible to have angular lazily retrieve either the cookie or form input value? If so, how? I cannot find any docs on how to do this.
I could not find an exact answer to my question. I ended up creating a service to find the 'input[name="__RequestVerificationToken"]', get its value, and return an object with that value. I then set the headers in the config using that object. This lets me lazily extract and append the value as a header for a form that doesn't appear immediately on the page.
I also realized, upon further review, that ASP.NET's AntiForgeryToken support requires both the cookie and form input/header value to validate, so the built-in AngularJS support wouldn't suffice anyway.
If anyone has a better solution, I will happily transfer the answer to that solution.
A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.
But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?
In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.
For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?
I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.
If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.
Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.
If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.
Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.
Create an MVC action filter and add it as a global filter during Application_Start.
Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.
The global MVC filter does this:
Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.
If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.
The WebAPI filter does this:
Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.
If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.
Otherwise, throw a 403 Forbidden exception.
A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.
I currently have an intranet site that is accessed by external customers. I therefore set this up using Forms Authentication. However the powers that be (my bosses) want all our domain users to not have to enter their username and password to access the site.
I've done a bit or reading and everything seems to point to setting up a WinLogin.aspx page that you alter to use WindowAuthenthication and then redirect from there.
I have a problem with this as I don't like the idea of putting an aspx form in my mvc application.
Can anyone tell me how to achieve mixed authentication using a strictly MVC Controller/Action setup without a second application?
NOTES: running MVC 3 on an IIS 7 box.
Forms Authentication is not related to the URL or physical structure of your files. What matters is that a URL should ultimately map to a physical (or virtual) resource on the server, and be processed, and be returned back to the user.
Thus, somewhere in between for each incoming call (each HTTP request, even those for CSS and JavaScript files), you have to see if the current user has enough permission to access it or not. If no, then you might redirect him to the login page.
If you want, you can have a URL like /user/windowslogin where user is the name of the controller, and windowslogin is the name of your action method. Then you can create a custom authentication attribute (something like [WindowsAuthentication]) on your windowslogin action, and in that attribute (which is an MVC filter in essence), you can see if the current request comes from within your domain, and if so, talk to Active Directory for authentication or stuff like that, and on case of successful authentication, create an authentication cookie using FormsAuthentication class, and the rest of the story.
However, I don't think this would be an easy task. Others might introduce better solutions.