-
Notifications
You must be signed in to change notification settings - Fork 10
Using Monaco with Keycloak
First, we need to install the Monaco Template.
dotnet new install Monaco.Template
The following command will show us all the available options within the template:
dotnet new monaco-backend-solution --help
We are going to need these:
To create a solution named MonacoWithKeyCloak
that contains the
configuration for Keycloak and a json file with the realm settings, we
will use the following command:
dotnet new monaco-backend-solution -n MonacoWithKeyCloak -kc -kr monaco-realm
Now the solution has been created.
These are the files that are different from a vanilla usage of Monaco template
In the solution file we will find a new folder called KeyCloak
which
contains the json file with the realm configuration:
The appsettings file in the Api project has endpoints configured to match the name we gave to the realm:
And the realm configuration json file itself:
For this tutorial we will run Keycloak inside a container. Here you can find very useful information for this purpose.
For now, we will just use Keycloak in development mode:
docker run --name monaco-keycloak-tutorial -p 8080:8080 -e
KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=P@ssw0rd
quay.io/keycloak/keycloak:latest start-dev
Keycloak is now up and running. We can navigate to http://localhost:8080 where we will be prompted for the admin credentials we just used in the "docker run" command.
Now it's time to create a new realm by importing the json file provided by the Monaco Template. You will find it in the solution directory.
In the realm selector, click on "Create realm"
Drag and drop the json file to the "Resource file" area
Now you can review the json contents and modify the name of the Realm. In this example we'll leave it as is.
The realm is now created, and you can select it in the realms dropdown
At this point, let's see what happens if we try to call an endpoint in our solution.
Open the solution and run the Api project. We will be presented with the Swagger OpenApi user interface.
If we try to execute a call to GET Companies then we receive a 401 status code, since that endpoint requires the caller to
be authenticated and have the companies:read
scope.
This means that we need to authorize the caller.
If we click on "Authorize"
The following dialog will appear:
We leave the default "client id" and select at least the scope that we need for this example, companies:read
, and click Authorize.
We are now redirected to the Keycloak login page where we are prompted for user credentials.
At this point we only have the "admin" user in Keycloak and that is not a valid user for client authentication.
We need to create a new valid user.
Go to "Users", "Create new user"
Write an email for the user (no need to be real) and switch on "Email verified". Click create.
A message will appear at the top indicating that the user has successfully been created.
Now we need to set a password for the user. Click on the "Credentials" tab and set a password
The User is now created and can be authenticated, but we still need to give it the right access permissions.
We need to give the User a Role that contains the companies:read
scope.
Click on Users in the left menu and click on our user [email protected]
.
Filter by clients
Filter by "Customer" role and select the role. Click on Assign
Now we can successfully log in through Swagger
If we try again calling the GET Companies method, it will no loger return a 401 or 403.
In this example we have not set a database in the Monaco solution, so it will return a 500 error when trying to connect to the database.
Copy the Access Token from the Authorization Header
And paste it into a token decoder, like https://jwt.io/
Let's see some interesting points:
The field Authorized Parties (azp) is set to monaco-realm-api-swagger-ui
because it is the client_id that we have
used when authorizing in Swagger.
But the field Audience (aud) is set to monaco-realm-api
, and therefore the roles field contains the role Customer
, and the
scope field contains companies:read
(remember that we assigned the Customer Role of client monaco-realm-api
to our User).
Why are we getting the scopes and roles from a client different than the one we have set while authorizing?
This is because Keycloak allows the configuration of protocol mappers that can affect how the "aud" is composed. The "Audience Template Mapper" can be used to add static audiences to both the access token and the ID token. This allows us to explicitly specify the clients that should be included in the "aud" field.
So, in Keycloak, if we go to the client monaco-realm-api-swagger-ui
, and to the tab "Client scopes" we will see that it contains by default the client scope monaco-realm-api
.
Now go to "Client scopes" and select "monaco-realm-api".
Firstly, it is configured to be added as a "scope" in the token
Secondly, in Mappers tab, it contains a mapper of type "Audience"
Which finally is configured to include the client monaco-realm-api
in the "aud" field of the access token
So, in summary, this feature allows us to share the configuration of one client within other clients, which avoids the need for duplication.
- Index
- Getting started
- Architecture overview
-
Solution/Projects structure
- Solution structure
-
Projects structure
Monaco.Template.Common.Api
Monaco.Template.Common.Api.Application
Monaco.Template.Common.ApiGateway
Monaco.Template.Common.Application
Monaco.Template.Common.BlobStorage
Monaco.Template.Common.Domain
Monaco.Template.Common.Infrastructure
Monaco.Template.Common.Messaging.Messages
Monaco.Template.Common.Serilog
Monaco.Template.Common.Tests
Monaco.Template.Application.Tests
Monaco.Template.Domain.Tests
Monaco.Template.Api
Monaco.Template.Application
Monaco.Template.Application.Infrastructure
Monaco.Template.Application.Infrastructure.Migrations
Monaco.Template.Domain
- Features/Slices
- Validations
- Application configuration
- Template options
- Examples
[Wiki still in construction]