ASP.NET Core External Authentication (OAuth, .NET 7 Minimal Apis C#)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome everybody in this video we're going to be learning about external authentication and asp.net core as you're going to find out from this video it is all about off so if you haven't watched my video on off and auth 2.1 go ahead and watch it video is in the description we're gonna go through an example of setting up external authentication of GitHub and as a consequence of looking through all of the externals of the authentication Handler and if you don't know anything about authentication handlers go to the first episode in the playlist My Name Is Anton welcome to the raw coding YouTube channel let's go ahead and get started here's our empty sample project as I mentioned in my oauth presentation we first need to configure the provider which is going to be giving us authentication right where some entity somewhere over there and then the providers over here they want to be giving us this authentication session so we need to go over to them and they need to know who we are in GitHub this is how you do it you go to to settings and maybe I'll make this a little bit bigger but then you find developer settings and auth apps register a new application so you go through this registration process we then have a home page URL let's say that we'll choose localhost that's going to reside at something like 5005 okay we will copy this and for now I'm just going to put it in the authorization callback URL as well we're going to see what this is in just a second and let's register our application here we have the client ID and I'm not going to blur anything out because I'm just gonna delete this all anyway we'll copy the client ID we'll come back to our application and now to the Builder let's go to services and let's add authentication so we're adding the base authentication services and then we're going to add all this is going to be a GitHub authentication schema and we're going to be providing some options on these options we will be able to set the client ID okay if you've ever attempted to set up any other external authentication like Facebook and stuff like that the only things that you will need to provide is the client ID and client secret for now we're just obtaining a client ID and client secret so we generate a new client secret there we go well let's go ahead grab that and there we have it so we as a client we have our credentials now we have to go over to GitHub how does auth know to go to GitHub where on GitHub do we go to answer these questions you will have to find some kind of link to documentation for example like the oauth documentation over here and again let me Zoom this in and this information is pretty apparent that you need this information I explicitly point out that there is going to be two endpoints as part of auth that you need the authorization endpoint where you obtain your code and then the token endpoint when you exchange the code for the token the first endpoint is the authorize endpoint let's go ahead and grab it come back to the application and let's look for this configuration here we have the authorization endpoint cool let's place this over here so this is the first step out of three steps so the first step is we go to GitHub to the other authorization endpoint to grab an authentication code and again if you don't understand this terminology go watch the presentation here we will need to supply all the parameters which the middleware will take care of because remember auth is a specification all we have to do is follow the steps defined in the specification which is what the middleware is going to do and it's going to attach these automatically we then want the users to get redirected back to our site so they go to GitHub and then they need to come back where do they come back this is the Callback path configuration right so this is where GitHub is going to send them back and wherever it is that they get back they need to process the code and exchange it with the token endpoint let's grab the token endpoint we're gonna put it over here so token endpoint equals to over here the thing that I just briefly raved about the Callback path we're going to omit it from the settings so we can see the errors and that is just going to be a path for me to explore through the internals okay again all the parameters will be attached automatically we're gonna get back a token and finally again this is going to be something I'm going to talk about how we exchange the token or the information which we're then going to inject into the claims principle the user information endpoint is also part of the auth spec let's go ahead and stick this on the user information endpoint here we have the base information let's try to log in we'll have login HTTP context CTX and same as before we will just return a results challenge request we want to challenge GitHub and the signature on here is slightly weird so authentication properties and multiple schemas let's cut this out and then we have authenticate schemas this is going to be a new list of strings where in here we want GitHub okay and actually not that interested in the HTTP context we are going to use it for the default endpoint and just print the user so let's place a delegate over here and we will return context users claims we will select all the claims into a new object again remember the claim object does not serialize to Json too well all right hold to list this and semicolon semicolon over here and there we have it get user information and then attempt to log in through GitHub and the application currently is not running one thing that I'm gonna say is coming over here is the this is the location for my application I'm going to grab this URL I'm going to go to launch settings launch settings launch settings right I'm not having a sandwich over here we're doing off I'm going to delete everything except the oauth client profile we are then going to replace all of the URLs with just this one what this is going to do is when i.net watch this will launch my application on the appropriate Port let's drop down the terminal come back to the browser and open up the URL and currently we're not signed in so the claims are empty cookies are empty as well nothing is happening we can now go to login and the first error that we're gonna get is that the Callback is not specified the Callback step is from the Second Step so again we go to GitHub this is first step we do whatever we do over there input password whatever we need to be authenticated we authenticated on GitHub and then we are capable of reaching this endpoint this endpoint is going to produce a code and it's going to send us back to a callback path the Callback path is specified over here if this sends us on the home endpoint just the slash we already have something mapped on there what the oauth service is going to do and by the way remember that this service is going to get executed before any of our endpoints so use authentication will run over here before we reach any of the endpoints in the officet tension we register an oauth Handler which in its own right is an extension of the authentication Handler so the all Handler is an authentication Handler here we have Handler mode authenticate async this Handler Works slightly differently where before we were taking a look at JWT and cookie authentication we were saying once we reach this authentication middleware it's going to load the authentication session from the cookie what this is going to do is it's going to load the authentication session from the authorization code and that is going to happen only once so this authentication Handler inherits from remote authentication Handler where the handle request async which is going to handle the authentication is going to first check should it handle the request and it's going to handle the request only if it's going through the Callback path so throughout the lifetime of your application you're going to be calling different endpoints you're going to have many of them okay as you're recalling all of these endpoints all of the authentication handlers that you have registered are going to try to load some authentication session from a cookie from a token Etc the oauth authentication Handler is only going to attempt the loading if it's on the Callback path remember we are loading the authentication session from the code so when we land on callback we want to take the code and we want to exchange it for an access token and then we use the access token to perhaps fetch some additional information from the user information endpoint so this is how this works right if you've ever specified a callback path before and you were like how does it register another endpoint on there no it means the authentication Handler only runs on this path okay and what it's going to do is short circuit your call to that path and redirect you to the correct place in the end right so we have hello request if this returns true we should go ahead and try to load the authentication session we're gonna skip through all of this but by the end of it we will return you to the return URI okay so it's okay for you to basically say oh call back path anything this can be anything I'm gonna name it of GitHub CB so auth GitHub callback if I land on this path the Handler triggers if I have the code authentication should be successful if I land on this path and you know you just randomly land on that validation checks will run you will fail you may get an exception you may just get redirected to an error page okay we want to make sure we take this path we come back to our configuration and we tell GitHub this is where you need to go let's update our application come back to our website and here we're already trying to authenticate let's come back over here and quickly go through the network tab just so people are aware what is happening if you want to do this yourself make sure you have preserved log turned on I'm gonna log in and the first things that are going to be happening is I'm going to my login page this is going to go to the authorize endpoint GitHub does not redirect you to some kind of internal login page because I'm already authenticated if I wasn't authenticated it would redirect me to the login page and then I would end back on this authorize page add this consent screen I'm going to authorize myself and now we get the next error and this is the biggest point where most people get confused is that you need an additional authentication schema when you come back with the code and the oauth authentication Handler gets triggered to go exchange the code for the token come back with the access token it does not automatically put the access token back in the browser or in a cookie you need to create an additional authentication schema which can take the final crams claims principle and then store it in a cookie so the oauth authentication Handler gets a code by the end of which it will produce a claims principle but it's not configured to either stick it in a cookie or a token or anything else or a database Etc this is where you have to specify the mechanism for that the way that you do it is we're going to add a cookie authentication schema we're just gonna call it cookie and we're also going to set it as default so when we're going through the authentication middleware we will automatically attempt to load the user from the default authentication schema remember this Authentication schema only runs on the Callback path when we land on it and if we attempt to land on it without a code so this will reload as a token is no longer valid but let me go ahead and remove the code here we can see an exception that you know we don't have correct parameters let's come back to our application because we haven't done the final linking so we've added a cookie authentication schema let's grab this value we now want to say that you should sign in with this schema and we do it by specifying an option like this coming back to the browser let's initiate another login and now if you've seen there there was a slight error of too many redirects let's clear the network Tab and we're going to try to authorize again and you should see this network tab going slightly wonky here until we land on the authorized endpoint again but essentially we're stuck in an infinite Loop why well when we briefly took a look at the remote authentication Handler I said that we are gonna be going back to the return URI It's contained in the ticket context where do we create the ticket context right over here the return URI is set from ticket properties dot redirect URI the ticket is coming from the handle remote authenticated async so we have the auth result over here finally we're setting the ticket over here we can keep tracing it so from this author result ticket we're gonna essentially we're reversing through the code so we'll come to the bottom of this map method where we finally succeed and I have a breakpoint over here I'll remove it for now if we will need it again I will place it but here is the final ticket that we're creating we also have the properties which is what we would expect to contain the return URL here we have an option for saving tokens we can just disregard that whole block highlighting properties so let's keep following the trail up until we land on here authentication properties yet unprotected from the state okay so this is important same as the cookie value uses the data protection API to take your claims principle payload serialize it and then put it in the cookie and it's a little bit more than claims Principle as we will find out but here the state parameter contains some information about the authentication properties for this we will find out if we go to program Cs and when we initialize the challenge if we find the the challenge async of the authentication Handler we will scroll down here and we will look for challenge something we can use Ctrl F but here we have it so handle challenge async we have the authentication properties which have the redirect URI if it's not set set the current URL so if we're being redirected from the login page we're going to come back to the login page we don't want that we want to come back to the home page and the issue can be resolved by setting properties return URL or sorry redirect your right property to the place where we want to return after the Callback path so authorization callback path with the code and then redirect URI if we take a look at this Build Challenge URL we will try to take a look at the state over here where the properties get protected and get placed into the state if we come back to the browser and we take a look at the redirect URI that we have here so when we go to the authorize endpoint we will see that the state right over here this query parameter it is present over here and it is maintained throughout the flow so if we take a look at one of the previous requests over here when we go back to the Callback path right so we want to authorize we got the code and we're coming back to the Callback path here we have the state so essentially our authentication properties yet persisted through the path of going through GitHub and that is where our redirect URI is located so that is how if we go into program Cs and over here if besides the authentication schema will will supply our authentication properties so new authentication properties here let me scroll down we will specify the redirect URI which should just be the Home Path and for safety reasons I'm going to say that this is localhost 5005. I'll indent this a little bit and that should give us what we need if we come back over here and I don't know if I can back out into 5005. here I will initiate a login and for the sequel let's just clear the network tab we're going to log in here we're on the authorize page we're going to authorize ourselves and we're going to land on the home page however now the claims are empty and does that mean we're not authenticated no we go to cookies and we can see the cookie that we have configured being set here in memory so we are authenticated and we are capable of hitting this endpoint it's just we don't have any claims how can we actually set our claims well if we take a look at the user info endpoint it actually hints that the value is not used in the default implementation which is the current one that we're using over here it is for using and custom implementations of the on creating ticket what we've learned from the JWT video is that we can subscribe to events which happen in the authentication Handler here exactly the same thing happens if we go to the auth Handler and we are going to start from the beginning of handle remote authentication async where we extract the state and we use the data protection API to unprotect it we make sure that we've managed to successfully extract the properties then there is also a correlation ID if we take a look inside you're going to realize that it is just a cookie okay we create this correlation ID cookie when we Challenge and then once we come back around we still have it and we're able to load it up so this is prevention against cross-site reference attacks and sorry I failed to come back because this is inside the remote authentication Handler which is a base class so coming back to the authentication Handler against cross-site reference attacks we validate this correlation ID which is just a quickie we then have this whole section around errors which we don't really care that much about we then have the code which is the interesting bit if you wish the auth video you understand that this is our ticket to getting the access token if we don't have it something bad has happened finally we exchanged the code inside exchange the code we Forge a request so this is what this looks like which we then sent through the back Channel back channel is just an HTTP client okay so we construct an HTTP request send it through the back Channel and on the end of it we get a response we then do some kind of parsing on the response and we get an off token response where we will be able to obtain an access token token type and refresh token if these are present so by the end of if I'm coming back over here to where we called the exchange code async here we should have the tokens if there were no errors if we have the access token all is good otherwise emit an error finally let's create a claims identity and then we have the option to save tokens where do the tokens get saved if and did we do go to program and we call Save tokens right and we set it to true if we come back to auth Handler this block of code is going to get triggered the access tokens will all get stored in this off tokens list and then get added to the authentication properties this is where we're going to take a quick pause and remind ourselves that the reason we're in the authentication Handler is because we don't have any claims in our cookies okay we go through this auth authentication Handler by the end of which we get a cookie which doesn't have our claims and then there is this option of saving tokens and I'm just going to say it right here if you specify that option to True those tokens will get saved in the final cookie as well and we're really here to see how this service takes whatever data and saves it in the final cookie that is being produced okay so for now all we know is that this stuff gets saved in the properties we then have the create ticket async and here we have the creating ticket event being fired before this event gets fired we have the off creating ticket context which contains the tokens the back channel so everything that we need to you know enrich our claims principle that we have over here finally once a creating ticket is called we return success all good this whole function and we will go into the event in just a second the handle remote authenticate async gets called in the remote authentication Handler right over here so this line of code is where the oauth code on the Callback path completes we've exchanged the code for the token we have all that kind of textual information bundled up in the authentication result we then go further further do some error handling if any errors happen we create that final ticket context we do a little shuffling of parameters that really don't matter in the grand scheme of things we send an event that ticket is received and then finally we sign in as a sign in schema remember that we've specified the sign in schema to be the cookie sign in schema and from the cookie authentication video we know what happens in the authentication Handler when we sign in okay because we've explored that option coming back to remote authentication Handler we sign in with that cookie where we're passing in the claims principle that we want to sign in is this has been extracted from the token or you know and partially and then properties which if we specify the option for Save tokens this is where the tokens are going to be present okay and these properties and again if you need the refresher where they're being set alt Handler let's scroll down here to where we have the save options block on the properties we have store tokens and we're socket the storing of token's result where we're extracting every individual token from the token's result okay we have the tokens we store them in the authentication properties finally we are signing in with the cookie authentication schema where we're using our claims principle and the authentication properties now the important thing and again we're going to come back to add cookie we're going to come back to the cookie authentication Handler and we're gonna look for sign in async because that is what we're doing over here we're trying to handle sign in async when we create the cookie sign in context if we scroll a little bit down we should find the append response cookie this is the bit which is going to add the cookie value which is going to be a string to the response the ticket over here is this authentication ticket which contains the contains the claims principle and the authentication properties your cookie is the proper properties and the claims principle combined together so when we're signing in with the cookie authentication schema at the remote of authentication Handler we take the tokens and the authentication properties and the claims principle that we have and we're putting it in the cookie later on in program CS we can then go to http context and call get token async and grab something like an access token where again if we explore this option so we're now coming around from another direction we call get tokens on the authentication Service so get tokens get tokens and here we have the authentication Service where we try to authenticate so this is going to trigger authentication so it will load up your authentication context from the cookie or whatever it is and it's going to get a token from the authentication properties okay so the same value that it loads up the claims from the cookie by deserializing it it does the same thing but deserializing the authentication properties out of that cookie perfect now that hopefully you're either clarified or completely confused let's come back again to why we've started we are trying to find out why we don't have any claims in our user session we should have some knowledge now that we're going through this process and I should speak a little bit more freely that if I find the code exchange by the end of it we get tokens and finally we're creating a ticket by the end of which then gets returned to remote authentication Handler that doesn't do anything with the token to create a claims principle that my friend is up to you you need to tell the auth authentication Handler how to parse that token into a claims principle and that is the bit that we're going to take a look at right now so if we go into program CS we're going to subscribe to the event that the auth Handler calls during create ticket here we have creating ticket and this is going to call the on creating ticket function let's go to program CS we will go to all events on creating ticket and here we will assign a new context Lambda on the context Lambda we should have an access token this access token as part of the third step over here we should exchange for user information with the user information endpoint that we have already set over here let's create our request so request a new HTTP request message let me go along over here we're doing a get method and then we have the context dot options and the user information endpoint having this request we also have the access token which we should attach to the request so request headers the authorization request so new authentication header do not try to do this where you have better and then token you can see first you have the schema which refers to the authorization schema of the header or HTTP specification not part of asp.net core authentication schemas okay this is a different type of schema so better authorization schema we want to take the token and attach that as the value so we will have authorization key value colon Bearer space access token okay as part of the request once we have the request we will go to the context we will grab the pack back Channel and we will send async this request right so we're sending this request to GitHub this will give us some kind of result and by the way I'm not sure why Microsoft is not doing this during its back Channel extraction so you can see here we have the request message and then also response these are or should be from my experienced pre-pended with using to release resources or maybe I'm not aware of something but finally once we have the result we can go to context and we should be able to read from Json and just grab a Json element Json element is a very you know generic thing but this should be a user record and this whole oauth service is built to work with Json elements okay and actually let me not forget to wait over here and over here as well if we quickly diverge again into the auth Handler and we take a look at creating ticket async and auth creating ticket inside of here you will have a run claims action function so on the option you're gonna have some claim actions the purpose of this piece of code is it assumes that you're gonna make some kind of request to an endpoint to fetch user information or that information may be in the access token so you have two places where you can get user information this piece of code basically says look for these keys and map them to these claims okay and that's all this piece of logic is doing the place where it extracts the keys or the claims is going to be a Json element this user data so in program CS we want to go to the context and we want to run claim actions against this user object the bit that we would be missing at this point are the claim actions so before we add any of them let's place a breakpoint over here and we're going to take a look at what the user is what the access token is and we're gonna have a little talk about it with the application restarted let's attach a debugger I'm going to come back to the browser and resign in login I have content null exception that's because I'm reading it from the request not the result so let's restart the application here I will need to reattach the debugger there we go and let's login again and here we hit this point after obtaining the token and all of that so for the access token value this is what this looks like please please notice auth spec does not say JWT anywhere this is not a Json web token this is essentially a random string that is just an ID it is a reference on github's server later we're going to be taking a look at creating our own auth server so just understand that this is okay we then just use this token to obtain user information this is what our user information is going to look like so let me open this up the Json object is over here in the middle all we need to know that we have keys like login which points to the username and then another key like ID which points to the user identifier let's load up the login and the ID into our claims object for this we'll need to create mappings let's just play this hopefully I'm not gonna forget two words like login and ID and oh we're gonna find claims actions and we're going to add a claim action or there is a convenience method where you can say map Json key where you're mapping it to a claim type from a key let's map it to a sub from ID so sub is subject and then we can do something a little bit more custom for the name or the username let's do like claims types we'll take a Microsoft claim why not grab the name and Supply the login key and there we go the application has already started and by the way if we refresh right now hopefully this is no surprise by now we've not reapplained the cookie we have the same old cookie that contains no claims if you change the server side the state in your browser doesn't change okay we can clear this and if we go to login we now have an authentication session with claims from GitHub perfect some big points to understand over here with the context so if we go back to oauth Handler auth authentication context we will have the HTTP context assigned to the let's go all the way to the base of this to the HTTP context property okay so we can go to context grab the HTTP context request Services get database put access token in the database against a user so if you watch my authentication landscape video and you've seen how the token shouldn't be going in the browser at the moment if you choose the save tokens option it's going to go in the cookies into the browser if somebody gets the cookie they're not going to be able to get the access token but some things that you're not able to do if you put the access token in the browser is you're not able to watch over it so let's say your user doesn't use your website for a month your cookie policy says 90 days the access to token says 15 minutes you can keep using a refresh token to keep the token active if the tokens are stored on the back end if you put them in the browser your backend cannot keep those tokens alive okay so if you want a better user experience intercept the tokens store them in your database keep your cookies small and keep your users tokens alive for an appropriate amount of time and that's pretty much all you need to know over there if we search for Microsoft asp.net core authentication Facebook service right so let's add over this and with this package now added we can go to add Facebook authentication if we open it up so add Facebook go all the way down over here we have Facebook options and Facebook Handler can you guess what they inherit from if you look in Facebook options all of options which set up everything that we've set up manually for GitHub and all of the external authentication packages will do this for you and you know now that you can override them just by extending either the options or configuring them and Facebook extensions Facebook Handler this extends the auth Handler which you can do as well if you have your own custom server with custom conventions where you can customize all individual parts of the oauth Handler are going to work now that you've seen me do it hopefully you're not scared to look through this code you know that even though this function is maybe like 150 lines of code it is not that scary load up the request validate it get the code exchanger for the token take the token do some things with it otherwise create a claims principle with the authentication properties but that all on the cookie deal out the cookie use that cookie as the representation of your external authentication session and this will be it for this video thank you very much for watching if you enjoyed it don't forget to leave a like subscribe click the notification Bell if you have any questions leave them in the comment section you can also leave a comment if you want to say thank you 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 again thank you for watching the video and have a good day
Info
Channel: Raw Coding
Views: 11,531
Rating: undefined out of 5
Keywords: asp.net core, dotnet, external, authnetication, facebook, google, github, microsoft, oauth, oauth2, oauth2.1, tutorial, example, guide, explanation, asp.net core external authentication, github external authentication, AddOAuth, minimal api
Id: PUXpfr1LzPE
Channel Id: undefined
Length: 35min 42sec (2142 seconds)
Published: Tue Nov 01 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.