If you had a dollar for every time I say _permission_ in this video, how rich would you be? Get the source code for this video for FREE → the-dotnet-weekly.ck.page/permissions3 Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@@MilanJovanovicTech I counted permission/permissions: 98 authorization(not authorize): 43 You should reupload the video and say 2x permission at the end😂
The thing I was talking about before in other videos in this series was a third way to solve the problem you mentioned here 4:30 . (for authorization without calling the database and dealing with caches or storing all claims in jwt token) which is using enum flags. this is a much faster and more flexible way to deal with this problem. and we can use a single value in the database or jwt tokens, instead of list of permissions.
@@MilanJovanovicTech I had the same problem with jason taylor suggestion before that's why I've created that library (infiniteEnumflags) also I added an example of how it works to the repo. you can check it out.
Every time I come to watch your video for a specific topic, I learn tons of other topics too. Can you please make some videos about unit test and integration tests which are on a real project. Because I watched lots of videos about it, but still, I am stuck. Thanks for that and stay awesome. 😊😊
I already have a few videos on those topics: - th-cam.com/video/tj5ZCtvgXKY/w-d-xo.html - th-cam.com/video/a6Qab5l-VLo/w-d-xo.html - th-cam.com/video/Pk2d-qm5KwE/w-d-xo.html
Great post Milan. In the past when I first time implemented permissions based RBAC I spent so much time to get content that gathered here in several videos. One thing I noticed that might be improved in your example. In AuthorizationPolicyProvider you getting policy and if it's not null you creating it. To avoid creation of the policies all the time I'd suggest to take IOptions options from constructor to local variable and then slightly modify GetPolicyAsync to: if (policy == null) { if (Enum.TryParse(policyName, out var permissionId)) { policy = new AuthorizationPolicyBuilder() .AddRequirements(new PermissionRequirement(permissionId)) .Build(); // Add policy to the AuthorizationOptions, so we don't have to re-create it each time options.Value.AddPolicy(policyName, policy); }
I just wanted to thank you for everything that you are doing, your content is awesome and it helped me a lot. I think you deserve way more subscribers than you have :) p.s. good luck, loocking forward to video course series
@9:17 when filtering for the Member, would you not use "FirstOrDefault()" instead of "Where"? "Where" will return an IEnumerable containing one element but it will have had to check the whole table; whereas "FirstOrDefault" will exit out once it finds the Member you're looking for. However, you'd also need to deal with a potential null value if no Member with that ID exists. Maybe I'm missing something as to why "Where" was used here so thought I should ask/point it out.
It's going to be an index seek at the database level regardless. And I'm only returning that Member's roles in the end. The main point is there won't be any table scans involved.
In the next video u will show how to put the permissions into the claimsidentity. The thing with JWTs is that once they're created u cannot update them. How would u handle permission updates in this case?
That's the tradeoff you're making to gain performance. There isn't a clear way how to revoke a JWT once it's issues. One thing you can do is give it a short TTL.
@@Dustyy01 I'd ask myself two things: - How often do user's permissions actually change? - Can I suffer the performance hit of checking permissions in DB? If permissions don't change frequently, just put them in JWT and be done with it. Give the token a 60min lifetime, and just refresh it from the client when it expires. If I want to always fetch from DB to have the latest state, but I don't want to take a performance hit, I can introduce caching. And I just need to deal with cache invalidation when permissions change. Both solutions are viable, and simple to implement. I use fetching permissions from DB + caching with IMemoryCache on a current project, and it works like a charm.
I'm sorry, I asked the question wrong. By following your videos I was able to implement the HasPermission method and initialize the permissions and related entities using IEntityTypeConfiguration and builder.HasData. The problem is in the API tests using xUnit, I use the CustomWebApplicationFactory class that inherits from WebApplicationFactory to initialize my databases but I'm not able to insert the related entities. Could you explain how to seed the Permissions, Roles and RolePermissions in the CustomWebApplicationFactory?
Thanks for the great video Milan! I've got my version working, the only thing is that on the line 'AuthorizationPolicy? policy = await base.GetPolicyAsync(policyName);' the policy is always null, do you know why this is?
@@MilanJovanovicTech Of course. Now I will try to play with it in a test solution. I normally rather like to work with Permissions than only with roles. I like that solution with "HasPermisson" attribute. It's so elegant. .NET has evolved in a very powerfull platform.
Hi, Milan! Thanks for the video series, they help me to progress on my pet project I've got a question: Why are AuthorizationHandlers defined in the Infrastructure project, and not in the Application project for instance? What's the logic behind that?
Great series so far. I did run into a issue, if entering a userId that not valid, I kept getting a 404 error, even though I was thinking I would get a 401 error. Is there a way to trigger a 401 if the parsedId is not valid?
Hey Milan Thks for this awesome content !! I've my web api project with 41 controllers and several api methods (haven't count em all yet), and I'm defining permissions for each endpoint using a smart enum 'Permission', but it's begining to look quite long class, any suggestion?
you don't need to worry about the cached permission values if you invalidate the cache with the permissions key when you save changes in the permissions repo.
Hi Milan, Great channel you have! One thing i miss in this authorization serie, HasPermission, where does that come from? I saw the HasPermissionAttribute, in the controller you write [HasPermission(...)] and i don´t see where that is?
Hi Milan, just followed your video series through and your explanation of the various classes required to implement a full featured custom authorization are superb and have saved me endless hours I'm sure! I wanted my permission model to be split into two elements e.g. Users with Author, Contributer and Viewer levels. To achieve this I have just concatenated the Permission and Level enums to create my policy name within the HasPermissionAttribute and then I deconstruct them in the policy provider to allow the correct creation of my permission requirement, do you think this sensible?
Very cool video, the only question why do you add IPermissionService as singleton? why don't you add as scoped? is it compile time error, logic error or something else?
Hi Milan, thank you for the amazing tutorial! Very well explained, easy to digest and understand. I have a small suggestion though. If you want to avoid creating the policy every single time, perhaps you should add it to the AuthorizationOptions. You can do this by creating a new property: private readonly AuthorizationOptions options; Assign value to this.options in the constructor: this.options = options.Value; And modify the return a bit in that way: policy = new AuthorizationPolicyBuilder(Auth0BearerTokenConstants.AuthenticationScheme) .AddRequirements(new PermissionRequirement(policyName)) .Build(); // Add policy to the AuthorizationOptions, so we don't have to re-create it each time. this.options.AddPolicy(policyName, policy); return policy; Let me know what is your opinion about it :) Thanks again for the astonishing tutorial! Kinds regards/Martin
What about restrictions on owned content? Meaning, you can only successfully fetch your own member data. I am assuming ReadMember will allow me to fetch any members data. Using a guid and the only security measure is not sufficient.
I don't know if it is the way I have set it up, but I was getting an error when implementing the `GetPermissionAsync` method as defined here. What worked for me was to change the `.Select(x => x.Roles)` on the member context to `.SelectMany(x => x.Roles)` and then on the roles variable, remove the first `.SelectMany(x => x)`.
i face the error in GetPermissionsAsync too, while accessing GetMemberById endpoint. The error is from _dbContext.Set().Include(x->x.Roles).ThenInclude(x->x.Permissions)..... Error was 'Invalid column names RolesId)
Hi Milan, why did you decide to register the authorization handler as singleton? I was browsing through the aspnetcore source code and as far as I can tell, they register it as transient. By the way, thank you for your videos!
Thank you for the helpful video! But the HandleRequirementAsync() is not compiled in my case I had to put return Task.CompletedTask; How did it work in your implementation?
Hi Milan. Thank you for the video - very informative. I have a question regarding returning a custom error message from the AuthorizationHandler. I would like to inform the user of which particular permission is missing. It seems you can write a custom error message via httpContext.Response.Body.WriteAsync( bytes, 0, bytes.Length ); However, this seems a bit low level (writing a string to a byte[]), which makes be think that this may not be the conventional way of doing things. Do you have any opinons or alternative approaches for doing this?
@@MilanJovanovicTech The scenario is users can upload an Excel file. This can potentially contain anything as the content. On the Api side we parse the file to see what 'kind' it is based on the column names. Once we know what kind of Excel file it is we want to check to see if the user has permission to upload this kind of Excel file. Ideally, we want to return 403 and also a error message to the client similar to "You do not have permission to upload {excel file kind}"
Hi Milan, I implemented permission based authorization in .NET 5 with the support of your video. But I got stuck at some point. Here is my problem. - When I'm trying to access logged in user's userId (by context.User.Claims) from the context in PermissionAuthorizationHandler class it's always null. I almost wasted 5-6 days to figure this out. But I couldn't able to find where is the issue. Do you have any idea about this type of issue?
Hi Milan, thanks for this awesome series! However i face the error in GetPermissionsAsync too, while accessing GetMemberById endpoint. The error is from _dbContext.Set().Include(x->x.Roles).ThenInclude(x->x.Permissions)..... Error was 'Invalid column names RolesId)
I can register member and login but it seems like the MemberRole table didn't get populated with the userdata who has registered. Might this be the problem?
Wow, what an awesome series Milan and I really like your presentation style too, I am a fan of permission based auth and this is something I will definitely use in my future projects
Great video, could you help me I'm stuck with Unable to resolve service for type 'System.String' while attempting to activate 'Examination.Infrastructure.Authentication.PermissionRequirement'.
@@MilanJovanovicTech it's was extra step i made it which inject PermissionRequirment, now im in another problem which is Unable to create a 'DbContext' of type ''. The exception 'The skip navigation 'User.Roles' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.' i think because the joining table not configured in correct way which is (userRole) entity if you configured in another video or some think help me please give me the link, thanks again
In my PermissionAuthorizationHandler, the `context.User.Claims` is always empty. I know I have missed something obvious, but I have been though the video several times and I cannot figure it out. Any idea what my issue may be?
@@manofqwerty Hey, have you been able to figure that out? I have run into the same issue. I suppose it is because of a problem of decoding JWT. Because of it, even the Authorize attribute does not work as well.
@@MilanJovanovicTech Milan, Thank you for your videos and samples; I learned a lot from them. I also thought about resource-based authorization. In the retail network, for example, a salesperson can only access their own sales, a shop manager can view their shop's sales data, and a regional headmaster can get sales within their region, among other restrictions. Given its domain-specific nature and potential for advanced logic, I am considering implementing it using MediatR pipeline behaviors. Do you have any other suggestions for enhancing this?
@@MilanJovanovicTech , Even though IServiceScopeFactory is a Singleton Service it will still behave like a Scoped service, Since you have registered IPermissionService as Scoped in your DI container..Please let me know if I am missing anything here...
I am still trying to understand all of it. How it would be possible to check different conditions on different roles/permissions? Let's say, there are 3 types of users. 1. Super Admin 2. Manager 3. Staff Super Admin - this user should ve able to add/edit/delete users of rest 2 types but can not perform action on other super admins. Manager has some staff users under them. So manager kind of user should be able to add/edit/delete staff users who are associated to the logged in manager but can not add/edit/delete other managers staff users. And manager even can not alter any super admin user. Staff user can just login and do some other crud operations. How such requirements/Authorization can be implemented. Can you please explain in your next videos?
You're mixing apples and oranges. Permissions are simply about "which action can I _perform_ in the system". There's nothing here that controls *what data you are allowed to see*. It's a separate requirement, and it needs a custom implementation of its own.
Hello, First of all, I love your content! However, there is something I don't understand in the Presentation layer. There is a reference to the Infrastructure layer, whereas I thought we were only supposed to point to the Application layer
Hi bro I have followed every step. but I am getting these 2 errors. Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Authorization.IAuthorizeData Lifetime: Scoped ImplementationType: Gatherly.Infrastructure.Authentication.HasPermissionAttribute': Unable to resolve service for type 'Gatherly.Domain.Enums.Permission' while attempting to activate 'Gatherly.Infrastructure.Authentication.HasPermissionAttribute'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Authorization.IAuthorizationRequirement Lifetime: Scoped ImplementationType: Gatherly.Infrastructure.Authentication.PermissionRequirement': Unable to resolve service for type 'System.String' while attempting to activate 'Gatherly.Infrastructure.Authentication.PermissionRequirement'.)
Hi Milan, very nice video series, thank you so much for your content. I´ve implemented your feature with string parameter for permission, my permission are like Entity.Read, Entity.Update, etc... My solution it´s working perfect with defined entity controller like this: [HasPermission($"{nameof(Boxer)}.Read")] public async Task GetBoxers() But I´m having error calling "HasPermission" with a generic entity controller like this: [HasPermission($"{typeof(TBaseEntity).Name}.Read")] public virtual async Task GetBaseGenerics() Compiler Error CS0182 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type can you think of any solution to solve this? thanks in advance
Milan everything is good but you are speaking in a very flat tone means you need to highlight and pause at main points otherwise its hard to figure out whats the critical portion or real logic to remember, skip saying very common things like i am giving it as argument blah blah.. very much we can see it anyway, I request you first elaborate on drawing board whats the overall design and flow, I could not sync with your talk and video at the same time..
If you had a dollar for every time I say _permission_ in this video, how rich would you be?
Get the source code for this video for FREE → the-dotnet-weekly.ck.page/permissions3
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
I think authorization would be more lucrative haha
@@Dustyy01 You sure? Did you count? 😂
@@MilanJovanovicTech I counted
permission/permissions: 98
authorization(not authorize): 43
You should reupload the video and say 2x permission at the end😂
@@sineme Those are rookie numbers 🤣
Milan, you are one of the best content creator that I'm following from hundreds of others.... Loved this one.... Keep them coming....✌️
Thank you, this means so much to me, more than you can imagine 😊
Thanks you, Milan! Every new video I learn new and new and new!!! I really appreciate what you are doing!
Happy to hear that!
Great video, Milan I looked for video like this in past 6 months... Thank you
Glad you found it in the end 😅
The thing I was talking about before in other videos in this series was a third way to solve the problem you mentioned here 4:30 . (for authorization without calling the database and dealing with caches or storing all claims in jwt token) which is using enum flags. this is a much faster and more flexible way to deal with this problem. and we can use a single value in the database or jwt tokens, instead of list of permissions.
Agreed, but you are limited to 32/64 values with flags.
On a project I'm working on, we have ~300 unique permissions 😅
@@MilanJovanovicTech I had the same problem with jason taylor suggestion before that's why I've created that library (infiniteEnumflags)
also I added an example of how it works to the repo. you can check it out.
@@MilanJovanovicTech by example, I mean a flexible authorization implementation example by the library.
@@alirezanet I'll check it out. I was also preferring the approach using enum flags but ran into the limitations mentioned.
@@jwcodendaal 👍 let me know if you faced any problem
Every time I come to watch your video for a specific topic, I learn tons of other topics too.
Can you please make some videos about unit test and integration tests which are on a real project. Because I watched lots of videos about it, but still, I am stuck.
Thanks for that and stay awesome. 😊😊
I already have a few videos on those topics:
- th-cam.com/video/tj5ZCtvgXKY/w-d-xo.html
- th-cam.com/video/a6Qab5l-VLo/w-d-xo.html
- th-cam.com/video/Pk2d-qm5KwE/w-d-xo.html
Excellent topic.
Thank you very much Milan.
My pleasure!
Great post Milan. In the past when I first time implemented permissions based RBAC I spent so much time to get content that gathered here in several videos.
One thing I noticed that might be improved in your example. In AuthorizationPolicyProvider you getting policy and if it's not null you creating it. To avoid creation of the policies all the time I'd suggest to take IOptions options from constructor to local variable and then slightly modify GetPolicyAsync to:
if (policy == null)
{
if (Enum.TryParse(policyName, out var permissionId))
{
policy = new AuthorizationPolicyBuilder()
.AddRequirements(new PermissionRequirement(permissionId))
.Build();
// Add policy to the AuthorizationOptions, so we don't have to re-create it each time
options.Value.AddPolicy(policyName, policy);
}
That's a great suggestion!
This is so helpful!!!
Thank you Milan
Glad it was helpful!
I just wanted to thank you for everything that you are doing, your content is awesome and it helped me a lot. I think you deserve way more subscribers than you have :)
p.s. good luck, loocking forward to video course series
Thank you so much! I'm glad this series has been so helpful for many people. The subscribers will come, but the quality is what's important.
@9:17 when filtering for the Member, would you not use "FirstOrDefault()" instead of "Where"? "Where" will return an IEnumerable containing one element but it will have had to check the whole table; whereas "FirstOrDefault" will exit out once it finds the Member you're looking for. However, you'd also need to deal with a potential null value if no Member with that ID exists. Maybe I'm missing something as to why "Where" was used here so thought I should ask/point it out.
It's going to be an index seek at the database level regardless. And I'm only returning that Member's roles in the end. The main point is there won't be any table scans involved.
Way to gooo! Keep them coming. Part 4? :)
Part 4 on Tuesday, and that's a wrap for now 😁
@@MilanJovanovicTech suppose we request it to have more on this series! And vote in numbers
Yes yes yes yes yes.
Thank you Milan
I'm glad you liked! 😁
awesome Milan , thank you off lot, may God blessyou
You're welcome!
In the next video u will show how to put the permissions into the claimsidentity. The thing with JWTs is that once they're created u cannot update them.
How would u handle permission updates in this case?
That's the tradeoff you're making to gain performance. There isn't a clear way how to revoke a JWT once it's issues. One thing you can do is give it a short TTL.
@@MilanJovanovicTech so would rather go for reference tokens or just use permission validation on each call?
@@Dustyy01 I'd ask myself two things:
- How often do user's permissions actually change?
- Can I suffer the performance hit of checking permissions in DB?
If permissions don't change frequently, just put them in JWT and be done with it. Give the token a 60min lifetime, and just refresh it from the client when it expires.
If I want to always fetch from DB to have the latest state, but I don't want to take a performance hit, I can introduce caching. And I just need to deal with cache invalidation when permissions change.
Both solutions are viable, and simple to implement. I use fetching permissions from DB + caching with IMemoryCache on a current project, and it works like a charm.
@@MilanJovanovicTech that's actually a pretty smart solution, thank you so much for this idea!
Thanks for the assist. Much needed help.
Always happy to help!
I'm sorry, I asked the question wrong. By following your videos I was able to implement the HasPermission method and initialize the permissions and related entities using IEntityTypeConfiguration and builder.HasData.
The problem is in the API tests using xUnit, I use the CustomWebApplicationFactory class that inherits from WebApplicationFactory to initialize my databases but I'm not able to insert the related entities. Could you explain how to seed the Permissions, Roles and RolePermissions in the CustomWebApplicationFactory?
Run the migrations on startup
Great video Milan, thank you very much for this
My pleasure!
Thanks for sharing this video!
You bet!
Thanks for the great video Milan! I've got my version working, the only thing is that on the line 'AuthorizationPolicy? policy = await base.GetPolicyAsync(policyName);' the policy is always null, do you know why this is?
It should be null the first time, but not afterwards
@@MilanJovanovicTech I have it null every time I try it
@@Gamedocable Could you solve it?
Finally! Great, thank you! 😊
Did it meet expectations?
@@MilanJovanovicTech Of course. Now I will try to play with it in a test solution. I normally rather like to work with Permissions than only with roles. I like that solution with "HasPermisson" attribute. It's so elegant. .NET has evolved in a very powerfull platform.
Hi, Milan! Thanks for the video series, they help me to progress on my pet project
I've got a question: Why are AuthorizationHandlers defined in the Infrastructure project, and not in the Application project for instance? What's the logic behind that?
Because I see Auth as an Infrastructure concern, and not an Application (business) level concern
Great series so far. I did run into a issue, if entering a userId that not valid, I kept getting a 404 error, even though I was thinking I would get a 401 error. Is there a way to trigger a 401 if the parsedId is not valid?
We'd need to alter the code that's return a not found when the user doesn't exist - on the other hand, maybe this is the correct behavior
Hey Milan Thks for this awesome content !!
I've my web api project with 41 controllers and several api methods (haven't count em all yet), and I'm defining permissions for each endpoint using a smart enum 'Permission', but it's begining to look quite long class, any suggestion?
Define less granular permissions
Does one of your videos cover the creation of the individual database entity configuration classes shown in this video?
That would be Part 2
you don't need to worry about the cached permission values if you invalidate the cache with the permissions key when you save changes in the permissions repo.
Absolutely
Hi Milan, Great channel you have! One thing i miss in this authorization serie, HasPermission, where does that come from? I saw the HasPermissionAttribute, in the controller you write [HasPermission(...)] and i don´t see where that is?
In C#, you can omit the "Attribute" from HasPermissionAttribute and just write HasPermission and it will behave the same
Hi Milan, just followed your video series through and your explanation of the various classes required to implement a full featured custom authorization are superb and have saved me endless hours I'm sure! I wanted my permission model to be split into two elements e.g. Users with Author, Contributer and Viewer levels. To achieve this I have just concatenated the Permission and Level enums to create my policy name within the HasPermissionAttribute and then I deconstruct them in the policy provider to allow the correct creation of my permission requirement, do you think this sensible?
I think that makes sense, yes
Very cool video, the only question why do you add IPermissionService as singleton? why don't you add as scoped? is it compile time error, logic error or something else?
Isn't it scoped? 🤔
@@MilanJovanovicTech Sorry, I mean IAuthorizationHandler where you use IPermissionService. Just got mixed up with names.
@@georgeritchie4497 Ah, in that case it can be any lifetime. I just prefer singleton for stateless classes.
Hi Milan, thank you for the amazing tutorial! Very well explained, easy to digest and understand. I have a small suggestion though. If you want to avoid creating the policy every single time, perhaps you should add it to the AuthorizationOptions. You can do this by creating a new property:
private readonly AuthorizationOptions options;
Assign value to this.options in the constructor:
this.options = options.Value;
And modify the return a bit in that way:
policy = new AuthorizationPolicyBuilder(Auth0BearerTokenConstants.AuthenticationScheme)
.AddRequirements(new PermissionRequirement(policyName))
.Build();
// Add policy to the AuthorizationOptions, so we don't have to re-create it each time.
this.options.AddPolicy(policyName, policy);
return policy;
Let me know what is your opinion about it :)
Thanks again for the astonishing tutorial!
Kinds regards/Martin
But the policy changes dynamically per Permission, and it's only created once and then reused. I don't think it's a significant cost.
Hey Milan, great series of videos. Do you have any links that would be helpfull for unit testing the auth handler?
I suggest to test that using Functional Tests
What about restrictions on owned content? Meaning, you can only successfully fetch your own member data. I am assuming ReadMember will allow me to fetch any members data. Using a guid and the only security measure is not sufficient.
Right. That's resource-based authorization and I didn't tackle that
I don't know if it is the way I have set it up, but I was getting an error when implementing the `GetPermissionAsync` method as defined here. What worked for me was to change the `.Select(x => x.Roles)` on the member context to `.SelectMany(x => x.Roles)` and then on the roles variable, remove the first `.SelectMany(x => x)`.
Maybe you had a slightly different entity configuration? EF Core version? Could be a number of things
@@MilanJovanovicTech yeah, I think this is likely the case, but thought I'd comment it here just incase someone else has the same issue 👍
i face the error in GetPermissionsAsync too, while accessing GetMemberById endpoint. The error is from _dbContext.Set().Include(x->x.Roles).ThenInclude(x->x.Permissions)..... Error was 'Invalid column names RolesId)
do you think you can make a series on GraphQL APIs using hotchocolate where we still apply CA and DDD
I want to explore GraphQL for sure, but it'll have to wait a bit, I need to do my research 😁
Thank you, explained it beautifully. Have you combined this with Identity?
Not Identity server, but with other IDPs yes
Hi Milan, why did you decide to register the authorization handler as singleton? I was browsing through the aspnetcore source code and as far as I can tell, they register it as transient. By the way, thank you for your videos!
Really? My understanding was that it should be a Singleton. Either one is fine.
What tool do you use for should the values of variable when you are stepping through the code
ReSharper
Thank you for the helpful video!
But the HandleRequirementAsync() is not compiled in my case
I had to put
return Task.CompletedTask;
How did it work in your implementation?
Make it async, await something inside
@@MilanJovanovicTech Ah. Now I see differences. Thanks a lot!
Thank you for your videos. Good luck!
Hi Milan. Thank you for the video - very informative.
I have a question regarding returning a custom error message from the AuthorizationHandler. I would like to inform the user of which particular permission is missing. It seems you can write a custom error message via httpContext.Response.Body.WriteAsync( bytes, 0, bytes.Length );
However, this seems a bit low level (writing a string to a byte[]), which makes be think that this may not be the conventional way of doing things. Do you have any opinons or alternative approaches for doing this?
I wouldn't advise exposing permission information to consumers of your API. A 403 response is good enough.
Why would they even need to know?
@@MilanJovanovicTech The scenario is users can upload an Excel file. This can potentially contain anything as the content. On the Api side we parse the file to see what 'kind' it is based on the column names. Once we know what kind of Excel file it is we want to check to see if the user has permission to upload this kind of Excel file. Ideally, we want to return 403 and also a error message to the client similar to "You do not have permission to upload {excel file kind}"
Hi Milan,
I implemented permission based authorization in .NET 5 with the support of your video. But I got stuck at some point. Here is my problem.
- When I'm trying to access logged in user's userId (by context.User.Claims) from the context in PermissionAuthorizationHandler class it's always null. I almost wasted 5-6 days to figure this out. But I couldn't able to find where is the issue. Do you have any idea about this type of issue?
Check the pinned comment perhaps. Maybe it's not wired up correctly.
Can you please send document/blog link where we can check code.
Thanks!
It's on Patreon
👋
Great video.
Glad you enjoyed it
Hi Milan, thanks for this awesome series! However i face the error in GetPermissionsAsync too, while accessing GetMemberById endpoint. The error is from _dbContext.Set().Include(x->x.Roles).ThenInclude(x->x.Permissions)..... Error was 'Invalid column names RolesId)
Did you run the DB migration? 🤔
yes I did, switch to Persistence project and ran "Update-Database" and from Management Studio I can see all the migrations history
I can register member and login but it seems like the MemberRole table didn't get populated with the userdata who has registered. Might this be the problem?
@@MilanJovanovicTech what could be the problem here?
what about users of same role has deffernt permissions, because know u get the permission of the role insted of permission of spicific user
The relation is between role and permissions, not users and permissions.
We'd need a different data model for what you're describing.
Thanks Milan for sharing this... is there a link for github repo of this code?
U could check his GitHub for expensely server, I think he did the same there
Most likely, good idea
I share the code on my Patreon, but I also have examples on GitHub
Wow, what an awesome series Milan and I really like your presentation style too, I am a fan of permission based auth and this is something I will definitely use in my future projects
@@jamesevans6438 Thanks James :)
I've used this on a couple projects now and it worked out great.
Great video, could you help me I'm stuck with
Unable to resolve service for type 'System.String' while attempting to activate 'Examination.Infrastructure.Authentication.PermissionRequirement'.
You probably missed a step?
@@MilanJovanovicTech it's was extra step i made it which inject PermissionRequirment, now im in another problem which is Unable to create a 'DbContext' of type ''. The exception 'The skip navigation 'User.Roles' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.' i think because the joining table not configured in correct way which is (userRole) entity if you configured in another video or some think help me please give me the link, thanks again
In my PermissionAuthorizationHandler, the `context.User.Claims` is always empty. I know I have missed something obvious, but I have been though the video several times and I cannot figure it out. Any idea what my issue may be?
When creating the JWT does it end up having the claims?
You can inspect it on jwt.io
@@MilanJovanovicTech yeah, the JWT has the subscriber and email claims associated with it.
@@manofqwerty Then it could be something around configuring Auth with the framework
@@MilanJovanovicTech yeah, that is what I was thinking. I will have another look tomorrow. Thank you.
@@manofqwerty Hey, have you been able to figure that out? I have run into the same issue. I suppose it is because of a problem of decoding JWT. Because of it, even the Authorize attribute does not work as well.
Good job! Will you look into resource-based authorisation?
Yes, that's still an outstanding topic
@@MilanJovanovicTech Milan, Thank you for your videos and samples; I learned a lot from them. I also thought about resource-based authorization. In the retail network, for example, a salesperson can only access their own sales, a shop manager can view their shop's sales data, and a regional headmaster can get sales within their region, among other restrictions. Given its domain-specific nature and potential for advanced logic, I am considering implementing it using MediatR pipeline behaviors. Do you have any other suggestions for enhancing this?
Hi Milan, Why do we need IServiceScopeFactory? Why can't we directly inject IPermissionService through constructor injection??
It's a Singleton service, and IPermissionService is scoped
@@MilanJovanovicTech , Even though IServiceScopeFactory is a Singleton Service it will still behave like a Scoped service, Since you have registered IPermissionService as Scoped in your DI container..Please let me know if I am missing anything here...
I am still trying to understand all of it.
How it would be possible to check different conditions on different roles/permissions?
Let's say, there are 3 types of users.
1. Super Admin
2. Manager
3. Staff
Super Admin - this user should ve able to add/edit/delete users of rest 2 types but can not perform action on other super admins.
Manager has some staff users under them.
So manager kind of user should be able to add/edit/delete staff users who are associated to the logged in manager but can not add/edit/delete other managers staff users.
And manager even can not alter any super admin user.
Staff user can just login and do some other crud operations.
How such requirements/Authorization can be implemented. Can you please explain in your next videos?
You're mixing apples and oranges. Permissions are simply about "which action can I _perform_ in the system".
There's nothing here that controls *what data you are allowed to see*. It's a separate requirement, and it needs a custom implementation of its own.
@@MilanJovanovicTech hmm, so do you have separate videos on this as well which shows how to implement it in manageable way or in your style?
@@microtech2448 Didn't get that far yet, unfortunately. But it's coming at some point
@@MilanJovanovicTech would love to see that series. Thank you
great Milan
Thank you!
Finally
What do you mean finally? 😅 This video is out for a year...
I am getting this error when trying the GetMemberByID, not sure what I am doing wrong
401
Undocumented
Error: response status is 401
Me neither 😅 There's no way to tell from this
@@MilanJovanovicTech 🤣 thanks
The source code you shared here does not contain your full implementation of permission based
It should be the implementation for this video only. The one in Part 4 should be the complete version.
@@MilanJovanovicTech got the code. Thank you so much
Hello,
First of all, I love your content!
However, there is something I don't understand in the Presentation layer. There is a reference to the Infrastructure layer, whereas I thought we were only supposed to point to the Application layer
Layers on the same level such as Presentation/Infra can reference each other. How else would you set up DI?
Hi bro
I have followed every step. but I am getting these 2 errors.
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Authorization.IAuthorizeData Lifetime: Scoped ImplementationType: Gatherly.Infrastructure.Authentication.HasPermissionAttribute': Unable to resolve service for type 'Gatherly.Domain.Enums.Permission' while attempting to activate 'Gatherly.Infrastructure.Authentication.HasPermissionAttribute'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Authorization.IAuthorizationRequirement Lifetime: Scoped ImplementationType: Gatherly.Infrastructure.Authentication.PermissionRequirement': Unable to resolve service for type 'System.String' while attempting to activate 'Gatherly.Infrastructure.Authentication.PermissionRequirement'.)
There is no way you're getting that error if you followed Part 1-3 exactly. Looks like it's something related to registering the custom auth handler.
Same Issue 100% if you solved Tell me , thanks
"You know the drill"
I laugh at my self sometimes, but I'm having fun 😅
Hi Milan, very nice video series, thank you so much for your content.
I´ve implemented your feature with string parameter for permission, my permission are like Entity.Read, Entity.Update, etc...
My solution it´s working perfect with defined entity controller like this:
[HasPermission($"{nameof(Boxer)}.Read")]
public async Task GetBoxers()
But I´m having error calling "HasPermission" with a generic entity controller like this:
[HasPermission($"{typeof(TBaseEntity).Name}.Read")]
public virtual async Task GetBaseGenerics()
Compiler Error CS0182
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
can you think of any solution to solve this?
thanks in advance
It wont work. typeof is evaluted at compile time. And you specify your generic at runtime.
I understand, many thanks@@MilanJovanovicTech
I wish the source code was provided.
It's available on Patreon
😍😍😍😍
😁👋
Milan everything is good but you are speaking in a very flat tone means you need to highlight and pause at main points otherwise its hard to figure out whats the critical portion or real logic to remember, skip saying very common things like i am giving it as argument blah blah.. very much we can see it anyway, I request you first elaborate on drawing board whats the overall design and flow, I could not sync with your talk and video at the same time..
Things like that take time, don't think creating content is easy