Getting Started with Duende IdentityServer (2/2)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi and welcome to the second half of this talk on getting started with duende identity server in the first part of this talk we saw how to set up identity server and how to get a token using cool in this part of the talk we'll see how to protect an api with identity server and we'll see how to protect an nvc application and use the tokens from identity server to allow access both to the nvc application and to the api so the first thing we'll do is to create a service with an api so to create this service and add this api we're going to create a web api application so from the shell i can run dot net new and then the template is now called web api if we look in this directory we'll see it creates for me a project called weather api and that will return some weather data so what i've done now is i've created a solution using jetbrains rider and i just loaded that project that i created from the shell into that solution so the first thing i'd like to do here is change the ports this is currently listening on port 7032 for https and 5173 for http i'm going to change these to five four four five and five zero zero two and then if i run this just to see it working and if we browse to the site then sure enough we get the weather data this is just randomized data that's just created by this service so the next step will be to add security to the service and that only make the service available if we have a bearer token to use so now that we have this we need to protect it so back in the project the first thing i want to do is to add the authorized attribute to the controller and if i restart this then go back to the browser we now get an exception the exception we're getting here is saying we've added the authorized attributes but we haven't added authentication yet so that's what i want to do next so we're going to authenticate this using something called bearer authentication if you remember we just grabbed the token from identity server that's known as a bearer token our client will send that token to this server and the server will then check that that token is valid and to do that we don't need any libraries from identity server but we do need to connect to identity server to send it a copy of the token and to do that we're going to use a microsoft nuget package so we're going to nuget the package we want is called microsoft.asp.netcore.authentication.jwt bearer so i select that and add it into my project we then need to configure that code in our program.cs file so inside here we need to add a new service so we do build.services and we're going to add our authentication service when we add the authentication service we need to tell the service what scheme it is for and the scheme in our case is going to be bearer we could just put the string here bearer well there's a constant defined in that package that we can use called in the name space called jwt bearer defaults and the value is authentication scheme and we also need to configure the service we also for example need to tell it how to connect to our identity server so we do that by calling add jot bearer this takes in options and inside this options we set a couple of values we're going to set the authority and this is the url of our identity server so this is going to be https localhost 5443 and that's the endpoint of our identity server we also tell us the name of the audience we're checking this token for and that's going to be our weather api that's the name of the audience we also finally tell it what the token type is and we do that by adding a token validation parameters valid types this is an array of valid types and we specify the name of the type and the name for our jot token is at plus jwt once we have that then in this code we also need to add a call to app.use authentication to make sure we add this authentication into the pipeline okay given that let's restart the service and now if i browse to the service we get back of 401 so we now have authentication in place but we're no longer authenticated or we're not authenticators we're not able to hit this endpoint so to hit this end point we need to get a bearer token and we already know how to do that so we saw earlier i can send an http post to my identity server to this connect token endpoint i can specify the client id the scope and the secret and this will give me back a token i can then use that token which i'm going to grab here in another http request and that request looks like this so this time i'm sending a get to the weather forecast endpoint and i specify an authorization header the type of authorization is bearer and into that header we pass the token so if i grab this command and go to my shell and paste that command in we hit the end point and we're getting back the weather data so we've hit the endpoint we've been authorized and it's returning the data to us if in the shell i run that command without the bearer token it fails we get back no data we're getting back at 401 here cool is just not printing out the return value we get back here but we are getting back at 401 the same as if we would hit this endpoint from the browser without the bearer token so we've now seen how to add an api how to protect that api using identity server how to call that api using a token so what i'd like to do now is to write a net client for this api and i'm going to do that by writing an nvc application so if i go back into rider this time i'm going to add the application from inside writer itself rather than doing this from the command line so here i'm going to do add new project we're going to add an asp.net core web application the name of this is going to be weather mvc it's a model view controller application c csharp.net6 and no authentication i'll create this again if i go into launch settings here i'm going to change the ports this time we're going to run on 544 and 5 101 again the http port doesn't matter we're only using https but just for completeness say i'm changing that port as well okay so before we write code in here let me go back into the weather api application for a moment go back into my controller and remove this authorized attribute and the reason i'm doing that is that we're going to add a client to our mvc application that client's going to call into this weather api service and we want to make sure that clients working before we add any authentication to it okay so in here we have a single controller called the home controller and on this controller i'm going to add another endpoint we'll make this an async endpoint it'll return a task of i action result and we'll call this weather so from here i'm going to call the api endpoint so to do that i need an http client so i can say something like using var client equals new http client on the client i'm going to call the get method that's going to get request to the endpoint and that's going to return me a result so let's say file result this will be an async method so i want to await on it and i want to await on client dot get async and to get async we pass the url in this case of our weather service and that's https localhost 5445 slash weather forecast when i get that result back i can check to make sure it's valid so do we have a success status code if we do we'll do some processing if we don't we'll throw an exception so let's do throw new exception and then here say unable to get content okay so the content we get back here is going to be jason i want to decode adjacent into a c-sharp object so to do that i'm going to add a new c-sharp class to my project so in my models directory i'm going to create a model called weather data this has the date the temperature in celsius the temperature and fahrenheit and the summary string that comes back from the service back in the code i want to decode the incoming json into that model and to do that i'm going to use newtonsoft's json so in nuget i can add the newtonsoft.json nuget package to my weather mvc project and then in here i can say something like var model equals a weight result dot content dot read as string async so that gives me back text i can then decode that text into data so i can say var data equals jsonconvert dot deserialize object from the model i'm going to deserialize it into a list of weather data then once i have that i'm going to return a view that contains that then i can add a view to display this data and that view will look like this so we reference the model as a list of weather data and then inside the view iterate over that list and just show in a table the date the summary and the temperatures so if i run the weather mvc application and browse to localhost 544 home weather then sure enough we get back the weather data but remember i did turn off authorization on the api so if i go back to my code and go back to the weather api and the weather forecast controller and turn authorization back on and then restart the weather api application and go back to the browser and refresh and sure enough we're throwing an exception so we're now trying to hit an end point that requires authorization we aren't authorized because we're not sending across a bearer token so we don't get a success code coming back and it's the mvc application that's now throwing this exception so let's go and fix that so i need some way within this mvc application of connecting to the identity server and asking it for a token and the way i'm going to do that is by introducing a service so inside weather nvc i'm going to add a new directory called services and then in here i'm going to add a token service so before i show you the service i want to do one more thing i want to add another nuget package and the package i want to add is called identity model i'm going to add this into our nvc application and this has some helpers this has some properties and some methods that we'll use in our code so in my services directory i'm going to add three files this is code that i wrote earlier and this is the code that makes up the token service so there's an itoken service interface that has a method called get token and this returns a token response and that token response comes from the identity model package and this just contains the details of the token there's also an identity server settings file and if i look in here this identity server settings has things like the discovery url so where do we get the discovery document from the name of the client and also the client's password and those values map onto the values we set for the client inside identity server so instead identity server remember we had two clients configured we have the m2m client and inside the m2m client we have a client id and we have a password and that password is this value here it's the client secret that we're going to use when we load this service we need to load these settings we need to load these values we're going to get these values from the app settings file so in the app settings file we have a section called identity server settings inside here it has the discovery url which is the endpoint for our identity server it has the client name which is m2m.client and it has the client password which is the client secret that we set in identity server we then have the implementation of the token service so we have a classical token service that implements itoken service this has an eye options which is this identity server settings that we load from the configuration and we hold in here a reference to the discovery document so in the constructor for the service we call this get discovery document async method that comes from the identity model nuget package that we installed and we pass this the discovery url and this comes back with the discovery documents we hold a reference to our discovery document inside here and we need that discovery document as is from there we can get the url of the token endpoint inside the get token method we call another helper method called request client credentials token async and that gives us back a token response and into this we pass in the token endpoint we pass in the client name we pass in the client password and we also specify the scopes that we want and this gives us back a token so this token response will contain this bearer token so to use this i need to do two things in my home controller i have to inject this so inside here i'm going to say i token service token service and then assign that to a variable remember an instance this will be constructed and that constructor will go and get the discovery document then inside the call to the weather endpoint i need to access the token from that service and add that token as a bearer token to my http client get call so inside here i want to say something like this var token equals underscore token service dot get token remember the parameters of this is the scope so that's weather api dot read in this case that will give me back a token assuming it succeeds or will throw an exception if not so this is an async call so we need to await on it so once i have that i can then set this token on the client so to do that i can say client dot set bearer token this is another helper function on identity model and then to this i can pass token dot access token and that will add the bearer token as a header as an authentication header on this client before it sends the request so once i have that i now need to configure the application to make use of that service so back in program.cs i can do this i can say builder dot services dot add singleton i'm going to add the i token service and the token service so that will fire the token service up but remember if i look at this token service this needs some configuration this is past and i options of identity server settings so i have to configure that in program.cs as well so to do that so inside here we say builder.services dot configure and i need to specify the type here the shape of the data i'm grabbing from the app settings and that's my identity server settings and then to this i have to pass a reference to that section inside app settings so we call builder.configuration.get section and we give it the name of the section and the section name is identity server settings as we just saw that's this section here this line of code will load those values from the app settings configuration file and it will pass those settings into the token service as this i options of type identity server settings okay so with that in place if i restart my weather mvc application and then hit the home weather endpoint again then we now get back the data so now we've been authorized we're selling across the bearer token and we can get the data from the weather api so just to go over this one more time what are we trying to do we're trying to hit this protected endpoint get on this controller on the weather forecast controller with the authorized attribute and to do that we need a token we're trying to make that call from this weather endpoint here so it's at this point that we need that token to get that token we're injecting a service called the token service for the token service we have a set of initialization and that initialization data is held in appsettings.json and it's this identity server settings we load that initialization data that configuration data here in program.cs by calling builder.services.configure and passing it the name of the section when the token service itself is loaded that configuration data is passed in and the first thing we do is use that configuration data to get the discovery document by calling get discovery document async back in the controller we call get token to get the token that's a method called on the token service and that get token call calls request client credentials token async we use the end point from the discovery document and then things like the client name and the client password from the configuration to configure this request if that succeeds it returns the token and we can then call client.set bearer token to pass that token in the authorized header of the request that token is then checked by the protected api i'm assuming the token is okay at that point the call succeeds okay so we are most of the way there we have an nvc client that gets a bearer token from identity server sends that bearer token to the api and that api checks that bearer token with identity server to make sure it's valid i want to do one more thing i want to add authentication onto the mvc client so what the mechanism whereby when we try and hit this endpoint on the mvc client this endpoint is also marked as requiring authentication we throw up a user interface the user has to enter the username and password and if that succeeds they get access to the nvc client and then the mvc client can go on and get access to the api client so let's do that now okay so the api end point is protected but this endpoint here this weather endpoint itself is not protected so at the moment we don't require a user to authenticate before they can hit this weather endpoint so here i can just refresh the page there's no authorization there's no authentication required to allow the user to get this weather data so what i'd like to do is to make sure that the user has to authenticate before they can ask for the weather data so when we do this the first step is to add the authorized attribute to this endpoint now if i restart this nvc application and refresh the page we now get an exception and again we saw something similar to this previously it's saying there's no authentication scheme specified so basically i've said i want to be authorized but i haven't said how i want to be authorized so to add authorization to this we'd like the user to log in so the first question is is where do we add the ui for that login but we're going to use identity server so we're going to ask the user to log in via identity server so we're going to add that user interface onto identity server itself and to do this we're going to cheat slightly so we saw earlier we can build identity server from a template and that template will give us a user interface but we can add that user interface retrospectively to this application here so if i go to this uri so github.com duende software identityserver.quickstart.ui this is a git repository that contains the quickstart user interface code now if i scroll down here it gives us a couple of ways of allowing us to add this code into our project if you're running on windows you can run a powershell script so that's the iex script there if you're running on mac os or linux and i'm running on mac here you can use this curl script so if i grab this curl script and copy it to the clipboard so i'm in the identity server project directory here in my shell so inside here we have the cs proj file we have the program.cs from here if i paste in that code script and run the script look in this directory so we can see that there's a pages folder there's a dub dub root folder so we updated our project with this user interface code so back in jetbrains rider this is the identity server project we can see now we have our pages folder with some razor pages in here for the code and we have a dub dub dub root folder with again things like the css the javascript that we need to create a user interface on top of identity server so to do that there are some changes i have to make so in identity server i need to make some changes to program.cs so in here currently we're only mapping a get request to the root endpoint and returning hello world we need to change this now so that we map onto the controllers and we return the appropriate ui return the appropriate response there's a few things i need to add here so let me get rid of this map get so i need to say app dot use static files app.use routing app.use authorization we need to add our razer pages we need to say app.map razor pages and then we need to call require authorization as well so down here we've said map razor pages but i have to add the razor pages service here we call builder.services.ad razor pages and we should be good to go so here now if i refresh identity server and rerun the app and then browse to the localhost 5443 endpoint which is the identity server home page previously when we hit this endpoint it served up the text hello world now if i hit this end point we now get the durand identity server welcome page so we've definitely added some ui onto identity server now that i have that i can go back into my weather service and add the code required to ask identity server to authenticate the user so again i'll need some configuration for this so in appsettings.json i'm going to add another configuration section and this section is called interactive service settings so the authority url is localhost 5443 that's identity server and then the client id and the client secrets are the values we set on the client inside identity server so i look inside identity server and look at my config code and scroll down to look at the client this is my interactive client and this is my client secret notice it ends in 86b0 and this is the client secret here notice as well that this client here the interactive client has some uri set up there's a redirect uri which is localhost 544 something called a front channel logout uri a post logout redirect uri and these all point at localhost 544. if i look back in my mvc application and look at the launch settings indeed we are listening on port 544 so identity server knows about this application we now need to configure this application so that it knows about identity server so to communicate with identity server to ask for user we are going to use open id connect and to do that we need to add a nuget packet so in here i need to add the microsoft dot asp.net core authentication dot open id connect package and i'm adding that to my mvc application now in program cs for this application i have to configure the authentication so in here i'm going to call builder dot services dot add authentication and again this takes on options so for this options we need to tell it what scheme to use and these schemes have a name and we'll just call our scheme cookie we need to tell it how we're going to challenge so we have something called the default challenge scheme and for that as we've said we're going to use oidc and at this point the authentication is configured but we need to add some extra stuff to our services so we call add cookie the cookie has a name and that's the name you specified above to use that's going to be cookie i need to configure oidc connect so i call add open id connect we tell it the name we've used for the challenge scheme here so these two match up that's oidc and again we pass some options to configure this in here we have to tell it the url of the authority to use that's the url of our identity server and that's one of the things we put into our configuration into appsettings.json so to get at that we say builder dot configuration and we give it the name of that value in the app settings.config file which is interactive server settings colon authority uri we do the same thing for the client id so option start client id and that again is builder.configuration but this time it's interactive service settings colon client id and also the client secret okay we also need to add the scopes so we have an options dot scope which is a collection we're going to add something into this collection and again the value we're going to add comes from the configuration file this interactive service settings scopes and as this is an array the index into the array is element 0. okay with that in place there are a few more options we need to add here and these options are going to tell us how to interact with identity server so we set something called the response type so we're going to use code and this matches the grant type set in identity server we set use pkce to be true we set the response mode to be query and then we say that i want to save these tokens that are available for use later on so what are these things to save tokens tells the server to save the tokens it gets back into the cookie response mode sets this up so the data carried back in the response comes back in the query string and then use p k c e pronounced pixie and what p k c e stands for is proof key for code exchange and this is basically a mechanism for protecting the communication in the background while we're exchanging our information to get these tokens okay so now that we have client authentication here are the clients logging into the application the client will be allowed to access certain scopes and as part of this authentication it will get back an access token now currently when our mvc controller turns around and calls the api it gets a token from the token service it says i'm the client give me back a token and then we use that token to hit the api but now as i've just said we're actually getting back a token as part of our user authentication here and we can use that token so in here rather than calling get token from the token service if i get rid of that line i can get the token given to the client from the http context and this is another extension method here so we call get token async on the http context that gives me back a token that's a string i can then just call set bearer token with that token and then turn around and call the api and then finally down here as well as app.use authorization i want to say app.use authentication okay with all that in place we should be good to go so if i take my weather nvc application and restart it from here if i go to slash home slash weather it now asks me to authenticate if you remember in identity server we set up a user called alice with a password called alice so i click on login at this point it's asking me to consent to this login now this is not the default behavior i'll show you how to get the default behavior in a moment to get this behavior if i look back inside identity server i've turned on the require consent equals true value for this client that defaults to false so i left that out it wouldn't require consent i'll show you that in a moment so back here it's telling me what's being asked for so my user id my user profile things like my names access to weather api.read and then i can allow or don't allow so if i say yes allow it takes me to home weather that now has the token i've been authenticated i've been authorized to use this endpoint that itself then gets a token to call across to the api service that token is also checked and we get back the data so let me go back to identity server here remove this field and restart identity server so again from the home page here if i go to home weather it has to authenticate me if i type in alice and alice and log in then it's no longer asking me for consent you can enable or disable that but now we have a complete end-to-end solution we have our mvc application which is the front end for the user that asks the user to authenticate that front end talks to the back end that's a protected api and to talk to the back end that has to get a token from identity server and send that token across so using oidc to authenticate the user and then using a java web token to authenticate against the back end as i mentioned earlier we'll put this code up on github and the repository is here that's the end of this walkthrough i hope you've enjoyed it
Info
Channel: IdentityServer
Views: 11,717
Rating: undefined out of 5
Keywords: IdentityServer, OAuth, OIDC, Identity Server, Duende, SAML, Client Applications, API, REST, MVC Application
Id: qyedQ6RzOHw
Channel Id: undefined
Length: 32min 7sec (1927 seconds)
Published: Tue Mar 08 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.