Get the source code for this video for FREE → the-dotnet-weekly.ck.page/jwt-auth Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt If you run into any issues while trying to implement this, I made two mistakes in the video: - At 16:00 it should be one call to `UseAuthentication` and one call to `UseAuthorization` - `JwtBearerOptionsSetup` should implement `IConfigureNamedOptions`
Milan! suppose we have this api having authentication & authorization as you did returning token on success login. now i want to consume this token in my front-end application developed using net core, which will use this token to authenticate and authorize user. question is how should we will implement setting for front-end application so it get logged/signin on token and authorize user accordingly and auto add token in api call header.
Nice tutorial, especially the solution approach with options is quite pro. For those who try to use the example code with NET Core 7.0 will not have their JwtBearerOptions initialized thus token validation will fail. To solve the issue JwtBearerOptionsSetup must derive from IConfigureNamedOptions. JwtBearerOptionsSetup : IConfigureNamedOptions public void Configure(JwtBearerOptions options) { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = _options.Issuer, ... }; } public void Configure(string? name, JwtBearerOptions options) => Configure(options);
@tosunabi1664 thank you very much. I'm wondering I did the same in this video, I know it isn't wrong but it can't validate token. When I used IConfigureNameOptions and it worked
What a tutorial !!!! Lacking words to describe this ,everything is professionally done !! I like the way you handle errors ,I liked the way you use DI and they way you did set up the JwtBearerOptions instance ,thanks buddy .
Thanks, Milan, This video helped me to integrate the JWT-based authentication in Clean Architecture. I searched a lot of articles and videos but this one is great 🚀
I never leave comments on videos, but this one absolutely deserves it. Great tutorial, not the average ‘just get it done’ attitude. So much effort into doing it cleanly and actually teaching proper techniques. Thank you. I’m interested in learning more on the command implementation done at 0:50 and your overall project structure and approach. Are the any further videos on this project?
I talk about CQRS often: - ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-vdi-p9StmG0.html - ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-85YbMEb1qkQ.html You can search for "cqrs" on my channel, a bunch of videos will show up. Also many videos around project/solution structure.
This is great for anyone want to go into identity It will be very good if you continue it as a serie with openid dict 3 and extend it to oauth 2 and openid connect
Kindly smashed the ring bell button too. What about token expiration? Should we use refresh token endpoint or there is sliding expiration if we use this method?
We can (and should) also configure that through app settings also I would leave the refreshing to the frontend. The easiest is they can get a new token. Adding refresh token support requires more work.
@@MilanJovanovicTech Sorry to jump in :) requiring a new token in a real application would require us to send again username\password (so we would need to store them somewhere in the client application..not ideal)..better to use the refresh token flow
For people starting to move into this space, this is an excellent introduction to aspnet security. Well done Milan! With JWT bearer tokens, a word of caution - please consider your bearer token storage particularly in your browser based client side applications. Current recommendations is to move away from clients having any access to the JWT bearer tokens and instead use cookie based HTTPOnly cookies through patterns like BackEnd For FrontEnd (BFF). Again I want to take nothing away from this amazing video!
@@MilanJovanovicTech In the limited amount of space we have in a chat to discuss a very deep problem sure! Firstly, completely agree that many applications are storing JWTs client side. I have built several systems in that way :D The generalized problem that client side storage introduces is how to protect the JWT which is a plain text password with a short lifespan. The general two options used to date are Local Storage and Cookie storage. In both approaches, malicious JavaScript can extract the JWT bearer token from the user's session storage and use it to attack the protected resource. The problem gets worse when the refresh token is also sent to the client side to support silent refresh which I still commonly see. This is even worse because it removes the short lifespan assurances provided by the JWT. As such, the current OWSAP recommendation is to never provide the client with the bearer or refresh token but instead use HTTPOnly cookies. This particular characteristic of the cookie means that JavaScript cannot access the cookie (with the exception of a browser level security vulnerability)
The OWASP link is of particular importance because it talks through the specifics of proper session cookie storage and the additional requirements that go along with that. These are characteristics implemented in BFF frameworks such as Identity Server's implementation.
@@VastSolar while jwts are text and are completely modifiable, they also have server side validation to prevent tampering using standard public key infrastructure signing techniques. This means a valid jwt can only be created by a system with access to the private key. Encryption of jwt data is also possible.
Unless I missed you haven't showed on the video when you rigstered JwtProvider. Great video, I've done it myself as Infrastructure service without Mediator. I guess another small / quick video interesting to many people might be registering via Facebook or / and Gmail.
@@MilanJovanovicTech Forgot about that . but for people that came here for the 1st time looking for JWT implementation this comment might be useful ;-)
I came looking for copper but found gold. Amazing content, that helps a lot. I also have a question. What is in your opinion best way to tackle authentication in microservices environment and/or what are the best sources to look for?
Great explanation for the generation of the token, kinda lost in the DDD because it was never my cup of tea but thanks in adance for the great tutorial.
Great video! Thank you so much! This would have been even better if you had ditched Clean Architecture in favor of simple architecture; dare I say Minimal APIs? Clean Arch forces you to create way too many files and abstractions to do a trivial task and I've got PTSD from it.
Yet another great video, really breaks down simply what the JWT token is and all the options. Random question, how does your IDE show the values of your variables when debugging within the text editor? Seems very convenient compared to the separate windows.
Fantastic video! I'd love to see the using of the JWT expanded a little. For example yes they are a member and have successfully authenticated through the login endpoint. But they do not have permission to access GetMember endpoint based on claims (think it would be here that this differentiation would be set?). Would [Authorize] be un changed and the blocking of endpoint usage be within the endpoint method perhaps.. Anyway once again fantastic video had me captivated.
Hi Milan, following your technique to setup JwtBearerOptions through ConfigureOptions gives me a www-authenticate: Bearer error="invalid_token",error_description="The signature key was not found" error. I still need to add the configuration as a AddJwtBearer method parameter in order to get it working. It's a .NET 7 web api project.
Milan, I don't know why, but for some reason the JwtBearerOptionsSetup class Configure method was never called for me, if I followed your tutorial. The class had been instantiated, the constructor was called, but the not the Configure method. After I changed the interface from IConfigureOptions to IConfigureNamedOptions, then the Configure method called succesfully and works without any issue. :)
Oh, I have been trying to figure out for hours. I could configure authentication directly from startup but Milan's approach was not working. Now IPostConfigureOptions has worked, Thanks you both for this comments. Also Milan it would be nice to share your code from github or another source control website, it would be better for us to directly look into working version of it. Anyways thx for your awesome tutorials
@@MilanJovanovicTech Because the application layer cannot depend on the infrastructure, so calling the interface directly from the infrastructure would break the rule, right?
Hello! I encountered your videos on permission authorization and tried to return to the start of this 'series' link by link. This one seems to be the starting point, but, for example, throughout the video you use the Abstractions namespace from the solution. Am I right to assume that, for example, ICommand interface is your own written abstraction? Or is that some common logic that I'm just not familiar with? Being a little confused trying to follow along.
02:07 - You're using ICommandHandler interface from the Application.Abstractions.Messaging namespace. Can you please provide us with the implementation of the interface?
Thank you very much for that! Can you say when should we use symmetric and when asymmetric keys to create JWTs? Currently we use symmetric, the server creates the JWT and sends it encrypted to the user. When the user sends it back, the server decrypts and checks it. The symmetric key is securely stored on the server. Do asymmetric keys have any advantages here?
Ideally, you won't implement any of this on your own. There are excellent third-party authentication services that implement this much more securely than what I showed in the video.
Do you have a video when you started this project from beginning ? I found you have already written a lot of codes. I am a newbie, and I found you have great contents.
I just looked at Jwt authentication and was wondering: I wanted to use this for authentication API users where I supply an 'apikey' in the form of a Jwt. But then I want users to be able to use this token a long time (it will be configured in some backend services), but still have the ability to revoke it when needed. Am I correct in thinking the only way a Jwt can be revoked is by changing the Secret that is used to validate tokens, or I should store the token in the Api to have a revocationlist of tokens that I don't want to be used anymore. Maybe this is not what Jwt was intended for but with an API service you would like to give users access, be able to change what access they have (which endpoints they can call) and also either extend or revoke their access. This is not something a Jwt does out of the box unless you program around this. I tend to see a Jwt as something you can use to give shortlived access (hence the expiration) and during that time give access to a fixed set of resources, the claims (roles/policies) in the Jwt. It's handy that any endpoint can validate the token and give access to resources based on the claims inside this token without the need to go to some backend database. In my case we have an API that has several endpoints, let's say Company, Contact, Order and some users might not have access to Order so the Claims could quikly block users there. But then a user might have access to Company A, and **not** Company B, thats access information that cannot be stored in the Jwt and has to live elsewhere...right? But then when I have to store data elsewhere anyway, why not store as much of it elsewhere instead of in the Jwt...**I'm thinking of the Expire date here ** why store that in the token when I could also put in in the database, and even have the ability to extend it without having to give the user a new token. @Milan Do you have some place to discuss this with others outside of the RU-vid comments?
Thank you for video. I have a question, why you decide to make custom authentication (and authorization in future) instead of using something as Identity Server for example?
@@DENDYTWOO No reason to be, I never discriminate on knowledge. 😁 IDP = IDentity Provider What would that be? - Identity Server - Active Directory - Auth0 - KeyCloak - Firebase Authentication
@@MilanJovanovicTech I really appreciate you answering my question. I do not know what I should answer your question about IDP, but now I know what it means. You are smart and kind, thank you for the answer and for your videos
Is this a video in a series? I think I'm missing some basic setup to get this working. I'm trying to learn this, have an understanding of database migrations, controllers and REST api's but I don't understand how the interfaces you implement can execute the code of the class
@@MilanJovanovicTech i tried it out but it does not seem to work, after some debugging it looks like the JwtBearerOptionsSetup class does not run at all, seems that if i extend IConfigureNamedOptions instead of IConfigureOptions it works.
@@pseudocoffee4829 , Thanks for posting this. I was seeing a similar problem and hardcoded the config as a work around. I kept both interfaces and simply called the original Configure(options) from Configure(name, options).
Hey Milan! Nice solution! Is this approach good for production? Are the signing credentials secure enough? I've heard that a signing certificate needs to be present, however, I don't know how this is implemented. Thanks!
@@MilanJovanovicTech I'm asking for my bachelor thesis. My current implementation uses Auth0, but I want to build my own solution some day, just for the sake of it and understand the whole process. I'm still a junior dev and many of the solutions like Auth0 abstract much of the process and it itches me from the inside to learn it all haha.
Hi Milan. Nice video. I just like to ask if you have another one with the best practice to refresh the token before the original one expires. Regards from the land of Maradona and Messi
Milan, I get this exception whe try to create SymmetricSecurityKey: IDX10720: Unable to create KeyedHashAlgorithm for algorithm 'HS256', the key size must be greater than: '256' bits, key has '128' bits. (Parameter 'keyBytes')) I believe the problem is somewhere in UTF8. Could you advise how to fix this?:
Awesome video! Correct me if I'm wrong, wouldn't it be a good idea to create an extension method for the Services? ex: builder.Services.ConfigureJwtSetup(), then the said ConfigureOptions will just be invoked there (well if those two are tightly coupled).
@@MilanJovanovicTech Thank, you, i found my problems, issue with postman, after resolving my problems with postman, now works perfectly. Thank you for this content and for your answer.
hi, If authorization fails and you get this error : bearer error="invalid_token", error_description="the signature key was not found"; In JwtBearerOptionSetup class implement IPostConfigureOptions
Hi Milan, what about if i take this token and called specefic function with parameter not allowed to me ? how i can prevent it to happen specially from postman interface. Thanks
@@MilanJovanovicTech if you take the jwt token and make request from postman if will be accepted even if this api is acepefic for 1 user, what shall i can do to prevent this to happen? i hope you can get what i means
in another way lets supposed there is api with email id to get all email info. anyone can use this api through postman with valid jwt token with random email id to get email info without he has the access to this email itself.
@@ramysamir1987 You need to add the concept of resource authorization to that. Based on the JWT you can know WHO the user is, and determine if they can access that specific email
What are your thoughts of storing refresh and access tokens in localstorage? I have implemented refresh token rotation, and I do store the tokens in localstorage. So if the request is 401 etc I send to token/refresh and get new token pair and continue with the original request. But I am reading a lot of mixed information, some say its considerably more safe to store it in httponly cookies. I would appreciate your input on the implementation. Its a system where users login with a personal id. So its a bit sensitive, maybe httponly cookies is better then?
@@MilanJovanovicTech Okay, thank you :D I will go for the implementation I have right now but will consider switching before deploying. Appreciate the answer :)
@@sonnyskold5634 Hi! Since the refresh request will inherently always need the refresh token (and the refresh token will need to be sent to the UI in the first place), there really isn't a way to keep it safe from bad actors; as is generally the case with anything on the frontend. If you want it safe, keep it server side. The safety in these tokens comes from the hashing. It's actually pretty neat if you look up how it works, basically all the parts of everything that's encoded get meshed together and most stay on the server so the client knows very little about from what the token is generated. In terms of how to handle it, it depends on the concerns of the application. You'll often see websites have a "User inactive, you will be signed out in 'x' seconds, click to refresh" popup. I'm not certain but I assume that's just doing the refresh haha. Regardless if you're doing automatic token refresh, you should really do something to have either your users opt in or let them know that's what is going on for ethical considerations. You do want to make sure you're rotating your refresh tokens. You don't want any to be long-lasting. That said, what you listed works fine haha. I've done that in plenty of personal projects. For something more professional you should really do the pop-up.
@16:10 - you added app.UseAuthentication(); twice even though use said one should be app.UseAuthorization(); (lines 99 and 101) With that mistake, why did it work anyway?
Hello, I believe I have followed this tutorial perfectly a number of times and have created a solution just for testing it, though I am always hitting the same issue. When testing in Postman, I am getting the following error in the `WWW-Authenticate` header: `Bearer error="invalid_token", error_description="The signature key was not found"` - do you have any ideas why this may be happening?
Yes, I do believe I mistakenly configured JwtBearerOptionsSetup to implement IConfigurOptions, and it should be IConfigureNamedOptions or IPostConfigureOptions. Also, at around line 105 in Program.cs, I have UseAuthentication two times. Where one of those should be UseAuthorization
Hi, great video. I would just like to have one question. Why do you separate the login request and command? why doesn't the controller just take a command as a parameter?
Hi Milan, great video. I am having a small issue implementing this though. I did not see in your video the adding of the service IJwtProvider to the program.cs. When I add these steps to my application I get a service issue on the JwtProvider. Any quick suggestions would be appreciated.
Don't worry there were 2 issues. 1 was the IConfigureNamedOptions Implementation on the JwtBearerOptions and the service issue i resoled by just making the jwt token generation happen inside the authorisation controller and removed the JwtTokenProvider Class. All works now so thanks for the video. If you have a quick idea as to why the generate method works in the JwtTokenProvider class for you and not in my case it would be great to learn why.
@@MilanJovanovicTech Yes thank that is how I figured out an error I was having with failed JWT signature. I did try the scoped service but was getting a life cycle determination error. Token Generation is cruelty working as part of the Auth Api so will leave it there for now. Thankyou
@@MilanJovanovicTech Thanks. By the way, your source code is slightly different than the codes in the video. Did you make some improvements to it? For example, JwtBearerOptionsSetup is implemented IPostConfigureOptions instead of IConfigureOptions in the source code.
That's a tough one, the short answer is no. But you can somehow store the token and check it on every authentication request, which is stupid and degrades performance. One option is to give tokens a short lifetime. Another option is to code your client side code to request a new token when a user does an action like that.
Using the options pattern within your startup configuration is an overuse of the pattern. This will get refactored into an extensions method where you simply pass configuration into a static method where the JwtBearer options can be configured. Also the OptionsSetup folder is not very useful or extensible for scaling the app. Instead I would add a Middleware folder in the Api project (Gatherly.App) then add folders as you create more startup extensions (Auth, HealthChecks, OpenApi, Telemetry, Validation, Etc.)
FYI if you use implicit operators on your Result class to allow being constructed by an Error, you can make you code a lot cleaner by removing the need for Result.Failure(DomainErrors.Thing.Blah) and instead just return the DomainErrors.Thing.Blah