ASP.NET Core Custom OAuth Server (.NET 7 Minimal Apis C#)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what do we have over here building an authorization server that would be the natural progression after using GitHub for external authentication which is an oauth server and then delegating authorization from YouTube which is also an oauth server and in this video we're going to build an authorization server reason being is generally if you want to start doing external authentication or dealing out tokens people will jump to Identity server open ID connect you don't need all of that complexity you can implement it yourself with relatively minimal effort which is what I'm going to show you in this video we'll also be using the auth specification as a reference so in case you will get lost on your own you will see me use it and hopefully you will know how to use it as well my name is Anton welcome to the raw coding YouTube channel if you're enjoying this video don't forget to leave a like subscribe click on the notification Bell and I'm also adding more merchandise to my store go ahead and check out a link in the description if you want to be the first one to know about things that I'm doing go join the Discord server link is in the description as well let's go ahead and get started so we have a roughly intimidating looking setup over here but we have a client that is going to be authenticating with our authentication server which is an oauth server the client is basically a copy of one of our previous projects where we have the cookie authentication and we want to externally authenticate where their oauth server hence we're pointing to the authorization and the token endpoint we still have like a custom callback over here we will need to do a little bit of additional work we are using pkc which is what we're going to be covering today so if you don't know what pkce or how to implement it you're in luck we then hit the login endpoint on here and initiate our external authentication and that's all there is to this client over here it runs on Port 5004 auth server runs on Port 5005. the auth server setup is a little bit more complicated but nothing that you don't know already we have cookie authentication with a login path so when we hit this authorization endpoint because we need to be authenticated we're gonna go to this login page over here the login page is pretty simple we have our form without any user or password field so we're emitting all user management we're focusing on off I'm retaining the return URL and that's what we're going to be passing further into the login endpoint where we're just signing in and redirecting to the return URL so that's the login and the get login endpoints we then have the authorize and the token endpoints which is up to us to implement so they are both empty to quickly reiterate over the oauth flow first of all we're going to be on the client we're going to click login when we're going to land on authorize we're not going to be logged in we're going to go to the login page we will then submit our form we will process the login we'll get authenticated we'll get redirected back to the authorize endpoint where we'll create an authorization encode redirect back to the client and then through the back Channel obtain a token by posting a form to the Token endpoint now a quick note on the dev key service that I have over here it is basically a process of generating a key and then saving it in a file and then reloading it from that file if you've watched my JWT video you understand what I'm doing here if not then go ahead and watch it so as The Story Goes we want to be implementing the authorization endpoint first and writing code will be the easy part the hard part will be what code to write and for this we're going to refer to the auth specification the first bits are really the introductions and the first section as well where all of the terminology to you as explained I'm not gonna go over it I'm gonna mention it if you really want to understand it open this document up and read it it's not the easiest read but once you get into your head it's pretty good the second section is client registration if you've watched my previous video where we have to register a client with GitHub or YouTube with fill out a form this is that information you get given a client identifier a client secret you specify where do you want to be redirected to Etc Section 3 describes the information about which endpoints we want to implement which we already basically have the groundwork for the authorization and the token endpoint not much information about the authorization endpoint but it has some information about the token endpoint which we're gonna come back to later think of section 3.2 as the base class and then we have the actual description of flows how do you obtain a token this is where supported flows are described we are interested in the authorization code Grant flow where an access token is obtained through a back channel on the server side whereas section 3.2 is the base class to the Token endpoint the token endpoint extension sections are implementations where there is basically a composition between these two sections around what parts you want to implement let's jump straight into the authorization code we're not going to go over the flow because I think I've already killed it to death here we have the authorization request what kind of information do you need to supply we have the response type which describes what response are you gonna get we are asking for an authorization code flow because there is an authorization endpoint for many flows and they will give you back different things this is the way that you specify what result you're gonna get based on which flow you're essentially picking what flow you're choosing based on your response type you have then additional information of which client is asking for this you then have code Challenge and code challenge method this is part of the pkce specification which should be described in section 7.6 or at least reference to this pkc RFC which in section 4 and 4.2 describes how it's constructed so we can reference this later when we're actually working with these two parameters we then have redirect your iscope and state all of this information is coming from the URL and if I find it right over here right this is an example of a request to the authorized endpoint and here we also have some information about how the code challenge is constructed and then we have the code verifier so if you're not familiar with pkc the client before going over to the oauth server will generate a code verifier so the client is holding a code verifier goes through this process of constructing a code challenge sends that over to the authorization endpoint with the code challenge method which is this sha-256 encryption on the authorization endpoint we have to somehow persist this information because on the token endpoint as we'll find out later the code verifier is going to get given to us and we will have to take the information that we received on the authorization endpoint match it with the information we're receiving on the token endpoint and mash it together regenerate the values and this gives us proof that where the request originated from is where the token is being obtained is the same place so first of all let's go ahead and get these parameters into our code for this I'm going to use the HTTP request and on the request we will have the query where I can try to get these values we end up with something like this essentially parameters to our functions this is how these parameters are going to be supplied and then we want to generate an authorization response it has to consist out of the code the state which is being passed to us over here and then the issuer who is issuing this token all we have to do is redirect back to the client on the Callback path the Callback path is going to be found in the redirect URI and we have to attach these parameters as a query string okay not too complicated instead of returning OK we will just redirect to where to the redirect URI where we'll have the code and then the other parameters as well so the final result that we'll try the return will look like this the main thing that we're trying to generate is the code so what is the code the code is described as an opaque value which basically it's just something that shouldn't mean anything to anybody but the thing that is generating this value it shouldn't last for a long time and then it shouldn't be used more than once if it is being used more than once revoke all tokens that have been generated by this code so we will need to persist this token in some kind of Blacklist if it's been used and if it's being used again delete all the tokens and again you can revoke and delete tokens by using this Blacklist database of tokens and then that the authorization code is bound to the client identifier code Challenge and redirect URI if we scroll down a little bit there is a bit here about authorization and then there is a bit about the code Challenge and code challenge method the they need to be linked to the authorization code and this is because we're going to need that information at the token endpoint because we will need to validate that that request is coming from the correct place we will need to also validate this authorization code the way that we're going to do it is we're not going to be storing it in the database because that is long however that would be the correct implementation we're going to be using encryption and the data protection API so I data protection provider let's get data protection provider over here a little bit closer to where we're generating the result because this is where your validation should occur we want to create a protector for all auth this will be our protector here we will protect a payload we want to protect an authorization code this is where we can create a new model class of code and inside of here we will start putting information that should be associated with the authorization code and there we have it client ID redirect URI code challenge code challenge method and the expiry when we're creating our code new off code this is where we populate these parameters so here I have a code that expires in five minutes all of the information that I store in there I will now serialize this code and then I'm going to encrypt it using the data protection API this is going to be a code string which I can then pass in the URL and this is pretty much the backbone of the endpoint which should work the rest is Edge case handling what if this parameter is not supplied what if the correct scope is not being requested the rest is validation let's fire up our applications auth server.net watch no hot reload same thing on the client side we're gonna open up our client app and close the server first of all let's double check I don't have any cookies I do have one so I'll remove that going over to the network Tab and please please please if there is anything you take away if you're working with authentication of open ID connect the network Tab and the browser is your best friend this is how you understand what the heck is going on we're currently on the client we want to log in we type in login we are now on the submission form what the heck just happened and please do notice I have preserve log on first of all we went to the login endpoint we went there with a get request and we got a 302 and 302 just means that we got redirected to the location in the response headers we are going to get a location header that is where we're going we're going to the authentication server auth server to the auth endpoint that we have specified with all the parameters client ID the Scopes that we're requesting which are none the response type which is code and the redirect URI which is pretty much just the custom callback with the code challenge generated because we have specified that we want to be using pkce because of 2.1 requires pkc all of this information is here now because we're not authorized we got redirected over there right so we're over here trying to get this route we're not authorized where do we go we go to the login endpoint we go to the login endpoint because this endpoint requires authorization and we specify that if you're not authenticated with the cookie authentication you go to the login endpoint that is where we end up we are currently on the login endpoint so we got that okay here if I quickly inspect the form I am basically an URL encoding all of the information for the return URL over here I will submit and now we get an error it doesn't mean that anything bad has happened over here we have posted our form we've submitted our login information we found the user passwords good all good you go back to where you came from you go to the authorization endpoint with all of that good client ID scope response type information that you originally created so this whole logging in story is just a Side Story it's not the main story right we we are just logging in just so we can reach this authorization endpoint so we finally reach that authorization endpoint and it's successful we get redirected back to the client to the Callback where we're now trying to process this code that we have generated right look at that code it's beautiful we also have the state and our issuer over here we land on the Callback and the Callback errors because why wouldn't it error we don't have a token endpoint so this is the next part this is where we have to implement the token endpoint and before we know what we need to implement this is where we go over here we will take a look at 3.2 first which is the base implementation and then 4.1.3 which is kind of like what's added on to the base so what does the base implementation of the token endpoint say it says that you're going to receive a client ID you're going to receive a scope which is optional which is basically again for you to validate or for you to put things into the token to say which areas of your API the user has access to right again scope is about saying what resources a resource owner can access what information can a user access and here we have the grant type Grant type similar to the response type is a way to basically select a strategy or the token endpoint many flows you may get an authorization code you may get something else on the token endpoint you may be processing authorization code you may be processing something else how you want to start you specify the response type on the token endpoint what are you processing your processing a grant type an authorization code that you specify in this parameter these three are our base parameters and they get posted to us as warm URL encoded so one thing if you know about minimal apis is minimal apis only allow posting Json out of the box over in a bit of a pickle but nothing that we can't handle so let's go over to our token endpoint and again start with the HTTP request on the request we're gonna go straight to the body reader get all of the bytes and utf-8 decode them and now we essentially have this payload over here because it's essentially a query string in the body I could use regex there might be a class that I'm not aware of that I can just put this into and it's gonna automatically decipher the whole thing I'm not aware of that other class and I'm assuming most of you don't know regex regex is what I would rely on in this situation I'm gonna do the let's say sillier thing and we end up with this let's call it parsing process we split by the Ampersand we then split by the equals we get the key value and then based on the key we assign the value to the correct place a little bit monotonous but it will work again because this is the parsing process this is where we would do validation of these values and this is essentially the base layer to accepting a token request you then have access token Scopes so again you're loading permissions into the token and then you have the response this is what you have to return you have to return an access token you have to return what kind of type is it when does it expire the Scopes that have been requested and then a refresh token if it has been given this is where coming back to the application we have already imported Microsoft identity model Json web tokens package if you've seen my JWT video You're Gonna know what this package is it allows us to work with tokens and this is where the dev Keys come in this is where we're able to generate tokens okay so let's bring up our Dev Keys Dev keys on result okay we will return a new result we will specify an access token and to create an access token we will need a Handler new Json web token Handler the Handler will need to create a token and in here we provide a security token descriptor here we can specify a bunch of claims I'm giving it what is essentially a subject ID user ID and then a custom claim here I set an expiry time the expires in is recommended so if I would be supplying an expires in those would be time span from minutes and then we also need the token type so token underscore type this will be a error token okay B this can also be specified on here error scope and refresh tokens I will omit if you're wondering about refresh tokens I will have a video on it later finally to sign the tokens we want assigning credentials which are going to be new signing credentials and this is where from Dev Keys we want to get the RSA security key security algorithms I want RSA shot 256. okay and that is what you do to generate the token now this was the base thing for accepting and returning there is a bit over here where the initial parameters only specify these three however I've also included the code verifier because this is where it is contained on the example response and usually the extensions will show you that you need it so coming back to the table of contents we go to the implementation to the extension of the base implementation here the extension is over here right so token endpoint extension let's see what do we want here code will be required we already have the code we then have the the redirect URI that is required that is over there as well and then code verifier so this is where we have the code verifier I'm already getting it this is again where they re-specify the example request and this section over here goes over what validation you need to perform but mainly what we are going to focus on are these two sections verify that the code verifier parameter is present and then verify the code verifier in order to do this verification we're gonna need to come back to where it is explaining what the code challenge is which is the thing that we have originally stored in to the code string right so here we have the auth code so let's go ahead get this code over here go back to the Token endpoint place it over here and we're not going to be recreating the code but rather using the data protection API to get all that information right here is the code we we want to unpack it to get that code method so I data protection provider data protection provider will create a protector and here we want to unprotect where unprotecting the code that will be the code string and we want to use the Json serializer to deserialize code string to the authorization code now before we go too far because this is essentially an authorization process and the backbone for the token endpoint has essentially been built I just want to make sure that all the stuff that we're passing through here actually works the debugger is attached what I'm going to do is I'm just gonna refresh the Callback because there is no validation around any of the expiry time if the code has been used more than once Etc however there seems to be some correlation checking so basically cookies and stuff like that let's just re-log in here we are hitting the token endpoint it went by pretty quickly but here you can see we went to the login endpoint we went to the authorized endpoint and now we're coming back to the Callback on here let's take a look at the auth code and if that's been basically deserialized everything is fine so there is all of our information and again code Challenge and code challenge method the client is generating the oauth Handler is generating this information and actually we can go ahead and take this detour and see where it's happening in the program CS over here if we opening up the ad off go to the add alt Handler and when we've taken a look at this before exchange code async this is where the exchange is happening if context properties and you may think it's getting stuff from Context properties then we put it in the options well if you watched my previous video you understand that the state parameter is used for persisting these properties and and any other state information across that Journey to the other side this is where it extracts the code verifier attaches it to these token request parameters and this is where it's submitted through the back channel to our off server and this will be an interesting place to just have a break point as well let's come back to the Token endpoint let's take a look at some of the other stuff that we have Grant type is authorization code the code is the thing we already decoded redirect URI is the Callback that we're currently on and the code verifier is this secret value that is a little bit different from the code challenge that we're meant to reach and again let's just play this through or actually just stop it and we're just stopping the debugger the way that we generate a code challenge so again the code challenge is on the auth code code challenge over here the way that we generated is we need to take the code verifier we need to shot 256 it and then base 64 and we're using ASCII encoding and that is how we're gonna reach the code challenge so again let's create a function for it right we are good programmers private will validate code verifier we're gonna have the auth code and we're gonna have a string for code verifier here's our beautiful function I'm going to use a shot 256 create to create an instance or a hashing so shot 256 and we can stick this in a using this bit takes care of this shot 256 and again shout out to five six is a variable this will vary depending on the code method or code challenge method that is being supplied here and I'm only mentioning it because these are all the possibilities in the chance that you need to comply with many many different specifications you will need to say that okay these other services can parameterize my request or you can say we're only supporting shot 256 and if you supply an incorrect code challenge method you can go you know f yourself otherwise if you're building this for yourself most of the stuff you can expect it to be very linearal you don't need to cover an infinite amount of possibilities so covering this shot 256 we have the code verifier and the way that we generate a hash shot 256 we will have a compute hash function which can get a byte array buffer so we need to go to encoding ASCII characters and get bytes of the code verifier this is going to give us some kind of value that we need to base64 URL in code now there is a base64 URL encoder and this is coming from the package that we have imported over here I think there is another package as well base64 URL text encoder which is part of the asp.net core framework but yeah we want one of the base64 URL encoders because it's going to vary in the characters that get put into the final result so we want to encode whatever this thing that we're going to compute over here this is going to give us a code challenge that has been regenerated camel case and finally we can return code code challenge equals equals this code challenge place a breakpoint over here so we can take a look at it and because the class is static we want this function stack as well and right over here if not validate code verifier off code code verifier right we can return the results not empty but rather request and there it is so the application should reload and let's reattach the debugger and there we go now on the client side let's remove everything let's re-log in and or we log in I'm just going to clear are everything on here again again we're now on the token endpoint we are inside the validate code verifier and here what we're going to see is the code challenge looks like fbx GN and this looks like fbxgn so we have successfully taken our code verifier through the same encryption process and arrived at the same value so whoever gave us this code challenge that is in our authentication code in the beginning is the same thing that is giving us this code verifier now this is why we can say okay this is good to go we can deal out our authentication token here let's remove this breakpoint over here as well play and I don't have a break point on the other side so we're not hitting this breakpoint over here and I don't even know who will need it but coming back to the browser no claims are displayed although we should be authenticated if I take a look in the application we do have a cookie over here and the reason we don't have any claims or no claims are displayed on program CS this is where I've placed it to do we still need to go through the process of mapping claims at this point we only have an access token we're creating an identity because there is nothing that can enforce a standard of claims across all processes or all services that could possibly exist it's a main manual process okay if we know anything about access tokens and I'm going to place a breakpoint over here so we can actually inspect the access token that we're creating well split it by the dot and that will give us the base64 payload here because we're in the client we don't have that package here again try search for base64 URL text encoder here we can decode this payload this will just be a payload Json we can now extract user or payload information so payload Json document out of this payload Json or sorry rather this will have to get parsed parse this finally can go into run claim actions payload and this will contain a root element run claim actions on this context will execute whatever mappings we have supplied over here so currently I am mapping a sub claim to a subclaim so if on the token endpoint I'm supplying a custom claim with a value of Foo I will say that this custom claim I want to assign to custom 33 or something like that so this claim type should now be in my user context and I should be able to see it let's make sure that both services are restarted and running I'll attach the debugger to the client I'll close the other debugger and here we can relog in on the login page let's submit here we have the response in the body there you should see the access token and the response is 200 because we have an access token jump through to to over here where the access token is a little bit more accessible I'm going to take a look at the context and grab the access token we can go to jwtio paste this in here and we're going to see this middle bit is the bit that we're getting so when we're splitting by dot we're getting this middle payload bit and this is the Json object that is contained in there and get them plagued by the scrolling wheel not being inside of here but the sub and custom claim is what we're after coming back to the application I'm gonna play this through come back over here to the website and we should see our custom claim be mapped to custom33 and the subclaim also be mapped to the subclaim at this point we have successfully covered the Green Path the backbone of the oauth flow we have an oauth 2.1 compliant authorization server or we're on the way there because you need a bunch of validation what if you supplying correct parameters parameters are missing you need to handle all of those edge cases how do you do it well the beautiful thing about specifications is that everything is already described here if we go back to the table of contents and we go to the authorization request that is where we're making the request and we scroll down there should be an error section so four one two one error response basically if anything goes wrong this is the response that you need to return let's do a simple one where we will say an invalid request missing a required parameter coming back to the code going to the authorization endpoint closing everything else and this window so if a response type is not present it's instantly a bad request so we specify the error to be an invalid request because the request is missing required parameters and colludes and envelope parameter value so that's the error code that you put in the error property error description is optional error URI is optional state is required if it was present so let's go ahead and actually just whack this at the top I'll pass this on and then we can also add the IAS value over here okay SS falling incorrectly and placing this over here as well so this would be a invalid request now making sure that the application has restarted over here coming back over here going to that auth server to off authorize endpoint and saying that the response type is whatever doesn't make sense so first of all we need to authenticate let's close this off submit and actually messed it up with because the there is no redirect URI it looks like it actually skipped it because I actually have this parameter okay so first of all let's amend this a little bit let's just make sure that we Supply no parameters okay so no parameters we get our invalid request and now it's a proper error response this is the way that you would handle all of these again this code is pretty basic there is no reusability here this is pretty brute and can be implemented a lot more elegantly however you should get the idea about how to do your validation where to find the codes and what scenarios to cover it because if you read this documentation from start to finish you should understand what is required and what isn't no what is casket can yet if you enjoyed this video thank you very much for watching please don't forget to subscribe and click the notification Bell if you did if you have any questions leave them in the comment section if you want to say thank you leave it in the comment section as well but better yet come support me on my patreon get the source code very big thank you to all of my patrons that are already supporting me your help is very much appreciated best of luck to you and have a good day
Info
Channel: Raw Coding
Views: 26,114
Rating: undefined out of 5
Keywords: asp.net core, oauth, oauth2, asp.net core oauth server, authentication server, jwt, c#, authorization endpoin, token endpoint, oauth2.1, pkce, .net 7, minimal api
Id: EBVKlm0wyTE
Channel Id: undefined
Length: 33min 24sec (2004 seconds)
Published: Tue Nov 15 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.