Best practice: User Identites as parameters? - c#

I am currently developing an API with ASP.NET in C#.
One endpoint needs to call another in order to return a value.
public class Testcontroller : BaseApiController
{
[Authorize]
[HttpGet]
[Route("1", Name = "F1")]
public async Task<IHttpActionResult> F1()
{
return await F2(); // calls 2nd method
}
[Authorize]
[HttpGet]
[Route("2", Name = "F2")]
public async Task<IHttpActionResult> F2()
{
int I = 2;
return Ok(I.ToString());
}
}
Ok, the returned value of this whole thing will be 2, which is absolutely fine. However, in the real API, the second methods needs to retrieve some data about the user. That is normally handled with
var Name = ClaimsPrincipal.Current.Identity.Name;
var CurrentUser = await this.AppUserManager.FindByNameAsync(Name);
These two lines get the user information through the bearer token, that is passed to the method through the authorization process.
Considering this, the first function could call the other one. The downside is that those two LOCs for the user data do not work because this token is not passed properly.
How would you suggest working around this problem? I thought about adding an optional parameter and passing the CurrentUser through that. But I thought that might cause some issues in terms of security?
Thanks for your help!

Firstly don't use ClaimsPrincipal.Current. In Controllers you have a User property, that's where the identity is. ClaimsPrincipal.Current is a hang over from .NET 3.5. If you are going to move to .NET Core, then the User property is the right way to do it.
In response to your actual question there's no security issues in passing that through to other functions outside your controller.

Related

Pass data from controller to View with redirect method

I have an ASP.Net web application in C# in which I want to show some visualizations graphs in my report.cshtml View.
For the moment, I'm using the httpget function of my report.cshtml View to past the data to my View. But with this method, I can't filter the data. So this is what I want to do :
When an user connect in my website, it launch my Httppost function inside my HomeController, with the username & password as arguments.
I use this function to redirect to my report.cshtml View with the response.redirect method, in which the user will should his personnal data as visualizations.
My database is stored locally in a variable named my_list which I can use in my httppost function. What I want is to past this list to the js section of my report.cshtml when the redirection occurs.
Is this possible by using the redirect method?Or, if not, is there another solution to do it ?
My Index.cshtml :
<form method="post" , enctype="multipart/form-data">
<input required id="my_pseudo" name="my_pseudo" v-model="pseudo" placeholder="Username" />
<input required id="my_password" name="my_password" type="password" v-model="password" placeholder="password" />
<Button id="test_click" Text="Connexion">Connection</Button>
</form>
My httpget function :
public ActionResult EmbedReport()
{
List<Dashboard___Veins_Data___By_Institution> my_list = fct_BO.Class1.My_function("my_user");
// It's at this moment that the filter is applied, so I want that, instead of "my_user", I can put a variable which is the "Contact_name" of the user currently connected.
ViewBag.Message = my_list;
return View();
}
My httppost function
public ActionResult Index(string my_pseudo, string my_password)
{
//It's not good for the moment, but it's here that I want to see if "my_pseudo" and "my_password" match a line of my table "User_connection". If yes, the redirect method occurs and I would like to past my list at the same time ->
if (...)
Response.Redirect("~/Home/Report");
// If not, then the user will receive an error message.
else
Response.Write("<script>alert('Login or password incorrect')</script>");
return View();
}
I didn't finish this function, because my table "User_connection", in my database, is not finished for the moment.
Also, somebody adviced me to use RedirectResult instead of response.redirect. Is it a better solution?
Thanks.
May I suggest that you wrap your behaviour in interfaces?
so that you have an Interface
public interface IErrorHandler {
void provideErrorResponse(HttpResponse response);
}
and
public interface ISuccessHandler {
void provideSuccessfulResponse(HttpResponse response);
}
Because now you can implement small single purpose classes that process a correct or incorrect response a certain way, meaning if you need to error handle more than one result the same way (VERY LIKELY) you can simply dependency inject the error handler into your other controllers as well, and only have 1 set of tests for the interface, instead of a suite of tests to test the same behaviour on each controller.
In terms of your final question:
"
Response.Redirect("http://www.microsoft.com/gohere/look.htm");
Calling Redirect is equivalent to calling Redirect with the second parameter set to true.
Redirect calls End which throws a ThreadAbortException exception upon completion. This exception has a detrimental effect on Web application performance. Therefore, we recommend that instead of this overload you use the HttpResponse.Redirect(String, Boolean) overload and pass false for the endResponse parameter, and then call the CompleteRequest method. For more information, see the End method."
Taken from:
https://learn.microsoft.com/en-us/dotnet/api/system.web.httpresponse.redirect?view=netframework-4.8
That is likely why he recommended you not use it. but this is a total guess, as I cannot know what "somebody" was referencing at the time :)
As an example:
public class HomeController {
private IErrorHandler _errorHandler;
private ISuccessHandler _successHandler;
public class HomeController (IErrorHandler errorHandler, ISuccessHandler successHandler)
{
_errorHandler = errorHandler;
_successHandler = successHandler;
}
public ActionResult Index(string my_pseudom, string my_password)
{
if (...)
_successHandler.provideSuccessfulResponse(Response);
// If not, then the user will receive an error message.
else
_errorHandler.provideErrorResponse(Response));
}
}
Notice that you can even re-use successful responses provided they don't have a difference in how a success response should be handled (Less likely)
for purpose of "filtering" your view, that is usually done via the end-point to back-end by passing a set of parameters from your view, to the controller, then to the back-end, and then the resulting list of items, will be filtered at the back-end and provided back as a response.
So when you say you wish to accomplish this via re-direct I am not sure I completely follow what you intended to do?
Something like this?
public interface ILoginHandler {
ILoginResult HandleLogin(string username, string password);
}
public inteface ILoginResult {
bool WasValid {get;set;}
}
public class HomeController {
private IErrorHandler _errorHandler;
private ISuccessHandler _successHandler;
private ILoginHandler _loginHandler;
public class HomeController(IErrorHandler errorHandler, ISuccessHandler successHandler, ILoginHandler loginHandler)
{
_errorHandler = errorHandler;
_successHandler = successHandler;
_loginHandler = loginHandler;
}
public ActionResult Index(string my_pseudom, string my_password)
{
ILoginResult loginResult = _loginHandler.HandleLogin(my_pseudom, my_password);
if (loginResult.WasValid)
_successHandler.provideSuccessfulResponse(Response);
else
_errorHandler.provideErrorResponse(Response));
}
}
based on who logs in, you could make a factory pattern, that takes the username, and then provides a specific request to your back-end and returns the list you want, if you want filtering, provide the filtering values as part of the URL, or make it a post, so you can provide parameters that way.
And then update your factory to use these parameters also. Shouldn't be a problem then at all.

