I am creating an Web API application which takes jwt tokens as arguments.
However, I would like to make this transparent to most of the application.
I would like to take a request that looks like:
website/{controller}/{action}/{token}
then, parse and verify it, then sending it to the controllers as if it were requested in the following format: website/{controller}/{action}/user/{sub}/ect/{ect}
Is there a way for me to implement a catch all transform class in the existing framework? Or will I have to make a transformation method per action?
You certainly don't need a method per action. You can map it to a single controller method that does the routing:
routes.MapRoute(
name: "ProfileRoute",
url: "{controller}/{action}/{token}",
defaults: new { controller = "Home", action = "RouteRequest" },
);
This will then map to HomeController.RouteRequest(string token).
Not sure if you can do what you want in the actual route configuration, but I think not.
You should pass the token in the header. From wikipedia:
Whenever the user wants to access a protected route or resource, the
user agent should send the JWT, typically in the Authorization header
using the Bearer schema. The content of the header might look like the
following:
Authorization: Bearer eyJhbGci......yu5CSpyHI
You can then write a Authentication Filter or custom message handler to process the tokens.
Related
I'm using React router for my SPA and ASP.Net Core as an API backend using Identity for authentication. At the moment I'm trying to add email confirmation as part of the user registration process. What's troubling me is how to generate the confirmation URL without hard-coding the URL path into the backend.
This is what I'm currently working with:
// Somewhere in my UserService.cs...
// '_urlHelper' is an `IUrlHelper` injected into my service
var routeUrl = _urlHelper.RouteUrl("ConfirmEmail_Route",
new EmailConfirmationRequest { Email = email, Token = token },
requestScheme);
// Send the URL in some nicely formatted email
await _emailSender.SendConfirmationEmail(email, routeUrl);
// My API controller action to handle email confirmation
[HttpPost(Name = "ConfirmEmail_Route")]
public async Task<ActionResult> ConfirmEmail([FromBody] EmailConfirmationRequest payload)
{
var response = await _userService.ConfirmEmail(payload.Token, payload.Email);
...
}
The trouble is, that this generates a URL with a path like "/api/auth/ConfirmEmail?Email=..." but my React route is configured to handle a path like "/ConfirmEmail?Email=...". This means, when opening the URL the browser is obviously reaching the API controller action directly rather than going through my SPA (ignoring the fact the action expects a POST request).
All this makes good sense because _urlHelper.RouteUrl(...) only sees the controller actions within ASP.Net Core itself and knows nothing about the routes React uses. What I could do is hard-coding it somewhat like this:
var routeUrl = $"{requestScheme}://{hostname}/ConfirmEmail?Email={email}&Token={token}";
... which is not very versatile (I need to consider how to handle port number, subdomains etc.).
Are there any good alternatives I haven't been able to find yet?
Edit 26/12/2020:
It seems there's a little confusion about what the roles of my SPA and API backend are. To elaborate, this is my setup in Startup.cs (using .Net Core 2.1):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Other irrelevant setup left out for brevity
// ...
app.UseAuthentication();
// Setup routing
// Specific routes are defined in controllers
app.UseMvc();
app.MapWhen(ctx => ctx.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseStatusCodePagesWithReExecute("/api/error/{0}");
});
app.MapWhen(ctx => !ctx.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseStatusCodePagesWithReExecute("/error/{0}");
builder.UseMvc(routes =>
{
// For any path not beginning with "/api" return the SPA (Javascript) bundle
routes.MapSpaFallbackRoute("spa-fallback", defaults: new { controller = "Home", action = "Index" });
});
});
}
In other words: the REST API is supposed to be seen as an individual entity not caring about rendering a view but only exposing the functionality communicating in JSON. The React SPA is a bundled Javascript file that takes care of all the UI rendering and communicating with the REST API for signup, login and whatnot. All is secured using JWT (tokens) for authentication.
This means that the REST API does not care about the paths/URLs that the SPA uses to navigate the user through the application (at least as far as possible I'd like the API to be agnostic about how a client/SPA handles the URLs/UI/navigation - but in this case I might make an exception if necessary). So, there's no controller actions in the backend matching the routes used in the React routes SPA, which makes it difficult to _urlHelper.RouteUrl(...) but I'm open for suggestions.
According you comment, "My API backend doesn't really know about the routes the SPA uses".
What I think is you can ask frontend to pass the URL what they want when click the link in email.
User register will make a POST request to backend with
{
account: xxx
confirmEmailUrl: "https://www.example/confirmEmail"
// Front-end pass what URL they want and create the corresponding page on their side
// So they need to create the page by themselves
}
Send email
var url = "https://www.example/confirmEmail" + userId
SendEmail(url)
So when user get email and click the link in email, it will redirect to the corresponding page created by frontend and you don't need to know any about frontend
3.Call the confirm API in frontend.
They need to implement this by themselves.
In the confirm page they created when page loaded.
Get userId from query string and call the API you provide
I've got Swashbuckle going in my web api project. I want to get a custom handler invoked only when a user tries to hit the swagger ui page. I don't want to add the handler to the pipeline.
I thought something like this might work but it doesn't:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "SwaggerUI",
routeTemplate: "docsite/{*assetPath}",
defaults: null,
constraints: null,
handler: new MyHandler() { InnerHandler = new HttpControllerDispatcher(config) }
I use route attributes everywhere else in the project so I map those attributes first.
My swagger config is using
.EnableSwaggerUi("docsite/{*assetPath}",c =>
hence the docsite route.
I don't want to add the handler to the pipeline because I don't want it to get called with every web api call. I'm using it to restrict access to the swagger UI.
Also I'm using OWIN to authenticate one particular api so I get this error when I try to call that api
The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'MyHandler' is not null.
Parameter name: handlers
Figured I can avoid the error by just making sure the handler only happens when I do a swagger ui route.
Who wants to help!
Hellp all,
Generally speaking I want to "update" URL from action filter in OnActionExecuting method. I registered action filter to be global and that method will be executed before every action is executed.
In OnActionExecuting method I am processing some things and I want to update URL with accountName after that.
I have configured routes which can accept four segments route patterns - something like these: {accountName}/{controller}/{action}, {controller}/{action}...
Basically if I receive request which doesn't contain {accountName} segment or if is that segment empty I would like to update URL with new accountName and be able to see updated URL when request is ended.
I am trying to do that like in following:
filterContext.RouteData.Values["accountName"] = accountName;
filterContext.Result = new RedirectToRouteResult("AccountRoute", filterContext.RouteData.Values);
I added new route data value to filterContext and after I am trying to redirect to specific route with route data values.
That approach doesn't properly because I am encountered with "TO MANY REQUESTS" and it broke my app.
Any idea how I can update URL and avoid to many requests?
Thanks
I have an Asp.net MVC web application that uses convention based routing. I recently added some Web Api 2 controllers, for which I used attribute routing. Despite the documentation claiming that you can use both, I can either get the (attribute routed) API methods to work, or the (convention routed) web application methods.
This is RouteConfig.RegisterRoutes():
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Tables", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "Foo.Cms.Controllers" }
);
}
This is WebApiConfig.Register():
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// The models currently only serialize succesfully to xml, so we'll remove the json formatter.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
}
And this is Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
This way, only the routing to the web api controllers works. If I switch GlobalConfiguration.Register() and RouteConfig.RegisterRoutes(), like so:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
...only the convention-based routing works.
I'm at a loss. What's going on here?
Edit:
What I'm trying to achieve:
The application currently uses the basic {controller}/{action}/parameters convention. So I have a controller called ElementsController that has, for instance, an Index() method that is routed to /Elements or a ListPublic() method that is routed to /Elements/ListPublic. I achieved this with the convention based routing mentioned above.
I also have a bunch of Web Api controllers (for instance, TablesController) that I want to route to using a /api/v0/tables route. I tried to achieve this like so:
[RoutePrefix("api/v0/tables")]
public class TablesController : ApiController
{
[Route()]
public string Get()
{
// ...
}
}
As you can see, it's not the same route pattern: api calls are all prefixed with api/v0/. For some reason though, it still appears to treat them as the default {controller}/{action} routes.
What's occurring is that the "first registered" route is taking effect. If I have a MVC route defined as
{controller}/{action}/{id}
and a Web API route defined as
{controller}/{action}/{id}
The first registered route will take effect.
Why is this the case? Imagine you send a request to the server
foo/bar/1
Which route does this match?
Both!
It will choose the first result that matches the route regardless of the type of routing used. If you want some examples as to how to make these routings work, check out this link
If an URL matches two different route templates, it doesn't matter if they refer to MVC or Web API, the only rule is that the first registered route will be used to select the action to execute.
In your particular case, if you make a GET request to this URL: /api/v0/tables, the action selector starts checking the registered routes.
The first route that you're registering is an MVC route like this: /{controller}/{action}/{id}. So, the template matches with this values:
controller = "api"
action = "v0"
id = "tables"
If you register the attribute routes before the MVC route, the first matching route template will be your route attribute template, so the Web API controller action will be correctly selected.
In other words, if you need to route Web API and MVC in the same application you have to use routes which match different URLs for each action, and be careful with the order in which they are registered: register first the more specific templates, and then the more generic ones, so that the generic templates doesn't swallow the URIs which should be matched with the specific templates.
This is not the only option. You can also use route constraints, for example, if all your API has a particular naming convention which can be checked with a constraint (for example a Regex constraint), you can still use convention routing for Web API.
NOTE: apart from the route matching, it's also necessary that the HTTP method (like POST, GET, etc.) is supported by the action. So you can have two similar actions in the same route that accept different methods, and the Action selector would know which one to choose, but this doesn't solve your problem
In my first Web API project, I'm using attribute routing to direct incoming requests to my product details logic in what I believe to be a straight forward manner:
[Route("v1/inventory/{stock}/add")]
public IHttpActionResult PostAddSku(string stockNumber)
{
// stuff
}
But my customer wants to invoke the logic with a URL formatted as so:
http://localhost:57863/api/v1/inventoy
And in the request body:
action=add&stockNumber=ABC
I must accommodate my customer and recognize that I need to rewrite his incoming url to my expected structure, but am not sure where the best place to do that is. Do I do it in RouteConfig.cs? and if so, what is the most efficient way to inspect that action variable in the request body (which has 4 or 5 acceptable values each of which map to a particular action method in my controller?