Download File in Blazor Server Side with Authentication - asp.net-core

I have a Blazor Server Side Web Application that uses the default authorization and authentication.
app.UseAuthentication()
app.UseAuthorization()
I can protect my pages with
#attribute [Authorize]
I have a login page with anonymous access to authenticate. This works fine.
Now I need a way to let the user download files from this authorized pages. Surprisingly I haven't found any straightforward way to do this.
One workaround is to build an API Controller with the filename as a path argument and give the user a link to it.
[Route("api/[controller]")]
public class FileController{
[HttpGet("download/{filename}")]
public async Task<IActionResult> Download([FromRoute] string filename){
//Do some checks and get file from Filesystem
return file;
}
}
And in the .razor file
private string CalculateDownloadLink(string filename){
return $"{NavigationManager.BaseUri}/api/file/download/{filename}"
}
This is a dumbed down version. In reality the filenames are generic. This works too.
Now I want to add Authentication to the API Controller because I don't want anyone guessing filenames. But I don't know how.
Of Course the [Authorize] Attribute doesn't work because the code is outside the circuit scope.
I can't figure out how to use any build-in Authorization to make this work.
Is there a better way to download files from a Blazor app?

A bit late answering your question, but [Authorize] on the controller should work. Have you tried it? Controller methods get the same cookies that Blazor pages get.
But there is a better way to download a file from Blazor without a need for navigation. See this blog post.

Your controller is not a controller. It does not implement Controller...
[Authorize]
public class FileController : Controller
{
...
}
FYI: If you add the download attribute to the anchor the file will only download when clicked.
<a download href="#CalculateDownloadLink("file.txt")">Download file.txt</a>
I made a junk repo that works if you need me to post it.

Related

Asp.Net Core Web Api hide controllers and actions by api-key authorization

I am converting an Asp.Net Web Api project to Asp.Net Core with .net6.
This project uses a custom Api-Key authentication and authorization method to give access to some of the controllers and actions.
In the old project, when opening the swagger documentation, the only controllers and actions displayed are the ones that do not require authentication.
Once the user inserts the api key and clicks on Explore button, the authorized controllers and actions shows up in documentation.
To do this, in the legacy project, I used IOperationFilter and IDocumentFilter to hide controllers and actions if not authorized.
This is working because when you click on Explore button after providing the api key, the page is refreshed and, at every refresh of the page, the IOperationFilter and IDocumentFilter are applied.
In Asp.Net Core, it seems that the IOperationFilter and IDocumentFilter are applied only at startup, and, in addition, the new Authorize button does not reload the page, and works only client side to provide authorization to the subsequent user interactions with the Try it out button present on every action.
Is it possibile to do something similar in Asp.Net Core, or should I drop that feature in the new version?
I found something may help you :
In asp.net core Swagger construct the UI page with a json file as below :
app.UseSwaggerUI(c=>
{                    
c.SwaggerEndpoint("/swagger/v1/swagger.json","SwaggerFilterv1"); 
});
This json file is constructed with the options you configured when you registed the service
Then I tried to copy the content of json file to the static file in the project ,deleted part of the path(you could also apply the filters and copy the json file directly ),and replaced the json file constructed by .net core
It worked as excepted : Some of the Api was missing
and you may inject your js file to control it(Add a request header you if auth succeeded and refresh the page and check request header with middleware):
app.UseSwaggerUI(c=>
{                    
......
c.InjectJavascript("")
});
I tried to branch the pipeline(I omitted the Authentication process just tried to check the query para):
app.UseWhen(x =>x.Request.Query.ContainsKey("Key"),
app =>app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/somejson.json", "SwaggerFilter v1");
}));
app.UseWhen(......)
The Result:

Blazor (server side) authentication and static files

As per Microsoft's recommendation, I am using a custom AuthenticationStateProvider service for handling authentication/authorization for a Blazor server page.
It all works fine within razor components, where I can use the [Authorize] attribute or the AuthorizeView/Authorized/NotAuthorized tags.
Now, I wanted to serve static files outside the wwwroot folder but have control if the user is authenticated or not in order to serve the files.
Is there a way to control access to static files served outside the wwwroot folder?
What I found is something similar to (in program or startup):
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = (context) =>
{
if (context.Context.Request.Path.StartsWithSegments("/MyRequestPath"))
{
context.Context.Response.Headers.Add("Cache-Control", "no-store");
if (!context.Context.User.Identity.IsAuthenticated)
{
context.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Context.Response.ContentLength = 0;
context.Context.Response.Body = Stream.Null;
}
}
},
FileProvider = new PhysicalFileProvider("PathToMyFilesOutsidewwwroot"),
RequestPath = "/RequestPath"
});
The problem with that is that is uses Context.User.Identity.IsAuthenticated, i.e., it uses HTTPContext, which is not available within a Blazor page (and that is why we have to use AuthenticationStateProvider).
I'd like to stick to just using Blazor best practices, and not try to circumvent it via scaffoldding, javascript, or whatever.
Thanks in advance.
In the end I sticked to using the app.UseStaticFiles... approach, but in order to make it work I had to add authentication via cookies (outside of Blazor). Inside of Blazor I still use AuthenticationStateProvider, so the only thing that I had to take care is to authenticate via cookies and AuthenticationStateProvider at the same time when a user logs in. I suppose it makes sense, because authentication via cookies (prior to entering the Blazor "environment") gives me also the chance to call controllers or other pages out the razor components while still being authenticated (via cookies).