Why isn't my C# .Net Core Rest API route finding my method?

I am working on an API.
I have an "AbstractController.cs" and I am having difficulties calling a GET with two parameters.
[Route("api/[controller]")]
[ApiController]
public class AbstractController : ControllerBase
{
// GET: api/Abstract
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "missing implementation" };
}
Going to https://localhost:44363/api/abstract/ generates this.
["missing implementation"]
Awesome!
Now I need to make the Get method that passes in a show year, and code to a SQL query. Easy enough?
// GET: api/Abstract?ShowYear=2019&ShowCode=248621
[Route("api/{controller}/{ShowYear}/{ShowCode}")]
[HttpGet]
public Abstract Get(int ShowYear, int ShowCode) // GetAbstractByYearAndSHCode
{
string x = "";
return new Abstract();
}
No matter what I do I can't get this method to breakpoint/enter execution!
I'm guessing it's a routing issue, but I've tried most tenable ways of calling the endpoint.
Now I check the MS Documentation like any self-respecting-learning-programmer would.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-3.1
Endpoint routing in ASP.NET Core 3.0 and later:
Doesn't have a concept of routes. Doesn't provide ordering guarantees
for the execution of extensibility, all endpoints are processed at
once.
GULP Sounds scary.
I haven't touched my middleware at this point.
All examples I've looked at don't have my "MapControllers();" method in their Startup.cs.
This is because it's new to .NET Core 3.0/3.1
The method: "Adds endpoints for controller actions to the IEndpointRouteBuilder without specifying any routes."
OK, so do I have to manually specify the route here still?
I did this, and it sorta works.
Well, it breakpoints in the startup.cs now when I go to https://localhost:44363/api/abstract/2019/287
Wait, it's not doing ANYTHING with my controller code! Why?
The following (also above) code ends up declaring as a null in the startup.cs
var controller = context.Request.RouteValues["Abstract"];
Hoping to learn what I'm doing wrong.
MapGet("/api/abstract/{showYear}/{showCode}", ...);
Isn't this code responsible for mapping that route with the controller named AbstractController.cs in my Controllers folder? Not hitting any breakpoints.
Edit:
Reading through this which compares the differences in the Startup.cs from .Net Core 2, 2.2, MVC projects vs 3.0
I have a good feeling reading all of this, I will find the issue.
https://andrewlock.net/comparing-startup-between-the-asp-net-core-3-templates/
Edit.. nevermind didn't find a resolution.
Edit 2:
Completely commenting out this troublesome code in startup.cs:
endpoints.MapGet("/api/abstract/{showYear}/{showCode}", async context =>
{
var controller = context.Request.RouteValues["Abstract"];
var showYear = context.Request.RouteValues["showYear"];
var showCode = context.Request.RouteValues["showCode"]; // just an example.
//await context.Response.WriteAsync($"Hello {showYear} is your year, {showCode} for the code!");
//GetAbstractByYearAndSHCode();
});
endpoints.MapGet("/api/abstract/{showYear}", async context =>
{
var name = context.Request.RouteValues["name"];
await context.Response.WriteAsync($"Hello {name}!");
});
Resolves my issue of not being able to reach the controller.
https://localhost:44363/api/abstract/2019 hits, and the id value is 2019. Great.
https://i.imgur.com/rmHyDPg.png the output looks great.
I am still not able to use > 1 parameter. How do I simply use the Year, and ShowCode paramaters? What's the syntax?
https://localhost:44363/api/abstract/2019
Just add the parameters to your attribute
[HttpGet("{ShowYear}/{ShowCode}")]
The "api/abstract" route is already used by the first method.
You cannot use it again for other actions. Create a new one as shown inline.
[HttpGet("{GetAbstractByYearAndSHCode}",Name = "GetAbstractByYearAndSHCode")]
public Abstract Get(int ShowYear, int ShowCode) // GetAbstractByYearAndSHCode
{
string x = "";
return new Abstract();
}
And call the url as shown:
https://localhost/api/abstract/GetAbstractByYearAndSHCode?ShowYear=1&ShowCode=5

