✅ Storing data per user

I need an example on good practices for storing the application's data per user.(Web API) I have an app idea where custommers as soon a I register, they get their own space in the app. They want all my data to be private to them and not possible to be seen by other users. I need to make sure that my API requests only allow fetching the data specific to the user who requests it. I can't do something like adding a userId parameter to my API actions. This won't properly secure the data, as anyone would be able to replace that userId with another ID and see the data of another person.
59 Replies
Jimmacle
Jimmacle16mo ago
when your users log in your API would give them a special token that only the server can generate that identifies them and optionally says what kinds of things they can access for example, a JWT (json web token) then they send that with every request and you can use that to get trusted information about them and control access
aliyorulmazdev
aliyorulmazdevOP16mo ago
I already have JWT on my API calls. Can you show me an example of controller request to get products. That will show Ali to Ali's Products, Veli to Veli's Products.
Jimmacle
Jimmacle16mo ago
it looks like you can get the current user from the User property of your controller then get a claim from that like their username and use that to do anything user-specific
aliyorulmazdev
aliyorulmazdevOP16mo ago
aliyorulmazdev
aliyorulmazdevOP16mo ago
so i need to add userManager to my products controller. get current user via User . and modify my query to filter products via User.id
aliyorulmazdev
aliyorulmazdevOP16mo ago
actually i think i need to add this to List query instead of controller.
aliyorulmazdev
aliyorulmazdevOP16mo ago
Is that the correct way to do it ?
صآئدxXxالدمآء
@cesaydestek Hi bro Can you use JWT Authorization?
aliyorulmazdev
aliyorulmazdevOP16mo ago
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; with jwt
aliyorulmazdev
aliyorulmazdevOP16mo ago
صآئدxXxالدمآء
Create custom user
صآئدxXxالدمآء
Username password 🔑 and role Role make it Admin
صآئدxXxالدمآء
Check the this link Create JWT and hard Client user Then make Roles Base user give him Role which you needed @cesaydestek And do the logic Authorize(Role ="Admin") @cesaydestek what you need to create you can do it Try if you want any help let me now
aliyorulmazdev
aliyorulmazdevOP16mo ago
its not what i want i want user1 to does not have access to user2's products. When user1 calls an /getProducts endpoint it should give user1 to user1's products.
صآئدxXxالدمآء
I think i give you the answer already If you give users different Roles they will not access to this endpoints For example Admin user 1 can access product but user 2 is customer don't have access to this product?
aliyorulmazdev
aliyorulmazdevOP16mo ago
thats not my answer still i think. I cant define smt like authorize("user1") to my endpoint. my endpoint is one and only it should check user with httpcontextaccessor and detect users id maybe
Pobiega
Pobiega16mo ago
In the controller/httpcontextaccessor you have access to the authenticated user So use that users id in your query here is a fairly trivial implementation of this, for your reference
[ApiController]
[Route("[controller]")]
[Authorize]
public sealed class ItemsController : ControllerBase
{
private readonly ApplicationDbContext _dbContext;
private readonly UserManager<IdentityUser> _userManager;

public ItemsController(ApplicationDbContext dbContext, UserManager<IdentityUser> userManager)
{
_dbContext = dbContext;
_userManager = userManager;
}

[HttpGet]
public async Task<IEnumerable<Item>> GetItems()
{
var userId = _userManager.GetUserId(User);

var items = await _dbContext.Items
.Where(x => x.UserId == userId)
.ToListAsync();

return items;
}

[HttpPost]
public async Task<IActionResult> AddItem(string content)
{
var userId = _userManager.GetUserId(User);

if (userId == null)
{
return Unauthorized();
}

var item = new Item
{
UserId = userId,
Content = content
};

_dbContext.Items.Add(item);
await _dbContext.SaveChangesAsync();

return Created();
}
}
[ApiController]
[Route("[controller]")]
[Authorize]
public sealed class ItemsController : ControllerBase
{
private readonly ApplicationDbContext _dbContext;
private readonly UserManager<IdentityUser> _userManager;

public ItemsController(ApplicationDbContext dbContext, UserManager<IdentityUser> userManager)
{
_dbContext = dbContext;
_userManager = userManager;
}

[HttpGet]
public async Task<IEnumerable<Item>> GetItems()
{
var userId = _userManager.GetUserId(User);

var items = await _dbContext.Items
.Where(x => x.UserId == userId)
.ToListAsync();

return items;
}

[HttpPost]
public async Task<IActionResult> AddItem(string content)
{
var userId = _userManager.GetUserId(User);

if (userId == null)
{
return Unauthorized();
}

var item = new Item
{
UserId = userId,
Content = content
};

_dbContext.Items.Add(item);
await _dbContext.SaveChangesAsync();

return Created();
}
}
aliyorulmazdev
aliyorulmazdevOP16mo ago
_userManager.GetUserId will work the way i wanted i guess will it work with mediatr too ?
Pobiega
Pobiega16mo ago
sure. get the id, and pass it to the command/query
aliyorulmazdev
aliyorulmazdevOP16mo ago
cant i add _usermanager to command page ?
Pobiega
Pobiega16mo ago
?
aliyorulmazdev
aliyorulmazdevOP16mo ago
the thing is maybe i can only use _userManager on controller itself. so i need to detect id there. query here should have private userId get set i should pass userId from controller
Pobiega
Pobiega16mo ago
yes
aliyorulmazdev
aliyorulmazdevOP16mo ago
to query ur so kind
Pobiega
Pobiega16mo ago
var query = new Query(userId); verify that userid is not null first, just to be safe. and ofc, [Authorize] on the endpoint so only logged-in users can access it
aliyorulmazdev
aliyorulmazdevOP16mo ago
do i need to _userManager.GetUserId(User) write getuserId function
Pobiega
Pobiega16mo ago
no, UserManager<TUser> is an Identity thing
aliyorulmazdev
aliyorulmazdevOP16mo ago
i added middleware to controllers via builder options. unless i tag [AllowAnonymous] its all authorized
Pobiega
Pobiega16mo ago
assuming you are using Identity to handle your auth, it should work as is.
aliyorulmazdev
aliyorulmazdevOP16mo ago
so i should pass this id to every Crud query is there better way to do it instead of calling it on every httppost put del get request same as i added authorize to builder.options
Pobiega
Pobiega16mo ago
You're overthinking it
aliyorulmazdev
aliyorulmazdevOP16mo ago
sorry, just messing 6 months ago started. Junior junior here
Pobiega
Pobiega16mo ago
The problem with moving the "get current user" into the handler itself would be that your commands and handlers are now hard-coupled to asp.net just today, I had to make a CLI application that uses ~10 different commands from our business layer. Since our commands and handlers are entirely unaware of HTTP concepts, that was very easy in your case, it would not work
aliyorulmazdev
aliyorulmazdevOP16mo ago
Im trying to write SaaS order management system. Looking for a best way. People should not have access to other peoples stuff
Pobiega
Pobiega16mo ago
sure. the term for this is "multi-tenant" multiple separate datasets inside the same instance of an application
aliyorulmazdev
aliyorulmazdevOP16mo ago
for research. what would be best way to write smt like this. should i create seperate database(collection maybe) for each user
Pobiega
Pobiega16mo ago
no that wouldn't scale very well
aliyorulmazdev
aliyorulmazdevOP16mo ago
so regional is best way
Pobiega
Pobiega16mo ago
most of the time, you just have a TenantId property on EVERYTHING and all users are associated with a TenantId
aliyorulmazdev
aliyorulmazdevOP16mo ago
i just readed some database where i can store all users (products,options,etc etc) within 1 table
Pobiega
Pobiega16mo ago
yeah, thats a document database. its... very different I'm not a big fan of document dbs personally, as I've never had a usecase where it was better than a relational one
aliyorulmazdev
aliyorulmazdevOP16mo ago
relational is the way i guess last but not least can i use Authorize Claims same time on controllers
Pobiega
Pobiega16mo ago
?
aliyorulmazdev
aliyorulmazdevOP16mo ago
for example ali have 3 waiters waiters should have create access only
Pobiega
Pobiega16mo ago
.. create access only?
aliyorulmazdev
aliyorulmazdevOP16mo ago
on access to create endpoint for delete / edit they dont have access create and get only
Pobiega
Pobiega16mo ago
okay, sure if your question is, can I have multiple authorize attributes on a single endpoint, the answer is yes
aliyorulmazdev
aliyorulmazdevOP16mo ago
so i should add authorize attribute and add ali's Id to waiter this way waiter have access to endpoint cause it has ali's Id and they have create attribute its so hard to think about it..
Pobiega
Pobiega16mo ago
You're just saying random words at this point, but I think I get what you are trying to do You could give role-based authorization a try, if your system has many "groups" it makes life easier
aliyorulmazdev
aliyorulmazdevOP16mo ago
Role based authentication on multi tenant app
Pobiega
Pobiega16mo ago
claims would work fine too thou in fact, for multi-tenant I prefer claims
aliyorulmazdev
aliyorulmazdevOP16mo ago
so for id i should check claims too ? i registered userId on claim. maybe i can add role there too
Pobiega
Pobiega16mo ago
then you can skip the userId step and just have a claim for TenantId (this becomes important when you have more than one user per tenant) you could do a "role" claim sure or you do a "CanEditOrders" claim and if you have that, you can edit orders. etc
aliyorulmazdev
aliyorulmazdevOP16mo ago
for every endpoint i should write a claim this is best way ?
Pobiega
Pobiega16mo ago
this is one way there is no best way for all situations
aliyorulmazdev
aliyorulmazdevOP16mo ago
so i need another controller for manageClaims thank you sir! i wish you best
Accord
Accord15mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server