Blazor Webassembly Custom Authentication [Blazor Tutorial C# - Part 12]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi, welcome to Coding Droplets and thank you for watching this video. This is a complete course to learn .Net Blazor development. So, this video is part of a series, and this is Part 12 of .Net Blazor Tutorial Series. You can check out the playlist link, that is mentioned in the video description for finding the other videos in this series. In the last video we’ve implemented a custom authentication and authorization in a Blazor Server project. Now in this video I’ll walk you through the implementation of a custom Authentication and Authorization in a Blazor webassembly application. There are only some differences in the implantation process. As we done in Blazor Server project, we’ll authenticate a user, and the user will be able to access the components in the project based on their roles. Here the authentication is happening from the server and the server will send a JWT token to the client. The blazor webassembly client application will authenticate and authorize the user with the received token. So, let’s get started with the Blazor Webassembly authentication and authorization tutorial. First let me create a new Blazor Webassembly project by clicking on the create a new project button. Then I’m searching for Blazor Webassembly project template and selecting it Let’s provide the project name as Blazor Wasm Authentication and Authorization. Then clicking on next. I’m using .Net 6 Framework and I don’t need to configure for HTTPS. Then selecting the ASP.Net Core Hosted checkbox. So, we have already done a Blazor  WebAssembly video in which we explain about ASP.Net Core Hosted option. You can see the video in the Blazor Tutorial playlist. I suggest you to  watch that video, if you are new to the ASP.Net Core Hosted option. Now let’s create the project and you can see the three projects in the solution explorer – Server, Client & Shared. First let me create a folder named Authentication in both client and server projects. We’ll be keeping all the classes  related to authentication in these folders. Now first let’s do the implementation in the Server project. First, I’m going to create a model class named UserAccount for populating user details from the database. This model class is having three properties – User Name, Password & Role. Next I’m creating a service named, UserAccountService which will fetch the User Account details from the database. But in this demo application I’ll not be implementing the database. I’ll hardcode few users and save it in memory. But you can do the database implementation in this class and fetch the UserAccount data from your database. I’m declaring a private variable to temporarily save the user data in memory. You don’t need this variable. In the constructor of UserAccountService, I’m hardcoding two users – Admin & User. These two users have different roles Next I’m implementing a method to get user account details by filtering with their username. In this method, you can read data from your database. I’m just doing the filtration in the private variable Now I’m doing the dependency injection for this UserAccountService. I’m registering it as a singleton service. If you have any doubt about dependency injection, you can watch the dependency injection tutorial video in our Blazor Tutorial series. Next I’m creating a new model class named User Session in the shared project. This model class must be used in both server and client projects. And that is the reason, I’ve created it in the shared project. There are five properties for this model class. UserName, Token, Role, ExpiresIn which is used to store the number of seconds remaining for the token. And finally, ExpiryTimeStamp which is a DateTime datatype. I’ll explain about this model class in detail while implementation. Just before starting the implementation, we have to install two libraries from Nuget Package Manager – System.IdentityModel.Tokens.Jwt & Microsoft.AspNetCore.Authentication.JwtBearer. So let’s install those libraries. First I’m searching for System.IdentityModel.Tokens.Jwt and installing it Next we need Microsoft.AspNetCore.Authentication.JwtBearer and installed that as well. Now I’m creating a new class named JwtAuthenticationManager inside our Authentication folder. This is class for generating the JWT token. First, I’m creating a string constant, JWT Security Key. You must provide a very strong security key here. Next another integer constant, JWT Token Validity Minutes. I need the token to be only valid for 20 minutes. You can provide whatever value you need Now I’m creating a constructor for this class and providing the UserAccountService as a parameter Let me create a new private object and assigning this parameter. Next we are creating a method for Generating the JWT Token. The method accepts username and password as parameters. And it will return the User Session object as a response. For UserSession, we must import the shared project namespace, First let’s check whether the username or password is null or whitespace. If any of them is null we’ll return a null value from this method. Next let’s validate the user credentials. First I’m fetching the user account details from user account service by providing the username. Then let’s check whether the user account is null or the password is invalid. If the user account with the provided username doesn’t exists, we’ll receive a null value. So if it is null or password is invalid, we’ll return null value from this method. Now what if the user credentials are valid We must generate the JWT token and return the user session object. First I’m creating a token expiry time stamp which is a datetime object. The value will be Current time plus expiry validity minutes. That means current time plus 20 minutes. Next I’m creating a token key object which is the bytes of the security key constant. After that creating a new claims identity object with two claims in it – User Name and Role. Next is a signing credentials object which is needed for signing the JWT token and you can see the security algorithm which I used in it Security Token descriptor is needed for generating JWT token. It includes the claims identity, expiry date time object and the signing credentials. Now just creating an object of JWT Security handler. This object is used to generate the token. Creating the security token object now using JWT Security Handler. Now writing the token in text format to a new variable named token As we have completed generating the JWT token, now we can create a new user session object and return it as a response of this method. I’m providing all the values for User Session object – User Name, Role, Token and ExpiresIn. You might have noticed that we have not assigned the value for ExpiryTimeStamp property of the user session object. That we are going to use only in the client webassembly application. Now we can return this user session object Next important thing we must do is, adding the dependency injection for Jwt Authentication. Builder.Services.AddAuthentication and assigning values for Default Authenticate Scheme and Default Challenge Scheme properties. We can provide JwtBearerDefaults.AuthenticateScheme for both the properties. Also providing AddJwtBearer and assigning values of few properties. I’m assigning RequireHttpsMetaData as false. Save Token as true. TokenValidationParameters as new TokenValidationParameters in which ValidateIssuerSigningKey is true. And providing the Signing key as have provided in JwtAuthenticationManager class. Then validate issuer and validate audience as false. Also don’t forget to provide app.UseAuthentication and app.UseAuthorization. So, Next we can create a model class named LoginRequest in the shared project. This model class will be used to send the request from client to the server when a user tries to login. Here we need only two properties in this class – User Name and Password. Now let’s create an API controller in the server project. I’m naming it as account controller. In this controller, I’m creating a new method named Login which will accept LoginRequest as parameter from the body content. And it will return a UserSession object. Inside the method let me create an object of JwtAuthenticationManager. We must pass UserAccountService object as parameter to the JwtAuthenticationManager constructor. So let me declare a private object of UserAccountService. Now creating a constructor for the controller and assigning the value for the UserAccountService object. So we can pass this object to JwtAuthenticationManager. Next calling the Generate token method in JwtAuthenticationManager by providing the username and password in the LoginRequest we received. Finally just checking whether UserSession is null and returning Unauthorized response if it is null. Otherwise we’ll return the UserSession object. Let me also provide HttpPost, Route and AllowAnonymous attributes for the Login method. Next, we are modifying the WeatherForecastController. This is a sample api controller which will get created while creating the project. You can see that there is a Get method in this controller. The Webassembly client project is having a sample razor component named FetchData which will consume this API. Let’s provide an authorize attribute for this method. So that this method can only be called if the user is authenticated, and the user should have an Administrator role. Now we have completed the implementation in the server project. Next let’s do the implementation in the client Blazor WebAssembly project. So please don't  forget to give me a thumbs up for this video. Also let me know your feedback in the comments  section. I would like to hear from you. In the   client project, I’m installing two libraries. – Microsoft.AspNetCore.Components.Authorization and Blazored.SessionStorage. Blazored.SessionStorage is a library which we can use to read and write data in the browser’s session storage. We can also do it without using this library by calling javascript methods using IJSRuntime. So I’m installing Microsoft.AspNetCore.Components.Authorization now. Next installing Blazored.SessionStorage library. We must add the dependency for Blazored SessionStorage inorder to use it. So I’m adding it. I’m also creating an extension for Blazored SessionStorage. I need to save the data in an encoded format or simply a base64 encoded format. So I’ll implement the encoding functionality in an extension class. Let me create a new folder named Extensions and creating a new class named SessionStorageServiceExtension. I need it to be a public static class Next creating a method to SaveData by encoding json format of the object. First let me create a json string from the object using JsonSerializer Next converting the json string to bytes. Then converting the bytes to Base64String. Finally saving the Base64String in the session storage. We need one more method to read data from session storage. In this method, first I’ll read the base64string from session storage. Then convert it into bytes. Next reading the json string from bytes. Finally deserializing json string to the object using json serializer and returning the object. Next important this we need to do is, to create a Custom Authentication State Provider class. Let me create it in the Authentication folder. This class should be inherited from Blazor’s Authentication State Provider. Now we can implement the abstract class and that will override a method named GetAuthenticationStateAsync. Before implementing this method, let me create private object for ISessionStorageService which is an interface in blazored session storage. Also let me create a ClaimsPrinciple for anonymous users. Now I’m creating a constructor for this class and providing ISessionStorageService as a parameter. Then assigning the value of our private object Now let’s start the implementation of GetAuthenticationStateAsync method. First I’m providing a try-catch method and if any error occurs, we’ll return the authentication state of an anonymous user Next in the try section, first I’m trying to read UserSession from Session Storage and if it is null, we’ll return the authentication state of an anonymous user. Same way as we have provided in the catch section. Now if the UserSession exists, we’ll create a new claims principle with two claims in it – Username and role. The most important thing you must understand here is, you can see a string value JwtAuth. That is a string value which we need to provide for Authentication type. If you forget to provide that value, the application will consider the user as an anonymous user. Here you can provide anything. It can be even any dummy value. Anyway here, I’m providing it as JwtAuth. Finally, returning the authentication state of newly created claims principle. I’m also creating a method to UpdateAuthenticationState. We’ll call this method when user login or logout. If login or logout happens, we must update the session storage and notify blazor about authentication state change. So this method accepts UserSession object as a parameter. We’ll pass the object from login page. From logout page we’ll pass this value as null. Now let’s implement the method. First I’m declaring a ClaimsPrinciple object. Then checking that the user session is not null. User Session not null means, the user is trying to login. So we can create a new ClaimsPrinciple. Next I’m assigning ExpiryTimeStamp property for user session. This is to check whether the token is expired or not. After that I’m saving the user session to the session storage. Next moving to the else part. Else part means, the user is trying to logout Here we just need to make the ClaimsPrinciple anonymous and remove the user session from the session storage. Next important thing is to notify blazor about authentication state change. We can call NotifyAuthenticationStateChanged method which is in AuthenticationStateProvider class and pass the authentication state as the parameter. Now I’m creating one more method named GetToken. This method is for the razor components to get the JWT token from the session storage. In razor components, we need the token while consuming an API. I’ll be showing it soon. In this method we are checking whether the token got expired by comparing the current time with the ExpiryTimeStamp property value. Now let’s do the dependency injection for the CustomAuthenticationStateProvider class which we just created. Next in the imports.razor file, I’m importing Microsoft.AspNetCore.Authorization and Microsoft.AspNetCore.Components.Authorization namespaces as we are going to use these namespaces in many other razor components. So that we don’t need to import it separately in all other components. Now we can create a new razor component for login form. I'm naming it as Login.razor. First let me provide the page directive “/login”. So that this razor component can be accessed with that URL. Also I’m injecting some dependencies – HttpClient, IJSRuntime, AuthenticationStateProvider and NavigationManager. Next let me provide the html design for login page. It includes two inputs – UserName and Password. Then a button for login. Now in the code section, I’m declaring a private LoginRequest object which is used to fetch data from the inputs. Next I’m creating a method named Authenticate which will be called while clicking the login button. Here we can call PostAsJsonAsync method in HttpClient object to consume the Login API method which we have implemented in the Account API controller in the server. We are passing the LoginRequest object as the request body. Next if the API call is successful, we’ll read the user session object from the response content. Then I’m parsing our CustomAuthenticationStateProvider class from the AuthenticationStateProvider which is an injected object. Next we can call the UpdateAuthenticationState method by providing the user session as the parameter. Finally redirecting the user to the home page or the index razor component. I’m also checking whether the response status code is Unauthorized using an else if condition. If it is Unauthorized, we can just show an alert “Invalid User Name or Password”. Next we need to do some modifications in the App.razor file. First we need to change RouteView to AuthorizedRouteView. Then only the razor components will receive the authorization details Next I’m adding a CascadingValue named CascadingAuthenticationState. So that we can declare a cascading parameter in the code section of other razor components and get the user authorization details Now we are going to make some UI design changes for Unauthenticated and Authenticated users. We also implement some differences in the UI based on user roles. So first let’s start with Index razor component. First let me inject IJSRuntime. We need to show an alert with username Now we can show a greeting message using AuthorizeView. We’ll show Hello and username for authenticated users. We can get the username from @context.User.Identity.Name. We’ll be just showing Hello Guest for Unauthenticated Users. Next we’ll just show a button for only authenticated users. If the user click the button we’ll show the greeting message in an alert. So let’s implement the code section I’ve declared the cascading parameter for receiving the user’s authentication state. Now we can implement the method for showing the alert while clicking the button. First let me get the authentication state from Task of authentication state by awaiting it. This is the message to be displayed in the alert. We’ll get the username from authState.User.Identity.Name. Finally calling the alert javascript function using IJSRuntime. We also need some changes in the navigation menu. We’ll be showing the menu options based on the user authentication state and user roles. Let’s show the counter menu option for both Administrator and User roles. But it will be hidden for Unauthenticated users. Then let’s show Fetch Data menu option only for Administrator Role users. Next in the MainLayout razor component, I’m doing some modifications. First let me inject AuthenticationStateProvider and NavigationManager dependencies. Then we can provide options for login and logout. For Unauthenticated users we’ll show a login option which will redirect the user to the login page and for Authenticated users we’ll show a logout option. So let’s implement the Logout onclick event in the code section First I’m casting the CustomAuthenticationStateProvider from the injected AuthenticationStateProvider as we did before. Then calling the UpdateAuthenticationState method by passing a null value as User Session parameter. Finally navigating the user to the home page. Next we are implementing the authorize attribute in the counter razor component. This is very important because even if the unauthenticated users cannot see the menu option, the users will be still able to access the page by entering the URL manually in browser’s address bar. In order to restrict that we must provide authorize attribute. So counter razor component can be accessed by users with Administrator and User roles. Fetch data razor component can only be accessed by users with Administrator roles. We must implement the Authorization header in the fetch data API request as we have provided Authorized attribute for WeatherForecast API in the server project. So let me inject the needed dependencies. Now in the OnInitializedAsync Method, I’m creating a custom authentication state provider object by casting it from the dependency object. Next I’m calling the GetToken method which we have implemented and assigning it to variable named token. After that we can just check whether the token is not null. If it is null we’ll redirect the user to the login page. We’ll call the API only if the token is not null. Just before that we must assign the Autherization header of the API request. The header value should be bearer and then the JWT token. Next final step what we need to do in the client project is providing the authorization dependency. So in the program.cs class, I’m adding ‘Builder.Services.AddAuthorizationCore. Now its time to run the application and let’s see all are working as expected. You can see now it is only showing Home option in the menu, the greeting message is Hello Guest and there is no greeting button visible. We can see a login option here. Let’s try to login now. I’m authenticating with the username, user. Password is also user. Now the greeting message changed, showing counter option in navigation menu and greeting button is also visible. Greeting button is triggering the alert with the username in the message. User can view the counter page. Let me try to manually provide the fetch data page URL in the address bar. It is showing unauthorized message as this user is not having the permission to access this page. Now we can logout and login with admin user. Providing the username and password. Both are admin Now FetchData is visible in the navigation menu. Greeting button showing Hello admin. Admin user can access counter page and fetch data page. And you can even see we are getting the data from the Weather forecast API. That means the API request is also authorized. Now I’ll show you the Session Storage value by opening the developer options. We can open the Application tab and inside session storage, we’ll be able to see the key-value. You can see the value for UserSession key. It is an encoded value which we are generating with our extension class. So everything works perfectly. So that's it for this video. Hope you liked it. Please subscribe, like and share  this video. See you all in the next video. Thank You!
Info
Channel: Coding Droplets
Views: 24,969
Rating: undefined out of 5
Keywords: blazor wasm authentication, blazor wasm identity server, blazor webassembly authorization, blazor webassembly jwt authentication, blazor webassembly custom authentication, blazor webassembly authentication and authorization, blazor webassembly authentication, blazor webassembly authentication with identity server, blazor webassembly authentication tutorial, blazor webassembly authorize roles, blazor wasm jwt, blazor webassembly jwt, blazor wasm authenticate, blazor tutorial
Id: 7P_eyz4mEmA
Channel Id: undefined
Length: 29min 34sec (1774 seconds)
Published: Tue Mar 22 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.