ASP.NET Web API 5.2.3 Attribute Route returning 404

I've got an ASP.NET Web API project that I'm working on. I've got an APIController called SpellbookController that has basic CRUD operations to an EntityFramework repository.
However, when I try to add a method that takes a parameter that's not an id, I can't seem to get it to route correctly.
Here's what it looks like:
// GET: api/Spellbooks/user#email.com
[ResponseType(typeof(List<Spellbook>))]
[Route("api/Spellbooks/{username}")]
public IHttpActionResult GetSpellbook(string username)
{
List<Spellbook> spellbooks = db.Spellbooks.Where(x=>x.Username == username).ToList();
if (spellbooks == null)
{
return NotFound();
}
return Ok(spellbooks);
}
So I'm trying to hit http://localhost:xxxx/api/Spellbooks/emailaddress,
but I get a 404 every time.
It's definitely an APIController, and I have config.MapHttpAttributeRoutes(); turned on.
What am I missing?
Where is your username parameter?
Your call should looks like this-
http://localhost:xxxx/api/Spellbooks/emailaddress/David
Update
Try to declare the parameter as string {username:string}
Update 2
The problem is with your '.' as you can see in this link.
You can send the email without the point in the parameter and then replace ite back to you regular mode or you can use all the good advice that the link provide.
If you step through it, do you get a value when looking at username? Or is it always null?
If your route is not working, a simple way to debug the route is to make a request to http://localhost:xxxx/api/Spellbooks/?emailaddress=thisemailaddress and see if you can get a response. In fact, from a REST standard, it can be argues that it's a cleaner route, since you will be returning a collection of elements, rather than a single object.

How does WebSecurity.IsAuthenticated check if you're logged in or not?

