:toc: = How to use devon4net toc::[] == Introduction As you may know, https://devonfw.com/website/pages/docs/devonfw-guide_devon4net.wiki_master-devon4net.asciidoc.html[devon4net] is a framework that will assist you in getting started on your.NET applications and integrating high end configurable components. This article contains information in the form of a step-by-step guide on how to do a variety of operations. == How to: Create a new devon4net project In this part, you will learn you how to easily create a new project using devon4net libraries and start working on it. You can create a variety of applications thanks to the different templates available. There are also multiple ways to create a new project, we will show you the most common ways. NOTE: The configuration characteristics are not covered in this document. Please feel free to read the documentation for each component to discover how they work and the configuration options available. === Command Line Interface (CLI) This is the fastest and most efficient way. You will be installing and starting a project thanks to the template available in the NuGet Gallery using CLI. For this part you will need to have .NET 6.0 SDK installed. You can run the following command to check your version: [source, console] ---- > dotnet --version 6.0.102 ---- If you don't get any response please follow the https://docs.microsoft.com/en-us/dotnet/core/install/[installation guide] provided by Microsoft to install the latest version of .Net SDK for your OS. ==== Step 1 - Install the Template Open your favourite terminal (Windows/Linux/macOS) and run the command showed below to install the latest version of the the https://www.nuget.org/packages/Devon4Net.WebAPI.Template/[devon4net web API template]: [source, console] ---- > dotnet new --install Devon4Net.WebAPI.Template The following template packages will be installed: Devon4Net.WebApI.Template Success: Devon4Net.WebAPI.Template::6.0.3 installed the following templates: Template Name Short Name Language Tags ------------------------------- ------------ -------- ------------------------------ Devon4Net API solution template Devon4NetAPI [C#] devonfw/Devon4Net/Devon4NetAPI ---- We recommend you updating the template to the latest version. However, using the following option, you can select the version of your interest: [source, console] ---- > dotnet new --install Devon4Net.WebAPI.Template::6.0.3 ---- Now you will have the template available in your Visual Studio 2022. Just type `devon4net` in the search bar when creating a new project! .Devon4Net API template in VS2022 image::images/api_template_ide.png[] ==== Step 2 - Create a new project To create a new project run the following command: [source, console] ---- > dotnet new Devon4NetAPI The template "Devon4Net API solution template" was created successfully. ---- This will create a project with the default name in the actual directory. If you want to specify the desired name and output directory you can specify the following options: [source, console] ---- > dotnet new Devon4NetAPI --name MyProject --output C:\Projects\MyProject The template "Devon4Net API solution template" was created successfully. ---- You can do it also choosing the template when creating a new project in Visual Studio 2022 as shown in figure 1, and configuring the name and output directory as shown in figure 2. .Devon4Net API template in VS2022 image::images/api_template_configure.png[] ==== Step 3 - Run it After running it with Kestrel you will be able to access to the swagger `index.html` and try the API in the following link: https://localhost:8085/swagger/index.html[https://localhost:8085/swagger/index.html] === Create it from scratch in Visual Studio 2022 This method is a little more time consuming, but it allows for a more customized configuration and project structure. You will be using Visual Studio 2022 to create the project and add everything you need by hand. ==== Step 1 - Create a new project Create a new ASP.NET Core Web API project using the template provided by Visual Studio. You can type `api` in the search bar and select it as shown in figure 3. .ASP.NET Core Web API template in VS2022 image::images/api_template_create_project.png[] Once you go through all the initial configuration process, choosing a name, location and so on; you will find your project as shown in the next image. .Default ASP.NET Core Web API template structure image::images/api_template_initial_structure.png[] You can delete both `WeatherForecastController.cs` and `WeatherForecast.cs` as they are an example in the template but we recommend you keeping them so you can try the API when done with all the steps. ==== Step 2 - Add the NuGet reference To install the NuGet package for the API Configuration we will use the Visual Studio package manager console. To open it, go to `View > Other Windows > Package Manager Console` as shown in the figure below. .Package Manager Console location in menu image::images/api_template_package_manager.png[] Now you can run the following command. It will take a minute to download and install all the packages: [source, console] ---- PM> install-package Devon4Net.Infrastructure.WebAPI ---- Once its done, you should be able to see the dependency in the Package Dependencies of the project. ==== Step 3 - Set up your project Now you will need to add some configuration in the `Program.cs`. The following lines will initialize the configuration for the WebHostBuilder and configure the components that were imported with the NuGet installation respectively, making use of extensions methods for the `ServiceCollection` and `WebHostBuilder` classes: [source, c#] ---- builder.WebHost.InitializeDevonFw(); builder.Services.ConfigureDevonFw(builder.Configuration); ---- Now you'll need to configure the middlewares included with the following line: [source, c#] ---- app.SetupMiddleware(builder.Services); ---- NOTE: Don't forget to import the package to be able to use this methods! It is not necessary, but we recommend to also setup the logger so you can keep track of the trace running: [source, c#] ---- builder.Services.SetupLog(builder.Configuration); ---- The `Program.cs` will end up looking like this: [source, c#] ---- using Devon4Net.Application.WebAPI.Configuration; using Devon4Net.Application.WebAPI.Configuration.Application; using Devon4Net.Infrastructure.Middleware.Middleware; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); // devon4net builder.WebHost.InitializeDevonFw(); builder.Services.SetupLog(builder.Configuration); builder.Services.SetupDevonfw(builder.Configuration); var app = builder.Build(); app.UseHttpsRedirection(); // devon4net app.SetupMiddleware(builder.Services); app.UseAuthorization(); app.MapControllers(); app.Run(); ---- ==== Step 4 - Configure components The lines added on the previous step will need some configuration in the `appsettings.json`: [source, json] ---- { "devonfw": { "UseDetailedErrorsKey": true, "UseIIS": false, "UseSwagger": true, "UseXsrf": true, "UseModelStateValidation": true, "Environment": "Development", "ForceUseHttpsRedirection": false, "Kestrel": { "UseHttps": true, "HttpProtocol": "Http1AndHttp2", //Http1, Http2, Http1AndHttp2, none "ApplicationPort": 8085, "SslProtocol": "Tls12", //Tls12, Tls13, none. For Https2 Tls12 is needed "ExtraSettings": { "KeepAliveTimeout": 120, //in seconds "MaxConcurrentConnections": 100, "MaxConcurrentUpgradedConnections": 100, "MaxRequestBodySize": 28.6, //In MB. The default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB "Http2MaxStreamsPerConnection": 100, "Http2InitialConnectionWindowSize": 131072, // From 65,535 and less than 2^31 (2,147,483,648) "Http2InitialStreamWindowSize": 98304, // From 65,535 and less than 2^31 (2,147,483,648) "AllowSynchronousIO": true } }, "IIS": { "ForwardClientCertificate": true, "AutomaticAuthentication": true, "AuthenticationDisplayName": "" } } } ---- And also in the `appsettings.Development.json`: [source, json] ---- { "ExtraSettingsFiles": [ "appsettingsExtra.json", "Directory path", "Specific file name" ], "KillSwitch": { "UseKillSwitch": false, "EnableRequests": false, "HttpStatusCode": 403 }, "ConnectionStrings": { "Default": "Todos", "Employee": "Employee", "RabbitMqBackup": "Add your database connection string here for messaging backup", "MediatRBackup": "Add your databascere connection string here for messaging backup" }, "Certificates": { "ServerCertificate": { "Certificate": "", "CertificatePassword": "" }, "ClientCertificate": { "DisableClientCertificateCheck": true, "RequireClientCertificate": false, "CheckCertificateRevocation": true, "ClientCertificates": { "Whitelist": [ "3A87A49460E8FE0E2A198E63D408DC58435BC501" ] } } }, "Headers": { "AccessControlExposeHeader": "Authorization", "StrictTransportSecurityHeader": "", "XFrameOptionsHeader": "DENY", "XssProtectionHeader": "1;mode=block", "XContentTypeOptionsHeader": "nosniff", "ContentSecurityPolicyHeader": "", "PermittedCrossDomainPoliciesHeader": "", "ReferrerPolicyHeader": "" }, "Cors": [] } ---- === Unable to start devon4net template on macOS or older Windows Versions If you try to execute the devon4net template on macOS or an earlier version of Windows, such as Windows 7, you will receive the following error message: image::images/Exception-DevonWebAPI-MacEnvironment.png[] Because these operating systems lack ALPN(Application-Layer Protocol Negotiation) functionality, Kestrel does not handle HTTP/2 with TLS. To resolve this, navigate to 'appsettings.json' and change the Kestrel configuration as seen below: [source, json] ---- { "devonfw": { "UseDetailedErrorsKey": true, "UseIIS": false, "UseSwagger": true, "UseXsrf": true, "UseModelStateValidation": true, "Environment": "Development", "ForceUseHttpsRedirection": false, "Kestrel": { "UseHttps": true, "HttpProtocol": "Http1AndHttp2", //Http1, Http2, Http1AndHttp2, none "ApplicationPort": 8085, "SslProtocol": "none", //Tls12, Tls13, none. For Https2 Tls12 is needed "ExtraSettings": { "KeepAliveTimeout": 120, //in seconds "MaxConcurrentConnections": 100, "MaxConcurrentUpgradedConnections": 100, "MaxRequestBodySize": 28.6, //In MB. The default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB "Http2MaxStreamsPerConnection": 100, "Http2InitialConnectionWindowSize": 131072, // From 65,535 and less than 2^31 (2,147,483,648) "Http2InitialStreamWindowSize": 98304, // From 65,535 and less than 2^31 (2,147,483,648) "AllowSynchronousIO": true } }, "IIS": { "ForwardClientCertificate": true, "AutomaticAuthentication": true, "AuthenticationDisplayName": "" } } } ---- WARNING: HTTP/2 without TLS should only be used during app development. Production apps should always use transport security. === References Here are some interesting references to continue learning about this topic: * https://docs.microsoft.com/en-us/dotnet/core/install/[Install .NET on your OS - Microsoft Docs] * https://docs.microsoft.com/es-es/dotnet/core/tools/[.NET CLI overview - Microsoft Docs] * https://docs.microsoft.com/es-es/dotnet/core/tools/dotnet-new-install[dotnet new --install option - Microsoft Docs] * https://docs.microsoft.com/es-es/dotnet/core/tools/dotnet-new[dotnet new - Microsoft Docs] == How to: Create and add certificates to a project In this part, you will learn how to easily create a new certificate and properly add it to your devon4net project. === Create a certificate using OpenSSL In order to create our own certificate for development purposes we will be using https://github.com/openssl/openssl[OpenSSL] toolkit. To ensure correct behavior, make sure the tool is properly installed. NOTE: Please refer to the https://www.openssl.org/docs/man3.0/man1/[OpenSSL command documentation] to learn more about the commands used in this guide and how to install the toolkit. To run commands for OpenSSL, you will need to add OpenSSL to your environment, variables, or open a OpenSSL command prompt. NOTE: The working directory (directory where all files are created and readed) is the console actual path. Use `cd` command to go to your desired directory. ==== Step 1 - Create a Certificate Authority (CA) First we will need to create a Certificate Authority to sign the certificate. For that, we will run the following command which will create the certificate `RootCA.pem` and the corresponding private key `RootCA.key`. [source, console] ---- > openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.pem -subj "/C=ES/ST=Valencia/L=Valencia/O=Certificates/CN=MyProjectCertificate.local" ---- Now we will create the public key `RootCA.crt` for the certificate by running the following command: [source, console] ---- > openssl x509 -outform pem -in RootCA.pem -out RootCA.crt ---- If you want to export the certificate you can run the command: [source, console] ---- > openssl pkcs12 -export -out RootCA.pfx -inkey RootCA.key -in RootCA.crt ---- ==== Step 2 - Create a Certificate signed by the CA To create a new certificate run the following command: [source, console] ---- > openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=ES/ST=Valencia/L=Valencia/O=Certificates/CN=localhost.local" ---- Before signing it, create a `domains.ext` that contains the following: [source, txt] ---- authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment subjectAltName = @alt_names [alt_names] DNS.1 = localhost DNS.2 = localhost.local DNS.3 = 127.0.0.1 DNS.4 = fake1.local DNS.5 = fake2.local ---- Once the files are created, you'll need to sign the certificate with the CA we created earlier: [source, console] ---- > openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -extfile domains.ext -out localhost.crt ---- Run the next command to export the certificate: [source, console] ---- > openssl pkcs12 -export -out localhost.pfx -inkey localhost.key -in localhost.crt ---- You will end up having something like this: .Certification Authority (left) and localhost certificate signed by CA (right) image::images/certificates.png[] === Add certificates to a devon4net project Once you have created a certificate or in case you already have yours, you can add it to your project thanks to devon4net tools. ==== Step 1 - Add it to your project Locate the Certificates directory in your startup project. If it doesn't exist, please create it and drop your certificate `.pfx` as shown in figure 2. .Certificates directory in startup project image::images/certificates_add.png[] ==== Step 2 - Configure your appsettings Now configure your certificate in `appsettings.Development.json`. For that, you'll need to specify the file name and the password you chose. Look for the `ServerCertificate` configuration and add something like this: [source, json] ---- "Certificates": { "ServerCertificate": { "Certificate": "localhost.pfx", "CertificatePassword": "12345" }, "ClientCertificate": { "DisableClientCertificateCheck": true, "RequireClientCertificate": false, "CheckCertificateRevocation": true, "ClientCertificates": { "Whitelist": [ "3A87A49460E8FE0E2A198E63D408DC58435BC501" ] } } }, ---- === References Here are some interesting references to continue learning about this topic: * https://github.com/openssl/openssl[OpenSSL] * https://www.openssl.org/docs/man1.0.2/man1/openssl-req.html[`req` command documentation - OpenSSL Docs] * https://www.openssl.org/docs/man1.0.2/man1/x509.html[`x509` command documentation - OpenSSL Docs] * https://www.openssl.org/docs/man3.0/man1/pkcs12.html[`pkcs12` command documentation - OpenSSL Docs] == How to: Setup JWT As you may have learned at this point you can set up JWT component in a number of different ways according your needs. For that you'll need to configure your `appsettings.json`. NOTE: Please read documentation about JWT component first to learn what you need to do to use it in your project. Assuming that you already have the JWT component correctly installed and available in our project let's start thinking about how we can put it to good use. === Configuration We can configure it to work either with a secret key or a certificate. If you choose certificate, you will need to add a certificate to your project, and specify the password and the encryptionAlgorithm used. You can learn how to do it following the tutorial included in this document. If you specify both, the secret key will take priority. For example lets specify the next: .JWT configuration example image::images/json_jwt_example.png[] NOTE: The property `SecretKey` needs to be an encrypted key using the algorithm specified. This would create the following configuration: * A token with audience and issuer equal to `devon4net`. * It will expire in 60 minutes * It will validate the signature and if the token is valid in time * It will require tokens that are signed, and have both expiration time and audience specified. * It will use the secret key encrypted with SHA 512 === Claims Json Web Tokens work with claims. A Claim is a piece of information about a subject. It is similar to a key-value pair, where the value will be the claim type, such as the name or the role of an authenticated user. This claims are stored inside a JSON and then encrypted forming the JWT. In .Net we can create Claims using the `Claim` class avaiable in `System.Security.Claims`. It has many constructors but the most important is the following one, where you can create a Claim based on two strings. [source, c#] ---- var nameClaim = new Claim(ClaimType.Name, "DevonUser"); var roleClaim = new Claim(ClaimType.Role, "Administrator"); ---- You can choose between a variety of claim types thanks to the `ClaimType` class. As you can see in the previous piece of code, in this case we have asserted a name and a role in two claims. This could be for a user, for example. === JwtHandler In JWT component we have a handler that is cofigured on the installation of the package and can be injected for use in any wanted service. This is the `JwtHandler`. This handler will allow us to manipulate, encrypt and extract information from Json Web Tokens. |==== |*Return Type* |*Method Name* |*Parameters* |*Description* |string |CreateJwtToken |List clientClaims |Returns the encrypted jwt given a list of claims. |List |GetUserClaims |string jwtToken |Returns a list of claims given an encrypted token. |string |GetClaimValue |List claimList, string claim |Returns the value of a claim given a list of claims and the type of the claim to recover formatted as a string. |string |GetClaimValue |string token, string claim |Returns the value of a claim given an encrypted token and the type of the claim to recover formatted as a string. |SecurityKey |GetIssuerSigningKey |- |Returns the issuer's signing key. |bool |ValidateToken |string jwtToken, out ClaimsPrincipal claimsPrincipal, out SecurityToken securityToken |Returns true if the token is valid. |string |CreateRefreshToken |- |Creates a refresh token for the JWT token. |==== === Video === References Here are some interesting references to continue learning about this topic: * https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims[JSON Web Token Claims - auth0] * https://docs.microsoft.com/es-es/dotnet/api/system.security.claims.claim?view=net-6.0[Claim Class - Microsoft Docs] * https://docs.microsoft.com/es-es/dotnet/api/system.security.claims.claimtypes?view=net-6.0[ClaimTypes Class - Microsoft Docs] == How to: Setup security and roles in API controllers In this part of the document, you will learn to use the different attributes over the controller methods that manage end-points. This attributes are provided by .Net core libraries and can be used to specify the behavior of Web API controllers and action methods. === Attributtes You can use a large number of attributes, some are optional, for example to define the route of end-points `[Route("/GetSomething")]` and other are required, like `[ApiController]` to indicate that the class is an API controller. NOTE: We will be explaining the security related attributes. Those that are specific to the controllers will not be mentioned. ==== [HttpOptions] This attribute identifies an API controller end-point that support the HTTP OPTIONS request. The HTTP OPTIONS method is used to get information about the communication options available for a specific URL or server. NOTE: Please do your research on this method if you are not familiar with it. ==== [AllowAnonymous] `AllowAnonymous` allows any type of user (authorized or unauthorized) to access the information provided by the end-point. This attribute can be specified for controller class or for individual end-points. Specifying it for individual end-points will override the controller attribute. An example could be: [source, c#] ---- [HttpGet] [AllowAnonymous] [Route("/v1/getsomething")] public async Task GetSomething() { ... } ---- ==== [Authorize] `Authorize` only enables you to restrict access to requests with an authorization specified in the header. This attribute can be specified for controller class or for individual end-points. Specifying it for individual end-points will override the controller attribute. You can specify different properties to the attribute: |==== |*Property* |*Type* |*Description* |*Example* |`AuthenticationSchemes` |List of strings separated by comma |List of schemes from which user info is constructed |`[Authorize(AuthenticationSchemes = "Bearer")]` |`Policy` |String |Policy name that determines access to the resource |`[Authorize(Policy = "MyPolicy")]` |`Roles` |List of strings separated by comma |List of roles allowed to access |`[Authorize(Roles = "User")]` |==== For example, lets create a controller that is authorized only for users with role 'Admin' and 'Tester' provided in 'Bearer' type authentication: [source, c#] ---- [ApiController] [Route("[controller]")] [Authorize(AuthenticationSchemes = "Berarer", Roles = "Admin,Tester")] public class DebugController: ControllerBase { ... } ---- ==== [EnableCors] & [DisableCors] NOTE: Please refer to the CORS component documentation to learn everything about CORS. You can enable a Cors policy for controller or individual end-points. Specifying it for individual end-points will override the controller attribute. You will need to specify the policy you want to enable. This policy will need to be described in the `appsettings.{environment}.json`. For example, lets create a CORS policy named 'CorsPolicy' and enable it for a controller, and disable it for a method: [source, json] ---- "Cors": //[], //Empty array allows all origins with the policy "CorsPolicy" [ { "CorsPolicy": "CorsPolicy", "Origins": "http://localhost:4200,https://localhost:4200,http://localhost,https://localhost;http://localhost:8085,https://localhost:8085", "Headers": "accept,content-type,origin,x-custom-header,authorization", "Methods": "GET,POST,HEAD,PUT,DELETE", "AllowCredentials": true } ], ---- [source, c#] ---- [ApiController] [Route("[controller]")] [EnableCors("CorsPolicy")] public class MyController: ControllerBase { ... [HttpGet] [Route("/v1/getsomething")] [DisableCors] public async Task GetSomething() { ... } ... } ---- === References Here are some interesting references to continue learning about this topic: * https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-6.0[Introduction to authorization in ASP.NET Core - Microsoft Docs] * https://docs.microsoft.com/es-ES/dotnet/api/microsoft.aspnetcore.authorization?view=aspnetcore-6.0[Authorization Namespace - Microsoft Docs] * https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS[HTTP OPTIONS - MDN Web Docs] == How to: Configure and use Keycloak server === What is Keycloak? Keycloak is an open-source tool that provides identity and access management providing highly secure properties to your application. It supports multiple protocols such as OAuth and OpenID. It has full support for SSO (Single Sign-On and Single Sign-Out) and third party Social Identity Providers such as Google, Twitter, Facebook... You can also connect it to some type of user database if you already have one for your client. This allows you to interconnect multiple applications through same users and data. One of the most interesting features is the GUI available through and end-point that allows you to configure and manage your Keycloak settings and properties. Long story short, Keycloak is a server that manages all your user related interactions, such as roles, groups, access tokens... so you can focus in developing your application and don't have to reinvent the wheel. === Docker Image For this example we will be running the Keycloak docker image and exposing it in a local port. NOTE: Please make sure you have Docker installed in your machine before running the command. To start the Keycloak Server run the following command: [source, console] ---- docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:18.0.1 start-dev ---- WARNING: This whole guide is meant to be used with the Keycloak Server version `18.0.1`. It may not work in the same way in future versions. This will start the keycloak server in a docker image. It will be availabe in your localhost port 8080. As you can also see by the command, it will create an initial admin user with username `admin` and password `admin`. You can sign in navigating to the address http://localhost:8080/admin/[localhost:8080] and using this initial credentials. .Keycloak Sign In image::images/keycloak_sign_in.png[] === Realms In Keycloak, a realm is the same as an environment. It enables the creation of exclusive groups of users and applications. In Keycloak, there is just one realm by default, named master. This is for managing Keycloak only, therefore don't use it for your own programs. To create a realm you can hover the small arrow where it says `Master` in the dashboard and click on `Add realm`. .Create a realm in keycloak image::images/keycloak_create_realm.png[] Choose a name, for this example we will name it `MyRealm`, and then click `Create`. Now when your realm is selected, you will be able to configure it through the `Realm Settings` tab in the left side menu. NOTE: Please visit the Keycloak documentation to learn more about how you can configure it and what options do you have. === Clients A client, in Keycloak is equivalent to an application. You can add multiple applications and all can use the same users, groups, roles... .Create a client in keycloak image::images/keycloak_create_client.png[] Now fill the form choosing at least a required client id. You can also select the protocol you will be using: * `OpenID connect` allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server. * `SAML` enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information. .Create a client in keycloak image::images/keycloak_set_client.png[] === Users To create a new user follow the next steps: 1. Log into the http://localhost:8080/admin/master/console/#/realms[Keycloak admin console] 2. Select the Realm in which you want to create users: + .Keycloak administrator console image::images/keycloak_user_1.png[] 3. Navigate to the *Users* section (1) in the left-side bar, then the *Add user* button (2): + .Add a user in Keycloak image::images/keycloak_user_2.png[] 4. Complete the fields (1) and click the *Save* button (2): + .Add a user in Keycloak image::images/keycloak_user_3.png[] + - The only required field is the username. - The required user actions field refers to some action that the user must perform upon first logging in. 5. Set a password for the user: + .Set credentials to a user in Keycloak image::images/keycloak_user_4.png[] + - Navigate to the *Credentials* tab (1). - Enter a password (2). - Confirm the password (3). - (*Optional*) Change the switch to on if you want the user to change their password after the first login (4). - Click the *Set Password* button to confirm the changes (5). === Groups Groups in Keycloak allow you to manage a common set of attributes and role mappings for a set of users. Let's create a new group and add some users inside: 1. Click the *Groups* section (1) in the left-side bar, then the *New* button (2): + .Create a group in Keycloak image::images/keycloak_group_1.png[] 2. Insert the name of the group and then click the *Save* button: + .Create a group in Keycloak image::images/keycloak_group_2.png[] 3. Groups are hierarchical. A group can have many subgroups, but a group can only have one parent. Subgroups inherit the attributes and role mappings from the parent. This applies to the user as well. By clicking in a group (1) and then clicking the *New* button (2), you can create a subgroup. + .Create a subgroup in Keycloak image::images/keycloak_group_3.png[] + For example, you could have something like this: + .Create a group in Keycloak image::images/keycloak_group_4.png[] 4. Add users to the groups: - Navigate to the *Users* section (1) in the left-side bar, then select the user you want to add to a group and click on the *Edit* button (2): + .Add users to a group in Keycloak image::images/keycloak_group_5.png[] - Click the *Groups* tab (1), select the group in which the user should be added (2), and then click the *Join* button (3): + .Add users to a group in Keycloak image::images/keycloak_group_6.png[] - You will see something like this: + .Add users to a group in Keycloak image::images/keycloak_group_7.png[] === Roles ==== Realm Roles Realm-level roles are a global namespace to define your roles. To add a new Real Role you have to: 1. Navigate to the *Roles* section in the left-side bar (1), and click the *Add Roles* button (2): + .Create a new Realm Role in Keycloak image::images/keycloak_role_1.png[] 2. Input a role name and click the *Save* button: + .Create a new Realm Role in Keycloak image::images/keycloak_role_2.png[] - The description field is not required 3. Now you may add users to that role as follows: - Navigate to the *Users* section (1) and select the *Edit* button (2) for the user to whom you want to assign the role: + .adding a Realm Role to a user in Keycloak image::images/keycloak_role_3.png[] - Select the *Role Mappings* tab (1), select the Role you want to assign (2) and then click the *Add selected* button (3): + .adding a Realm Role to a user in Keycloak image::images/keycloak_role_4.png[] ==== Client Roles Client roles are basically a namespace dedicated to a client. Each client gets its own namespace. Client roles are managed under the Roles tab under each individual client: .Creating a Client Role in Keycloak image::images/keycloak_role_5.png[] Input a role name and click the *Save* button: .Creating a Client Role in Keycloak image::images/keycloak_role_6.png[] For adding the Client Role to an user: Navigate to the *Users* section (1) and select the correct user to asign the role, select the *Role Mappings* tab (2), in the *Client Roles* section select the Client (3), select the desired Role to assign (4) and click the *Add selected* button (5) .adding a Client Role to a user in Keycloak image::images/keycloak_role_7.png[] === Hardcoded audience When your service relies on realm roles or does not rely on the token's roles at all, a hardcoded audience can be useful. A hardcoded audience is a protocol mapper that adds the client ID of the specified service client to the token as an audience. If you want to use a different audience than the client ID, you can use any custom value, such as a URL. The protocol mapper can be added directly to the frontend client. When the protocol mapper is directly added, the audience is always added as well. For better control over the protocol mapper, you can create the protocol mapper on the dedicated client scope: 1. Go to the *Client Scopes* section in the left-side bar. 2. Enter the name for the client scope. 3. Click the *Save* button. + .adding a Client Scope in Keycloak image::images/keycloak_client_scope_1.png[] For create the mapper itself follow the nexts steps: 1. Go to the Mappers tab. 2. Click the Create button. + .adding a mapper to a Client Scope in Keycloak image::images/keycloak_client_scope_2.png[] In the next screen configure the Mapper as follows: 1. Enter the name of the mapper. 2. In the Mapper type select `Audience` 3. Click the *Save* button. + .adding a mapper to a Client Scope in Keycloak image::images/keycloak_client_scope_3.png[] === Keycloak Sample This template allows you to understand the *authentication* and *authorization* process using Keycloak: * *Authentication*: Is the process of confirming that someone or something is who they claim to be. To secure access to an application or its data, most technology systems employ some form of authentication. When you need to access an online site or service, for example, you usually have to enter your username and password. Then, in the background, it compares the username and password you entered to a record in its database. If the information you provided matches, the system considers you a valid user and grants you access. * *Authorization*: Is the security process that determines a user or service's level of access. In technology, we use authorization to give users or services permission to access some data or perform a particular action. Authentication verifies the user (Lucia) before allowing them access, and authorization determines what they can do once the system has granted them access (view sales information, for example). In the `Download` section you will find a sample that you can use as a template for integrating Keycloak in your application. NOTE: Please download the template so you can follow the guide successfully. The template shows an example of a School with two types of entities, `Student` and `Teacher`, the teachers also have the Administrator role, that allows to access to their `Student list`. In the application you will be able to authenticate yourself through a username and password, and this will give you an authorization token that will allow you to use the application with a certain role. ==== Keycloak Setup To setup the keycloak server we provide the `realm_export.json` file that is the configuration of the server that we need to use the template. For import the Realm follow the steps below: image:images/keycloak_import_realm.png[] .Import a realm in keycloak image::images/keycloak_import_realm_2.png[] 1. Create a new Realm (1) 2. Click in the *Select file* button and choose the `realm_export.json` file (2) 3. Enter a name for your Realm and click the *Create* button. The next step is to create users in your realm for that follow the next steps: 1. Navigate to the *Users* section (1) in the left-side bar, then the *Add user* button (2): + .Add a user in Keycloak image::images/keycloak_user_5.png[] 2. Complete the fields (1) and click the *Save* button (2): + .Add a user in Keycloak image::images/keycloak_user_6.png[] 3. Set a password for the user: + .Set credentials to a user in Keycloak image::images/keycloak_user_7.png[] + - Navigate to the *Credentials* tab (1). - Enter a password (2). - Confirm the password (3). - (*Optional*) Change the switch to on if you want the user to change their password after the first login (4). - Click the *Set Password* button to confirm the changes (5). For this example, we created two user types,`student` and `teacher`: .Users for the template image::images/keycloak_user_8.png[] Now we are going to assign to the user teacher, the `Administrator` Role, for that follow the nexts steps: Navigate to the *Users* section (1) and select the correct user to asign the role, select the *Role Mappings* tab (2), in the *Client Roles* section select the `SchoolApplication` Client (3), select the Administrator Role (4) and click the *Add selected* button (5) .adding a Client Role to a user in Keycloak image::images/keycloak_role_7.png[] NOTE: If you need more information about how to configure the Keycloak server please refer to the `How to: Configure Keycloak server` section. ==== Devon4net.Application.Keycloak This is the startup project of the template. Its main purpose is to launch the api and configure it using the settings files. The following figure shows the directory tree of the application: .Devon4net.Application.Keycloak Directory tree image::images/keycloak_application_directory.png[] * *Controllers*: This template, has two controllers: the `KeycloakController` for obtaining the access token and the `SchoolController` for testing that token and the user roles. * *Model*: The model objects of the application. This template is about a School so we have Persons that are also Students or Teachers. The Teachers will have the `Administrator` role aswell. ===== Configuration The configuration is done in the `appsettings.json` file. It can contain subversions following the structure `appsettings.{environment}.json`. For the keycloak sample, the configuration is done as follows: [source, json] ---- { "Keycloak": { "Realm": "MyRealm", "Url": "http://localhost:8080/", "ClientId": "SchoolApplication", "GrantType": "password" } } ---- * `Realm`: Your Keycloak realm's name. * `Url`: The direction of your Keycloak server. * `ClientId`: The Id of the Client that you created in the Keykloak server administrator console. * `GrantType`: The grant type used to obtain the token. ===== Controllers ====== `KeycloakController` It only has one method with the `AllowAnonymous` annotation, that means that this method doesn't need authorization. [source, c#] ---- [HttpGet] [AllowAnonymous] public async Task Login(string username, string password) { return Ok(await _keycloakService.GetToken(username, password).ConfigureAwait(false)); } ---- The `Login` method calls the Keycloak Service to retrieve the token, it has two parameters, the username and the password. .Keycloak Controller image::images/keycloak_access_token.png[] ====== `SchoolController` Once we have the access token we can use the methods of this controller, for that we have to authorize ourselves: image:images/keycloak_authorize_1.png[] .Authorization with the access token image::images/keycloak_authorize_2.png[] There are two methods available for this controller: * `GetStudentSubjects()`: For this method, the user should be authorized; either students or teachers can use it. * `GetTeacherStudents()`: This method access is restricted to teachers only, as indicated by the following policy: + [source, c#] ---- [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "SchoolPolicy")] ---- + This policy specifies that only teachers with the `Administrator` role are permitted to use this method. ==== Devon4Net.Application.Keycloak.Implementation This package contains all the implementation classes. You will find the following directory tree: .Package directory tree image::images/keycloak_implementation_tree.png[] We have organized the files as if it was a Devon4Net module package: * Configuration: Contains all the files necesary for configuration. You can use realm-export.json to import the keycloak configuration for this specific example. + You will find in the KeycloakConfiguration class how you can add the different policies and the authorization and authentication configuration. + For example you can use the following code to add a policy that requires the claim user_roles as Administrator: + [source, c#] ---- services.AddAuthorization(options => { options.AddPolicy("SchoolPolicy", policy => policy.RequireClaim("user_roles", "Administrator")); }); ---- * Model: Contains the model objects. In this case we only need the token, which is the response of keycloak but we could put here also the user, for example, if we were to retrieve the information from keycloak. * Options: This directory contains the model for all the options used to configure. * Services: This directory contains all the services. There is only one service, which access keycloak through the end-points available. + In this example we only need the token, so there is a method accessing the token end-point from keycloak. You can see the end-points available in keycloak in its dashboard. === Downloads * link:resources/samples/keycloak/Keycloak.zip[Keycloak Sample] === References * https://www.keycloak.org/getting-started/getting-started-docker[Getting Started (Docker) - Keycloak Docs] == How to: Avoid cross-site request forgery (XSRF or CSRF) attacks Every time you authenticate into a web app, your browser becomes trusted for an amount of time to that specific site. This way you can use the application without having to authenticate yourself each time you want to do an operation that requires authorization. This is achieved normally by providing authentication tokens that last a determinate amount of time. Your browser includes in each request a token issued by the app at the moment of authentication, the app verifies it and then sends back the response. A cross-site request forgery is a type of attack where a malicious site can use your authentication token to issue requests through your browser. This is done, without you even noticing and it works because browser requests automatically include all session cookies. So if the request is done from another site with all these cookies (including your authentication cookie) it wont be different from you actually doing the request. For example, you enter the site `vulnerable-bank.com` and forget to log out after doing all of your operations. Now you are surfing the net and enter the site `malicious-site.com` this site has a hidden script that performs the following request: [source] ---- POST /transaction HTTP/1.1 Host: vulnerable-bank.com Content-Type: application/x-www-form-urlencoded Cookie: SessionID = 1n4j0sd7uohpf13j98nh570923c48u account={MaliciousAccount} amount=1000 ---- As all the session cookies are included in the request, the site will be allowed to process the transaction without you even noticing. The most common way to prevent this vulnerability is by making use of anti forgery tokens. This token is placed in the headers and is issued with the request. The malicious-site cannot === Configuration To configure Anti-Forgery protection in a devon4net project, you must set `UseXsrf` property in `devonfw` configuration section to true. You will find this section in `appsettings.json`: [source, json] ---- { "devonfw": { "UseXsrf": true } } ---- Setting this property to true will allow you to use https://docs.microsoft.com/es-es/dotnet/api/microsoft.aspnetcore.antiforgery.iantiforgery?view=aspnetcore-6.0[`IAntiforgery`] interface through the service provider and dependency injection in your project. === Use In the devon4net Web API Templaate you will be able to find an example of controller using this protection. You can inject the `IAntiforgery` interface in the controller constructor like so: [source, c#] ---- private readonly IAntiforgery _antiForgeryToken; public AntiForgeryTokenController(IAntiforgery antiForgeryToken) { _antiForgeryToken = antiForgeryToken; } ---- Now you can access its methods to manage this special token. For creating one we can use `GetAndStoreTokens(HttpContext httpContext)` method available on `IAntiforgery`, and store it in the cookies as shown below: [source, c#] ---- var token = _antiForgeryToken.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append(CustomMiddlewareHeaderTypeConst.XsrfToken, token.RequestToken); ---- NOTE: Visit the documentation about this interface to find more information about `IAntiforgery` methods. This will be done through a GET request. Now we can vaklidate this token in a controller thanks to the attribute `[ValidateAntiForgeryToken]`: [source, c#] ---- [HttpGet] [Route("/v1/antiforgeryToken/hellosecured")] [ValidateAntiForgeryToken] public ActionResult HelloSecured() { return Ok("You have reached a secured AntiForgeryToken method!"); } ---- === Testing We will be using https://www.postman.com/[Postman] to test the use of the controller, as you can see in the next figure, if we try to access the end-point protected by the antiforgery token validation we get an error: .HelloSecured end-point error image::images/anti_forgery_hellosecure_error.png[] That is beacuse no valid token in `XSRF-TOKEN` header is specified. Lets access the end-point we created to provide an XSRF token: .Get XSRF token end-point image::images/anti_forgery_token.png[] Now that we have the token we can specify it in the request header as shown in the next figure: .HelloSecured end-point access image::images/anti_forgery_hellosecure_xsrf.png[] === References Here are some interesting references to continue learning about this topic: * https://owasp.org/www-community/attacks/csrf[Cross Site Request Forgery (CSRF) - Owasp] * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html[Cross Site Request Forgery Prevention Cheat Sheet - Owasp] * https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-6.0[Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core - Microsoft Docs] * https://docs.microsoft.com/es-es/dotnet/api/microsoft.aspnetcore.antiforgery.iantiforgery?view=aspnetcore-6.0[IAntiforgery Interface - Microsoft Docs] == How to: Import a database In this part of the document you will learn how to easily import a database to your devon4net project or any .Net project. This process is known by a variety of different names: scaffolding, database first, reverse engineering... But they all refer to the process of creating entity models and/or database context automatically based on a database schema. === Prerequisites You can import a database in your favourite type of schema, but for this example we will be using SQL Server and Visual Studio 2022. NOTE: Ensure that you have a connection ready to your database and a correct installation of Visual Studio 2022. For this example we used https://docs.microsoft.com/es-es/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15[Microsoft SQL Server Management Studio] to create our database and provide us with a local database connection. === NuGet packages installation Before starting with the scaffolding you will need to install the following NuGet packages in your destination project. ==== Open the Package Manager Console To open the Package Manager Console, follow the next menu steps: `View > Other Windows > Package Manager Console` and then ensure that the default project in the console is set to the destination project as shown in the next figure. NOTE: If the destination project is not the same as the startup project, you may need to install some of the packages in the startup project too. The package manager console will warn you of that if it is necessary. .Package Manager Console default project image::images/pacakge_manager_console.png[] ==== Install the packages * Microsoft.EntityFrameworkCore.Tools [source, console] ---- install-package Microsoft.EntityFrameworkCore.Tools ---- * Microsoft.EntityFrameworkCore.Design [source, console] ---- install-package Microsoft.EntityFrameworkCore.Design ---- * MIcrosoft.EntityFrameworkCore.SqlServer + NOTE: As we mentioned we will be using a SQL Server schema, we are installing the database provider for it. Please choose your own https://docs.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli[Database Provider]. + [source, console] ---- install-package Microsoft.EntityFrameworkCore.SqlServer ---- === Scaffolding command Run the following command to execute the scaffolding operation: [source, console] ---- scaffold-dbcontext 'Server=(localdb)\mssqllocaldb;Database=DevonDatabase;' Microsoft.EntityFrameworkCore.SqlServer -Tables School,Teacher -ContextDir Domain\Database -OutputDir Domain\Entities ---- NOTE: Your connection string and database provider may be different. * The first argument is the connection string. * The second argument is the Database Provider package for the used schema. * `-Tables {Table list separated by comma}` is the list of the tables you want to scaffold. * `-ContextDir` will specify the relative path for the context. * `-OutputDir` will specify the relative path for the models. In our case, before doing the scaffolding we had something like this: .Directory tree before scaffolding image::images/before_scaffolding.png[] And the scaffolding produced the following directory tree: .Directory tree after scaffolding image::images/after_scaffolding.png[] If you do not specify `-Tables`, `-ContextDir` and `-OutputDir` all of the tables will be scaffolded and the default directories will be the project directory. === Add the context for Dependency Injection Now that we have our context we will need to add it either in `Program.cs` or `SetupDatabase` method in `DevonConfiguration` class. As we are using Devon, we will need to go to `Devon4Net.Application.WebAPI.Implementation.Configuration.DevonConfiguration` and add the folowing line in `SetupDatabase` method: [source, c#] ---- services.SetupDatabase(configuration,"DevonDatabase", DatabaseType.SqlServer).ConfigureAwait(false); ---- Where: |=== |*Parameter* |*Description* |`` | Database context you want to add |`configuration` | Available `IConfiguration` instance |`"DevonDatabase"` | Name of the connection string defined at `ConnectionString` section in the `appsettings.{environment}.json` configuration file |`DatabaseType` | Database schema available in devon (see the following list). |=== List of supported databases: * SqlServer * Sqlite * InMemory * Cosmos * PostgreSQL * MySql * MariaDb * FireBird * Oracle * MSAccess === References * https://docs.microsoft.com/en-us/ef/core/managing-schemas/scaffolding?tabs=vs[Reverse Engineering - Microsoft Docs] * https://docs.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli[Database Providers - Microsoft Docs] == How to: Use LiteDb In this part you will learn how to easily start using a LiteDb database in your project. NOTE: Please read the documentation of this component to learn more about how to set it up and use it. === Video As you will find all the information in the component documentation, we prepared a video using LiteDb in a very short example: .How to setup and use LiteDb component video::videos/howto_litedb.mp4[] == How to: Customize Headers HTTP headers let the client and the server provide additional information with an HTTP request or a response. As this headers provide information about either the client or the server, it can be dangerous if this information lands in the wrong hands. As Owasp explains in great detail, proper HTTP headers can help prevent security vulnerabilities like Cross-Site Scripting, Clickjacking, Information disclosure and more. In devon we take security very seriously, that's why we developed a very easy form of customizing HTTP headers. You will be able to do it configuring the headers in the `appsettings.{environment}.json` file. A middleware will be configured with those options and will modify each HTTP response according to the specified options. === Configuration [source, json] ---- "Headers": { "AccessControlExposeHeader": "Authorization", "StrictTransportSecurityHeader": "", "XFrameOptionsHeader": "DENY", "XssProtectionHeader": "1;mode=block", "XContentTypeOptionsHeader": "nosniff", "ContentSecurityPolicyHeader": "", "PermittedCrossDomainPoliciesHeader": "", "ReferrerPolicyHeader": "" }, ---- The following table shows the options that can be configured. NOTE: Please refer to the links provided to learn more about each header and what can be done with it. |==== |*Option* |*Header* |*Description* |`AccessControlExposeHeader` |https://developer.mozilla.org/es/docs/Web/HTTP/Headers/Access-Control-Expose-Headers[`Access-Control-Expose-Headers`] |Indicates which headers may be exposed as part of the response by listing their names. |`StrictTransportSecurityHeader` |https://developer.mozilla.org/es/docs/Web/HTTP/Headers/Strict-Transport-Security[`Strict-Transport-Security`] |Allows a website to tell browsers that it should only communicate with HTTPS instead of using HTTP. |`XFrameOptionsHeader` |https://developer.mozilla.org/es/docs/Web/HTTP/Headers/X-Frame-Options[`X-Frame-Options`] | Can be used to indicate whether a browser should be allowed to render a page in a ``, `