Category
📚
LearningTranscript
00:00Alright guys, welcome back. So in this lesson, our focal point is to make sure that the token
00:05that gets generated makes sense and then we'll pass it back with our auth response. Alright,
00:10so I'm just going to write everything inside of this method. This can kind of blow the
00:15method but of course, if you want to see how to fully architect and implement separation
00:20of concerns and solid principles in a minimal API, for more enterprise of a project, you
00:26can always check out my course that is dedicated to that. For this course over, we're keeping
00:31it as simple as possible just to get the API up and running to support our .NET MAUI app.
00:37So the first thing that I want is a key and I need to generate the security key based
00:43on the same code that we used for this issue or signing key up top, right? So to generate
00:51that I'm going to say var, let me just say keys equal to that line of code. And then
00:57I want to say var credentials will be equal to a new signing credentials. There we go.
01:06I will give it the key. And then we specify that we're signing it using the security algorithms
01:14.HMAC256 algorithm. Alright, so now that we have the key and the credentials, we need
01:22to start filling in the blanks or start collecting enough information about the user so that
01:28when we're generating the token, we have everything ready. So the first thing that I want would
01:32be the roles. What roles will or does this user have because it is very possible for
01:39a user to have multiple roles in a system. Identity uses a many to many relationship
01:46between the users and the roles, right? So it is possible that when you try to get the
01:50roles of the user, you're getting multiple. So I want all of them. So get me all the roles.
01:56I can just say user manager dot get roles async. And then it's going to ask me for the
02:02user. Guess what? I have the user because I already retrieved and validated the user.
02:07So get me said user's roles. Now that we have the roles, that's actually coming through
02:13as a list of roles, right? And I'm also going to try to get what we call the claims. So
02:22var user claims or just claims. So claims, like I said, are bits of information about
02:30the user, right? Everything is going to end up being a claim. So the role is going to
02:35be a claim and the claims are already in the data in the system as a claim. But these
02:40are roles and claims that might have been stored about the user when the user was created.
02:46So after we have collected all of that information, now I need to compile a list that comprises
02:54claims based on the roles, claims based on what was in the database and additional claims
03:00that I know I need for the token. So I'm just going to say var token claims would now
03:07be equal to a new list of guess what? There's a data that data type called claim, right?
03:15And if you don't have it, you control dots and add the missing using statement. And then
03:21inside of this list, we're going to be adding some new claims. So new claim. And a claim
03:29is really just like a key value period. So a claim has a key and then the value. So in
03:34this case, we can say sub. If you go back to JWT.io, you would notice that there were
03:39certain in the decoded example, you would have seen IAT and you would have seen sub
03:47and you'd have seen JTI. Those are all different claims that are there, right? So new claim.
03:56And then I'm going to add this constant JWT registered claim names. So if you control
04:02that, you'll see here that you have two using statements. The one that I want us to use
04:06would be Microsoft dot identity model dot JSON web tokens. So that is the using statement
04:12that we want. And this is going to give us a constant sub. So for claims, some of them
04:19are simple, some of them are complicated bits of string, you don't need to commit them to
04:23memory because these constant classes exist and they are standard across all systems.
04:30So like the example or the description rather said, JSON web tokens are an open standard.
04:37So that means if you're building a system for Java dot net, Maui, Ionic, Flutter, JWT
04:44is a standard. So every language will see sub and know what sub means because it understands
04:50that it's something having to do with the JWT. So it's easier to just use the constants
04:55than to have the magic strings. So here I'm going to say the sub or subject is what sub
05:01means. This typically would be like the email address of the user. It could even be the
05:06ID of the user. It depends on you. So I'm going to say ID, right? Another one that I'm
05:12going to add, and I'll just do control D and duplicate the lines, JTI. So JTI here
05:20would act more like, what did they call those again, more like a nonce. So I guess that
05:27doesn't help much. But a nonce is like a unique code that should be different each time. So
05:32that helps to replace to reduce or prevent replay attacks, meaning somebody who got a
05:38token that expired three weeks ago, trying to use it, they can't, but then they have
05:44to, they have much more to regenerate than just the date and time of the, of the expiry,
05:52because this code needs to be different every single time, right? So that does help in that
05:58regard. So we'll just use a GUID. Every time it gets generated, we get a new GUID and convert
06:02it to string. And that is a value for our JTI claim. Another claim that is useful is email.
06:09So of course we can add email address. Remember, these are bits of information about the user.
06:15And then if you just use this constant class and scroll, you'll see that there are a lot
06:20of claim types that you can add. So even if you needed to add light there, the name of
06:26the person, right? Whatever data you have on the person that once again is not too intrusive,
06:32not too sensitive, then you can add inside of the JWT because the idea is that the client
06:41shouldn't need to reach out to the server every single time it needs to know a little
06:45thing like an email address or a user ID. So you want to give it enough information
06:50in the token that it can be kind of autonomous after getting the token. But once again, I
06:56recommend leaving the sensitive data out of it. So there is a situation as well where
07:03sometimes you want a custom claim, right? So sometimes you can actually put in your
07:10own custom claim here, right? Because it's really just a string. It's just a key value,
07:17string type and string name. So you put in the name of the type you want, and then you
07:22can put in user dots. I don't know what you would want to put there. True. Email confirmed
07:31true, right? So this isn't a standard claim. Email confirmed is not a standard claim. I
07:36can easily put that there as a type email confirmed just to show you that you can actually
07:42add your own claims to the whole list. Another thing to notice that claims always have to
07:48be string. So yes, this is a string, but this is a Boolean. So I would actually have
07:54to say to string for that error to go away. All right. So those are the things to pay
07:59attention to. So now that we have put in our standard claims that every JWT should have
08:05and every system knows it should look for at minimum, we put in our own custom one,
08:11what happened to the roles and the claims from the database, right? So for roles, what
08:18I can do here is say dot union. I'm going to use up some link here. So I'm going to
08:26say dot union. And well, I can just add on the list of claims, but this is already a
08:31list of type claim, right? See list of claim. This is a list of claims. I can just union
08:38say, we'll just join on to that existing list. And I can also say dot union here, but
08:45this one, we're going to have to get a bit more creative where I'm going to have to say
08:49from the list of roles. So roles is just a list of type string, but I need to turn it
08:54into a key value period. So from the list of roles, I want to select, and then I'm going
09:04to select into new objects of type claim. Right, let me see if my link is still strong.
09:15And for each claim object, I'm going to put in the name. I'm sure I have JWT that registered
09:23claim role. No, that one isn't there. There is another constant class called claim types.
09:32Right? Okay, claim types that role. So claim types may have just as many or even more as
09:40the JWT registered claim names, right? But the one I'm after is role. Now, if you look
09:46at the value of role, you'll see why I don't just write the word role. Right? So when I
09:52said that it's a standard, and you don't want to necessarily commit all of them to memory,
09:56you'll look at it and you'll see that that claim string that key is very complicated.
10:01So we can just use the constants to our advantage. So I'm selecting a new claim type for each
10:07role that is there, create a new claim type with that key and the value coming from the
10:13list which our token here is Q. Let me just rename it to role so it reads better. Alright,
10:23so select all for each role that you select, get me a new claim type that is of type role
10:29with the value that is the role. All right. And right there, we have compiled our list
10:35of token claims. The work isn't done yet. But we're almost there. So the final leg of
10:41this is not actually generate our token. So I can say new token is equal to JWT security
10:51token. And this is well, this class has a constructor that takes quite a few things,
11:01right? So the first one would be who is the issuer. So we're already established where
11:06the issue is coming from our issuer. And I'm just going to go back up here and borrow as a matter
11:12of fact, let me just collapse all the code. So that it's a bit easier to scroll. So our
11:20issuer is our issuer code here from our from our configuration. So that's the issuer, right?
11:29What else do you need, we need the audience, right? So to set the audience, we do the same
11:34thing with from the configuration. And we need the claims. So we need to give it the
11:42list of claims, which we just compiled, right? What else we need to specify when it expires.
11:51So expires here would be duration. And I'm forgetting what I put there as the key. So
11:58that's duration in minutes, right? And it's a good thing I named it so cleanly because
12:04expires needs a date time, right? So I can just give it five, five is the duration in
12:10minutes. What I'm going to say though, is date time, dot UTC. No. And the reason I'm
12:17using UTC now is that that's more global than just the time. No, but the time now is relative
12:22to your machine. And the client machine might be on a different date time using UTC. Universally,
12:28however, is well, it is universal. So it's the standard time, no matter where you are,
12:33this is the time we're adhering to. So UTC now dot add minutes because I did say duration
12:39in minutes, right? And then I need to add a number. The config value is in a string
12:46format. So I need to convert dots to int 32, and then put in the configuration value. And
12:57that's the minutes, that's a value that I'm adding. So once again, get the universal standard
13:02time, add five minutes to that. And that is our expiry time from when it is issued. All right.
13:10And then the final thing would be our signing credentials. So our signing credentials would be
13:18the credentials that we generated up top here. And that generates our token. Now, when we have
13:26done all of this, it is still in the form of JWT security token, which we know we need it to be in
13:34the form of a string. So then to get it is as a string. So let me call this security token,
13:42right, then to get it as a string token, which I'm just going to call, let's say access token,
13:48I'm going to say JWT security token handler, which is a built in class for us once again,
13:56that has a method. And this I can say, is equal to a new JWT security token handler object,
14:05which can then say write token, get my spelling right, there we go. And then we pass in the
14:13security token that we want to write. So all of this converts whatever object was generated here
14:20into a simple string. And then access token is what we will pass back in our auth response.
14:29So that's quite a journey, right? Now, I'm getting a few errors here. And I believe it's because
14:37I have a conflict between the library that I used for something. So it's saying ambiguous reference
14:46between the system that identity model tokens, and the one that I might have chosen to use.
14:54So I think I should be using system identity model tokens, instead of the one that I chose.
15:02So let me clean that up a bit. Let me take out this one. Alright, and there we go. So now there's
15:10no ambiguity knows that it's using system dot identity model dot tokens dot JWT. That's what
15:16we're using for that class type. And we have to use that one because we use it for other things
15:23throughout. Alright. So let us review what we've done. So to generate the access token,
15:29we have to go and get the JWT key, go ahead and generate that security key and then sign it using
15:36our SHA-256 algorithm. Then we went and got all the roles and all the claims that the user may or
15:44may not have in the database. Then we created a combined list of known claims, potential custom
15:51claims, and all the claims from the database and all the roles in the form of claims. So now we
15:59have one big list of claims that are all bits of information about the user that has just
16:05authenticated or just been verified rather. Right? Well, yes, authenticated. Then we go on to
16:12generate the security key. So the security key requires us to provide the issuer, the audience,
16:18pretty much all the bits of information that we said in the token validation parameters are needed.
16:25So because we said we needed issuer, audience, lifetime, all of those things, we have to make
16:31sure that we include them in the generation. We add our claims as compiled up top. We set the
16:38expiry date to be five minutes or whatever time, you know, time span you have stipulated in your
16:45system. And then we set our signing credentials. At the end of all of that, we get a string
16:51representation of this token key. And then we return that in our auth response. So with all of
16:58that, let us go and test and see what we get back when we try to log in. All right, so let us attempt
17:04the login. And I put in the credentials already. When we click execute, look at what we get back
17:10for our token value. Now, if I take this and jump over to JWT.io, just to see what we get.
17:18If I paste that inside of the encoded section,
17:23it's going to show me all of the payload data, right? So sub, that's the user ID, JTI, that is our
17:31JWT token, pretty much, sorry, not JWT token, our nonce, rather. That's our nonce value. So unique
17:39identifier for this token. We have the email claim, right? We have this custom claim that we added,
17:48see, true. And then look at this for the role, we have administrator. So we looked in the database
17:56and got the user's role. We have the administrator, we have the expired time. And you see,
18:02it's in a numeric form. But if you hover over it, it will tell you the exact time that it is set to
18:09expire. We have the issuer, and we have the audience. All right. So all of those things
18:18combine to give us our JWT, or JSON Web Token, or access token, whichever word you use. So now that
18:28we have all of that working with our JWT being issued upon login, let's jump back over to our
18:34.NET MAUI app, and then add in the logic to make the call to the login API endpoint,
18:42and then receive the token, analyze the token as needed, and handle that whole login flow on the