This is probably a stupid question, but I've looked pretty hard on Google and couldn't come up with the answer.
I'm creating a website where the database is in another continent, so speed is a crucial issue.
From what I understand,
WebSecurity.Login(form.userName, form.password))
initially checks the database it's initially set up with and logs you in if the username and pw are correct.
Now for every backend function I'm writing, I'm sticking a
[Authorize]
attribute and a
if (WebSecurity.IsAuthenticated)
{ .... }
before performing any action. So does the WebSecurity.IsAuthenticated check the database at all to check if it is logged in or not? I just need to know for speed reasons.
Also is it redundant to put the [Authorize] and WebSecurity.IsAuthenticated in EVERY backend method?
Thanks for any help and opinions
So does the WebSecurity.IsAuthenticated check the database at all to check if it is logged in or not?
No, it just checks if the principal object in current request has the authentication flag set to true.
Usually the principal object is set by an authentication module, there are few different modules. Most use cookies to persist the information of the authenticated user (e.g. Forms, SessionAuthentication) and if the cookie is present and it's valid, the module sets the principal for the request which you can get by calling:
HttpContext.Current.User
in any method of your code (assuming the call is made from a web app that sets the HttpContext.Current).
Some authentication modules can rely on other authentication factors, for example the Windows authentication relies on NTLM/Kerberos protocols which in turn rely on specific headers rather than cookies.
Also is it redundant to put the [Authorize] and WebSecurity.IsAuthenticated in EVERY backend method
Yes and no.
What you most probably mean by "every backend method" is you mean a controller/action method in an MVC app. If this is so then, yes, you don't have to repeat both in a controller/action method.
But in any other method in your backend that is not a controller/action, the WebSecurity.IsAuthenticated still works while the Action attribute doesn't.
Thus, if by "every method" you literally mean every possible method, then the answer is no, these two are not redundant. One works always, the other - only in MVC controllers.
In addition to what #Wiktor Zychla has said, it is useful to note that WebSecurity.IsAuthenticated is functionally identical to Request.IsAuthenticated; to be precise, it's a wrapper:
namespace WebMatrix.WebData
{
//other class content omitted for brevity
public static class WebSecurity
{
//Context
internal static HttpContextBase Context
{
get { return new HttpContextWrapper(HttpContext.Current); }
}
//Request
internal static HttpRequestBase Request
{
get { return Context.Request; }
}
//WebSecurity.IsAuthenticated
public static bool IsAuthenticated
{
get { return Request.IsAuthenticated; }
}
}
}
So if you put all of the above together, WebSecurity.IsAuthenticated is equal to HttpContextWrapper(HttpContext.Current).Request.IsAuthenticated under the hood.
Aside: As correctly stated in the other answers and comments, [Authorize] is used to specify access to controller and action methods:
Specifies that access to a controller or action method is restricted to users who meet the authorization requirement. (source)
So IsAuthenticated works on both action methods and non-action methods, while [Authorize] only works on controllers and action methods, making it indeed redundant to use both on action methods.

Is it safe to use query string in a post action?

I've got two controller actions at the moment: one that displays a strongly typed view, based on an unique identifier and another one that changes the model. Here's some code to visualize what I mean:
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
// Request the device based on the code.
var device = GetDeviceModel(code);
// Present the device in a view.
return View(device);
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
}
The code identifies the device, but it is also possible to change it. And that's my problem: Inside the post method I can access the new code using model.Code, but I also need to know the old code, to be able to change it.
I tried several alternatives, but none of them did satisfy my requirements:
ViewData does not get persisted until the post.
TempData is based on Sessions or Cookies – none of them I want to use at the moment.
Hidden fields and model bindings are not an option, because they can be manipulated on client side.
Finally I tried requesting data from the query string like this:
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldCode = Request.QueryString["code"];
}
And this works! But I haven't found any resources on this around the web. So my question is: Is using the query string inside a post action safe against modifications? And what (if there are any) are the conditions for this to work?
If this is not a valid way to "remember" the code, are there any alternatives?
Based on the requirements you have mentioned in your question, it seems that you are looking for safety in the case. So I would say that QueryString is not safe. In my opinion using Session variables is the appropriate method that keeps your critical data in a safe location. You can use the method like this
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
Session["code"] = code;
....
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
if (Session["code"] == null)
throw new Exception("Error Message.");
var code = Session["code"].ToString();
....
Session["code"] = null;
}
I think you can make another property inside DeviceModel name OldCode.
and in your view you can save this value in hiddenfield like this
#Html.HiddenFor(m=>m.OldCode)
Now in controller post method you can get both the values like this.
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldcode=model.OldCode;
var newcode=model.Code;
}
Hope this helps...
Nothing submitted via a GET or a POST request is safe from modifications. While a GET query string is obviously easy to modify, it doesn't take much effort to spoof POST variables either. It sounds to me like you need to re-think your approach.
Optimally, you would do permission checking server-side to determine if the user is allowed to update the device with the passed (old) code. If they do not have permission, return an error such as an HTTP 550 code.
If you truly can't support this approach, I would suggest adding an "OldCode" field to the DeviceModel class as suggested by others, but encrypting it before sending it to the client. Then you can safely write it to a hidden field, and decrypt back on the server without fear of the user changing the value.

Categories