Role based Access to Static Content in ASP.NET CORE

Is there any way I can give access to static content based on their role. In my application, the authentication is done through Azure Active AD and the contents should be accessed based on their role e.g. employee should access all employee pages and students should access student pages. This is how my solution explorer looks like.
Solution Explorer
I know this is duplicate of Secure requests to .html files in ASP.NET Core but I couldn't find any approach to implement the solution. I have made new folder intranet outside the wwwRoot to serve my static content but still need to know how can I authorize the user and and serve role based static files.
As the document said, you could store the static files outside of wwwroot and any directory accessible to the Static File Middleware (for example: MyStaticFiles folder, like this), then, you could serve them via an action method to which authorization is applied and return a FileResult object:
[Authorize(Roles = "User")]
public IActionResult BannerImage()
{
var filePath = Path.Combine(
_env.ContentRootPath, "MyStaticFiles", "images", "Image1.jpg");
return PhysicalFile(filePath, "image/jpeg");
}
Then, you could view image by click the following link:
<a asp-action="BannerImage" asp-controller="Home">View Image</a>
[Note] After using the above method, if the authorize not working, try to clear the cache, perhaps the issue is related to the browser cache. Besides, if you meet the "HTTP Error 404.15 - Not Found" error, try to add [AllowAnonymous] attribute for other controller action method.

FileServerMiddleware with credentials?

We have the requirement in our enterprise environment to serve static file content from a network share in our ASP.NET Core application. Basically, it gets served under some sub path /content. For this, we have the following code, which works fine:
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider("//our/network/share"),
RequestPath = new PathString("/content"),
EnableDirectoryBrowsing = false
});
Now in production the system user under whose context the web application is hosted has no access to the file share. Thus, we have to use a certain technical domain user to access the files and for this we have to provide credentials (username/password) of this system user to the file server.
Unfortunately, we did not find an option to provide credentials to UseFileServer(). Is it anyway possible?
According to the documentation for UseFileServer it combines the functionality of among other things UseStaticFiles. According to the middleware documentation, the static file module provides no auth checks. They do give you some options on how to accomplish file serving with authorization (again from the middleware docs):
If you want to serve files based on authorization:
Store them outside of wwwroot and any directory accessible to the static file middleware.
Deliver them through a controller action, returning a FileResult where authorization is applied.
Not sure how you are going to pass the username/password to the server. If you plan to use something like basic authentication (and don't want to use the methods outlined above), you can probably modify the headers (when serving the static files) to accomplish the desired effect, but that is a workaround and probably not a good idea.
I would use middleware to protect contents. I will try to write simple example(I assumed you are using any authentication middleware to authenticate your users and my example is for static files).
-- Below code is untested and is just for an illustration--
First, you need to create a middleware something like this:
public class ProtectFileMiddleware
{
private readonly RequestDelegate _next;
public ProtectFileMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/protected"))
{
if (!context.User.IsInRole("Admin"))
{
await context.Authentication.ChallengeAsync();
return;
}
}
await _next(context);
}
}
and use this middleware like below:
public void Configure(IApplicationBuilder app)
{
app.Use(?)Authentication();// it depends on your design
app.UseMiddleware<ProtectFileMiddleware>();
app.UseStaticFiles();
// other
}
Result: if you try to access /protected url as an admin user, you will get expected response otherwise you will take a 401/403 response.
For more flexible way take a look at http://odetocode.com/blogs/scott/archive/2015/10/06/authorization-policies-and-middleware-in-asp-net-5.aspx
Yeah, those answers assume you're asking about client credentials. What you really need is a IFileProvider implementation that has credentials to access a specific resource. I don't know that .NET is very good at accessing files as different users, it usually relies on impersonation. See How to present credentials in order to open file?

Symfony 2 - FOSUserBundle - how to integrate in API

I work on a new Symfony 2 project. I'm learning this framework at the same time. For the user management, I use the bundle FOSUserBundle.
My project works very well, I can login, register, logout and all other commands available.
The thing is that I want to make smartphone app which will use the API of my Symfony app. In the app, the user will have to sign in, or to sign up. Is it possible to use FOSUserBundle methods for API too?
I studied another bundle for making an API, it's FOSRestBundle.
If there are not solution, do you think that I will have to create my own users method like :
/api/login
/api/register
Then, inside this method, I redirect to FOSUserBundle methods? I'm just wondering what is the best, and the cleanest way to login, and register with FOSUserBundle from smartphone, so by using API
I have this problem too.
I found the best solution is this
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class YourController extends Controller{
//Other Methods..
public function loginAction(Request $request){
try{
$token = $this->get('security.authentication.manager')->authenticate(new UsernamePasswordToken('username', 'password', 'firewall'));
$this->get('security.context')->setToken($token);
}
catch(BadCredentialsException $e){
return new Response("Bad credentials", 403);
}
return new Response("success");
}
}
I used the FOSRestBundle.
This bundle is very powerful and simple to implement.
The documentation is pretty complete.
The github link of FOSRestBundle here
Hope that it helps
You need to check WSSE and how to integrate it to symfony.
Also check this post. And there is a bundle that implementing WSSE authentication. WSSE one of the best solutions for your app.