I'm relatively new to Microsoft solutions and I need your guidance. I'm quite certain the answer exist somewhere, but I lack key words for my searches.
My stack is as follows: .NET Core 6, Entity Framework, Azure AD.
Ideally, I wouldn't need to store user's information in my DB. Everything should come from Azure AD. Authentication is already working, and I'm able to retrieve my current user in my controllers and services.
My goal is to add some relationships between my Azure AD users and some of my entities.
For example, let's say I'm doing a collaborative Todo list and I want my users to be able to cancel tasks. So I have an entity named Task, and in it, a column named CanceledBy.
On cancellation, in my TaskService, I want to do something like that:
var task = dbContext.tasks.find(taskId);
task.CancelledBy = User;
"User" is the current user. In a perfect world, it works like that, out of the box. Behind the magic, the user OID would be stored, and the user would be automatically resolved through GraphAPI (or something else?) when needed.
I'm guessing there may be a type that could help me in my entity Task? Like:
public class Task
{
...
AzureADUser CancelledBy {get;set;}
}
And some configuration to add to my Startup.cs.
Or am I completely wrong? Am I doomed to implement myself a service to retrieve my users through GraphAPI with their OID?
In that case my Task would look more like
public class Task
{
...
string? CancelledByOID {get;set;}
[NotMapped]
User? CancelledBy {get;set;}
}
And I'd have to map my users manually in my services every time I need them?
I'm simply looking for direction here. What are key words I could use to help my search? I tried stuff like "Relathionship with Azure AD User in Entity Framework", or "GraphAPI and Entity Framework" without success.
Thank you!
EDIT : After more searches, I think I want to do LinQ on OData connected to Entity Framework and Graph API. I hope it makes sense.
Related
In the new SPA (react and angular) web templates for .Net core 5. I'd like to fetch the current logged in User. However, when I try to get a user in the controller the User doesn't have anything populated.
Does anyone know how to achieve this with the new Identity Classes?
I've made a repo of the vanilla reactJS template, the only thing I changed is the line highlighted in my screenshot below to show there's no user set.
I've done a bit of googling and these pages are all I could find on the topic, unfortunately, they don't give enough detail for me to be able to implement anything practical.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-5.0
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0
Backend:
ClaimsPrincipal currentUser = this.User;
var currentUserName = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value;
ApplicationUser user = await _userManager.FindByNameAsync(currentUserName);
On the frontend if you need yo access it
//with UserManager
UserManager<ApplicationUser> UserManager
#{
var user = await UserManager.GetUserAsync(User);
}
// with SignInManager
SignInManager<ApplicationUser> SignInManager
#if (SignInManager.IsSignedIn(User))
To answer my own question.
In order to populate the User detail in the HttpContext you have 1 of 2 routes. Either change
services.AddDefaultIdentity<IdentityUser>();
to
services.AddIdentity<IdentityUser, IdentityRole>();
or you can continue to use the Core Identity
services.AddIdentityCore<IdentityUser>();
but then you also need to implement your own Microsoft.AspNetCore.Identity.ISecurityStampValidator and add it as transient services.AddTransient<ISecurityStampValidator, MyValidator>();
Your MyValidator implementation will be responsible for validating the cookie. You can see the default implementation here on github
Edit: Under the hood services.AddDefaultIdentity<IdentityUser>(); uses services.AddIdentityCore<IdentityUser>();. I feel like its importatnt to know this.
Is it possible to retireve from Facebook both basic information and additional variables at the same time? For example, using .NET sdk and calling
client.Get("me")
I will get name, first_name, last_name, etc. and other public information.
I can also run
client.Get("me", new { fields = "name,photos" })
but this gives me only variables I've asked for, without basic information. Do I need to call it twice? Eventually I could use the second option and ask for all basic fields individually like username, gender, but in this case how to ask for "other public information" as well?
Thanks in advance
Bartek
Your point is interesting. But /me only works as a shortcut to quickly get all basic fields at once. You have to override it.
Don't call it twice, since API requests take some time. I advice you to make only one complete request by specifying all the fields you really need.
Let's say I have 3 entities: Advert, User and UserRole. And in Web.Api project GetAllAdverts method.
public IEnumerable<Advert> GetAllAdverts()
{
return repository.GetAll<Advert>();
}
When i enter url ../api/advert I get JSON with all Adverts and data about adverts, but I get all data about user and user role too.
How can I get for example all advert data and only UserName form entity User ?
Is this done by creating DTOs ?
Thanks in advance !
Using DTO's is usually a good idea. It is more work, but it gives you full control and it abstracts out peculiarities of a specific data layer.
In your case, if you really only want UserName you even have to use a DTO, because it is impossible to partly load the User as navigation property from Advert.
If it does not matter that you see all properties of User except its navigation properties (like role), you may also consider to (temporarily) turn off lazy loading for the context in the repository and eager load Advert.User by using Include.
Background: Completely new to MVC2. Has C# experience, but limited web experience.
I need more fine grained access than simply assigning a Role to a user. The user may have the role at 0+ points in a tree.
/
/Europe
/England
/France
/USA
For example, a user might be moderator of all forums under "Europe" and have access to posting news in France.
The two example controllers have actions as these:
ForumController:
public ActionResult DeletePost(int id) { ... }
NewsController:
[HttpPost]
public ActionResult Post(int treeID, ...) { ... }
How should I approach this? From what I gather Membership+RoleProvider cannot do this level of fine-grained control.
Previously I have written custom user/role/auth system which supported all this, but it was incompatible with "the standard" controls such as LoginView.
The goal would be to have roles allowing access like so:
NewsAdmin
Add news
Edit news
Delete news
NewsPoster
Add news
Therefore, the Post action of News controler should check: Does user have "Add news"-access where he is trying to post?
I would really like to somehow specify this using attributes, so the actual action code could be cleaner and just assume that the caller has appropirate access.
Hope the question makes sense, and I can get some pointers on where to read.
(Oh, and I'm sure this question has been answered in some variant before. I just can't seem to find it. I won't mind single-link replies, if you feel they might be helpful to read)
I think you're being too quick to dismiss the role provider. If a user had a role called NewsAdmin_Europe_AddNews that would pretty much answer the question, wouldn't it?
Once you've made your authentication scheme work with the role provider, you need to tie that into MVC. Subtype AuthorizeAttribute and override AuthorizeCore. Warning: Your code here must be thread-safe and re-entrant. Call base.AuthorizeCore and then test for the specific role based on the URI/query (you won't get route values since this can be served from cache, bypassing MVC altogether).
This is some work, but will be more secure in the end than trying to reinvent membership.
So we have our web app up and going with entity framework. What we'd like to do is impersonate the current user when we're accessing the DB. We're not interested in setting impersonation up in our web config.
Ideally using something like this: Link when we're about to access data.
UPDATED: I'm looking for a way to abstract this code out so I don't have to have it in every repository function call.
Your EF connection string is going to need to be set up for using a trusted connection.
You won't need to set up Impersonation in your web.config, but you do need to be using Windows Authentication.
Then just do this:
using (((WindowsIdentity)HttpContext.Current.User.Identity).Impersonate())
using (var dbContext = new MyEntityFrameworkContainer())
{
...
}
Any code inside the curly braces of the using statements will run as the authenticated user.