Rewrite URl according to Attribute - c#

We have an ASP.NET Core Web API running on .NET 5. It has got many controllers and routes that are sometimes protected via the Authorize attribute and sometimes they are public.
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase {
[HttpGet("me")]
public IActionResult GetMyPublicInformation()
{
// code...
}
[HttpGet("me")]
[Authorize]
public IActionResult GetMyPrivateInformation()
{
// code...
}
}
Well now I would like to publish these REST routes through different HTTP Routes, depending on the Authorization requirement. For example, the route GetPublicInformation does not require authorization. That route should be available under public/user/me. Whereas the method GetMyPrivateInformation does require authorization and should be published under the route secure/user/me.
Of coure, I am aware that I can define the route manually in the HttpGet attribute (i.e. [HttpGet("public/user/me")), but - besides that I'm lazy - it would be error prone because we have the information available twice: Once with in the manual route definition (HttpGet) and once in the Authorize attribute. So if someone forgets to change one of the two attributes, we get an inconsistency.
Question: What are my options to automate this URL rewriting (I'm thinking of middleware)? Are there any drawbacks you see using this approach? I have been fighting this idea because I don't like extra magic sauce in my codebase. I prefer explicity, that's why I'm going for the manual route naming right now...
By the way: I have to take this on me because of limitations in Microsoft's MSAL library. I believe I shouldn't have to do this because we host an OpenAPI definition on which routes are and which routes aren't authorized. That should be enough in most cases.

Related

Disable controllers in certain API version

We are developing a new API version in our application, so that we basically have a
namespace Bla.V1.Controllers
[ApiController]
[ApiVersion("1")]
public partial class SampleController : ControllerBase {}
and a
namespace Bla.V2.Controllers
[ApiController]
[ApiVersion("2")]
public partial class SampleController : ControllerBase {}
We've also configured API versioning (AddApiVersioning), so that when somebody tries to access e.g. api/v3/sample/action, he will get a version not supported error response, provided by the API versioning middleware.
Now the V2 version is in development and we want to make it available only for the development stage. The controllers code will be deployed to production, we want however to disable the V2 version for production.
We would do it with a feature flag, but what should be done to hide the V2 controllers?
I was thinking:
Somehow do not register the V2 controllers. Didn't find a suitable option in AddControllers()
Set maximum version to V1 in the API versioning middleware. This works only for headers, doesn't block access to V2 controllers
I'm currently thinking about writing an authorization attribute that would deny access based on the feature flag status and apply it to V2 controllers. Is there a better and more elegant way to do this?
Well, in case somebody is interested at some point. Our controller code is autogenerated from a Swagger definition. So I added the following code to the generation template, which gets invoked in each controller method:
if (!this.IsV2Enabled())
{
var protocol = this.Request.IsHttps ? "https" : "http";
return this.BadRequest(new
{
error = new {
code = "UnsupportedApiVersion",
message = $"The HTTP resource that matches the request URI '{protocol}://{this.Request.Host}{this.Request.Path}' does not support the API version '1'."
}
});
}

Azure Ad - Redirect Uri hidden behind authorization

I'm encountering an issue where I can observe an infinite redirect loop. My project is based on the official MS example - active-directory-b2c-dotnet-webapp-and-webapi
Does "Redirect URI" (defined in Azure Portal) have to be publicly accessible endpoint?
What would happen if in my controller I decorated it with an [Authorize] attribute?
So basically in this example Redirect Uri (set as website root, i.e. localhost:1234/) would also be a route for an action in the controller, which requires authorization.
[Authorize]
public class ControllerA : Controller
{
[Route("~/")]
public ActionResult Index()
{
}
}
Could it cause an infinite redirect loop?
Removing the route attribute fixes the problem, but at the same time, I feel like it's not a real cause of the issue.
I imagine that OWIN authorization is higher in the application stack compared to the controller's authorization, so I think that OWIN authorization middle-ware would parse the response from Azure Ad in a first place, instead of enforcing [Authorize] attribute policy and rejecting it upfront.
You can certainly create an infinite loop scenario this way but it will end up short-circuited by Azure AD. After a few loops, it will stop redirecting back and surface an error.
The redirect_uri needn't be a publicly accessible URI, it will work with http://localhost for example. It need only be accessible by the client. After all, a "redirect" is simply an HTTP response issued by the server. It is actually executed by the client.
In general, the controller you're using for authorization (i.e. receiving the redirect) shouldn't be decorated by an [Authorize] at the class level. Typically you would only decorate the handful of methods that require a signed in user. That said, you could certainly decorate the class with [Authorize] so long as you decorate your callback endpoint with [AllowAnonymous].
The core of the problem and solutions are described in following document - System.Web response cookie integration issues. I've implemented 3rd solution (reconfigure the CookieAuthenticationMiddleware to write directly to System.Web's cookie collection) and it has resolved the issue. What lead me to discover that the cookies were the issue is another StackOverflow's question, which describes a really similar symptoms to the one I was observing.
A fact that the default route [Route("~/")] was mapped to one of the controller's methods which required authorization, and that it was also matching a redirect_uri that I've set up in Azure Portal was not a culprit. In my opinion this is a proof that redirect_uri call will be handled directly by OWIN auth middleware and it won't even reach a controller's method.

How can I validate a URL before the request gets to the controller

Our service uses ASP.NET Core and in the application pipeline, we have several middlewares which are configured in StartUp.cs Configure(IApplicationBuilder app) method.
The middlewares are added by this method:
app.UseMiddleware<Type>();
I would like to validate the HttpContext.Request.Path and make sure it can hit one of the controllers. How can I get the list of available routes (controller path) in the middleware code or is there even a simpler way to see if this certain request path will hit one of the registered controller? We used xxxxcontroller : ControllerBase and a [Route("controller/{version}/{id}] attribute to register the controller.
Thanks a lot.
I suggest you to take a look at Asp.net core identity, If I understood what you`re looking for, you need to use roles to guarantee access to certain routes.
I don't know how to get a list of all routes and check that the path is for a valid route but you can use middleware to check the response status code after MVC runs and if the status code is a 404 then you know it wasn't a valid route and you can handle it accordingly.
The UseStatusCodePagesWithReExecute extension method basically uses this approach to handle not only 404 errors but all error status codes.

MVC core routing not functioning as expected

So it's my first time setting up an netcore MVC based application. I've used MVC 4 in the past on plain old asp.net.
So i'm having issues with my routing. My application is an single page application (spa) that is accessible from the home controller on the index action. I can access this controller method fine, and my defaults are set so that this is navigated to at route: /.
I also have a second controller for authentication called AccountController. This controller's methods take and return JSON, rather then views. I can also access the methods on this controller from my application.
The issue i'm having lies in my next controller, which is the start of my API.
As such, i've put it in a folder called api inside my controllers folder. However, no matter what i try, i cannot seem to get the methods on the controller accessible. I have also tried moving it out of the api folder and just having in the route of the controllers folder.
The routing deffinition
app.UseMvc(routes =>
{
routes.MapRoute(
name: "api",
template: "api/{controller=Core}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I've tried adding and removing the api definition, removing the api part, and adding a template for actions aswel, all to no effect.
The troublesome controller
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I've tried adding [Route(~routing here~)] annotations to this controller and its methods with no success either.
Folder structure
I should also mention that i've tried plenty of URL's to access this controller on:
/api/Core/
/Core/
/api/Core/Get
I've been wracking my brain for the best part of a day trying to get this sorted and i know i'm missing something obvious, i just can't for the life of me work out what it is.
Edit:
I've added a cut-down sample of my project to github at: https://github.com/lexwebb/aspnet-test if anyone would like a complete example
Edit 2
It appears that my example works, i'm going to add things in to see what breaks it
AFAIK, default route requires the {action} using as well.
Instead of "api" default routing, you may to use the following configuration for such type of controllers (RESTFul controller):
[Route("api/[controller]")]
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I found this Routing is ASP.NET Core article useful in the past.
So as it turns out, i had made a mistake in a totally unrelated place. I had renamed my project half way through the beginning stage of development, after i had build scripts in place. This led to the the wrong dll being referenced on the server when the code was ran, a version that had all of my routing EXCEPT the new one, of course.

Owin.Testing.TestServer and attribute based routing

We are having the webservice that is using attribute based routing. Few examples of routes:
/api/v1/reporting/client
/api/v1/reporting/client/{id}
/api/v1/reporting/client/{id}/address/{addressId}
/api/v1/reporting/account
/api/v1/billing/client
/api/v1/billing/client/{id}
/api/v1/billing/client/{id}/transactions
Because of this structure we are using attribute based routing (each controller has RoutePrefix attribute and each method on it has Route attribute). At some point we started to convert it OWIN. Also we would like to use have a unit tests for most of our endpoints. So I ended up trying to use Microsoft.Owin.Testing.TestServer. However any endpoint I'm trying to test I'm getting 404 and it looks like b/c we are using attributes for routing (when I tested this method on webapi that doesn't use attributes I do not have this issue). So my question is how can I make attribute based routing work with TestServer or how I can replace attributes with something else that will work with TestServer and provide me routes I listed earlier.

Categories