Getting Started 🚀
In this guide, we will walk through the process of using Keycloak.AuthServices.Authentication to secure a Web API.
We will be setting up a .NET Core web API project and use Keycloak to handle authentication.
Create an empty web project:
dotnet new web -n GettingStarted
Install Keycloak.AuthServices.Authentication by running:
dotnet add package Keycloak.AuthServices.Authentication
dotnet add package Keycloak.AuthServices.Common
Replace the content of Program.cs with:
using Keycloak.AuthServices.Authentication;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeycloakWebApiAuthentication(builder.Configuration);
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "Hello World!").RequireAuthorization();
app.Run();
TIP
💡 For more detailed explanation of how to configure Authentication visit Configuration/Authentication
Configure Keycloak
Run docker image locally, as described in the documentation.
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.3 start-dev
For Keycloak configuration instructions see: Keycloak Configuration
Here is high level overview of what we need to do to configure Keycloak for our demo:
- Login with admin:admin
- Create Realm
- Name: Test
- Create a User
- Login: Test
- First Name: Test
- Last Name: Test
- Email: test@test.com
- Set Password
- Password: test
- Create a Client:
- Name: test-client
- Configure Client authentication - On (We will need it in the future to get access token via username:password)
- Configure the Audience Mapper
- ❗This is important, by default Keycloak.AuthServices.Authentication assume that the name of resource is the intended Audience / client. Otherwise, you will get 401 status code.
- Alternatively, you can specify
KeycloakAuthenticationOptions.VerifyTokenAudience=false
.
Configure API
Download adapter config so we can use it for seamless integration with Keycloak. All you need to do is to add the content of Adapter Config to the appsettings.Development.json "Keycloak" section like this:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Keycloak": {
"realm": "Test",
"auth-server-url": "http://localhost:8080/",
"ssl-required": "none",
"resource": "test-client",
"credentials": {
"secret": "IMoSbQIplDPmagMU6ZCb8DkQMkkZ0OiI"
},
"confidential-port": 0
}
}
Demo
Run the application:
dotnet run
# Building...
# info: Microsoft.Hosting.Lifetime[14]
# Now listening on: http://localhost:5228
# info: Microsoft.Hosting.Lifetime[0]
# Application started. Press Ctrl+C to shut down.
# info: Microsoft.Hosting.Lifetime[0]
# Hosting environment: Development
# info: Microsoft.Hosting.Lifetime[0]
# Content root path: C:\Users\Oleksii_Nikiforov\dev\keycloak-authorization-services-dotnet\samples\GettingStarted
Run HTTP request without token:
curl http://localhost:5228 -v
# * Trying 127.0.0.1:5228...
# * Connected to localhost (127.0.0.1) port 5228 (#0)
# > GET / HTTP/1.1
# > Host: localhost:5228
# > User-Agent: curl/8.1.2
# > Accept: */*
# >
# < HTTP/1.1 401 Unauthorized
# < Content-Length: 0
# < Date: Thu, 25 Apr 2024 15:04:41 GMT
# < Server: Kestrel
# < WWW-Authenticate: Bearer
# <
# * Connection #0 to host localhost left intact
Request a token based on Resource Owner Flow (we enabled it for the client previously):
curl --data "grant_type=password&client_id=test-client&username=test&password=test&client_secret=IMoSbQIplDPmagMU6ZCb8DkQMkkZ0OiI&scope=roles" \
http://localhost:8080/realms/Test/protocol/openid-connect/token
# {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJrUkdGSFpGZWNaU0VHWV9mOERvc1YxSWpEY01VaFpIczZ4YXlhTks5Uk5zIn0.eyJleHAiOjE3MTQwNTc1MDQsImlhdCI6MTcxNDA1NzIwNCwianRpIjoiNzI1MGQyYTktZTVhMS00NDJmLTllNzYtNWU2Yjc4YmIyNzYwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9UZXN0IiwiYXVkIjpbInRlc3QtY2xpZW50IiwiYWNjb3VudCJdLCJzdWIiOiJiZjBiMzM3MS1jY2RjLTQ0ZjYtODg2MS1jZTI1Y2JmY2FjMzkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0ZXN0LWNsaWVudCIsInNlc3Npb25fc3RhdGUiOiI1NjMzMzJkMi0xMTFhLTRlZjItYjZhMC1lYmMxZDNhZTlhMWUiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIi8qIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXRlc3QiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiNTYzMzMyZDItMTExYS00ZWYyLWI2YTAtZWJjMWQzYWU5YTFlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiVGVzdCBUZXN0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCIsImdpdmVuX25hbWUiOiJUZXN0IiwiZmFtaWx5X25hbWUiOiJUZXN0IiwiZW1haWwiOiJ0ZXN0QHRlc3QuY29tIn0.n2juqBgFtDtxErYMp5iGzFzeE6MeXVzyXUoAd9B4pxvLp8CDEE2OnbnjGfhwlrB70F6KCSxDfHv4HxKPKxWQ6KrPftKWwAOPMY7gGcVmlr0-vek8-bEWxnN2cSRY2mTeHMby4xlppEeq4hK1pBnL5YETn3WnXZ0GkRw7QDODhkcAgKmJUs5yw0Fit0dhq-TCMF9K1lBl-oac3nXBfzIYjZjzqNN9DZWoJMio2movlomsKKl57JrOUpaGTc7GlId4FSLqS0Kdh1gqqme2UG2ErvEs-wROIGfObLX-lL4gFoUW5sCCppUaZN6IkzJhebBK85iiTCUYmAsJruPWX2P_hw","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2NmQ1ZjZlNi1iMDQ1LTQzOWUtYjNiZC02ZDdjMWVhNzBiN2MifQ.eyJleHAiOjE3MTQwNTkwMDQsImlhdCI6MTcxNDA1NzIwNCwianRpIjoiNDI2MGY2MTEtY2VmZi00ODE2LWFjM2QtZjBmNGRlZTVkMzRlIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9UZXN0IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9UZXN0Iiwic3ViIjoiYmYwYjMzNzEtY2NkYy00NGY2LTg4NjEtY2UyNWNiZmNhYzM5IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6InRlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjU2MzMzMmQyLTExMWEtNGVmMi1iNmEwLWViYzFkM2FlOWExZSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjU2MzMzMmQyLTExMWEtNGVmMi1iNmEwLWViYzFkM2FlOWExZSJ9.psZvxBRXsG7CbrV27R7OgcoJVGcgy0yiufUaDKQipwOz_lBI0S3OFEo6EU-JjOlCyIqNvRwXOGJw0BxfBObyuQ","token_type":"Bearer","not-before-policy":0,"session_state":"563332d2-111a-4ef2-b6a0-ebc1d3ae9a1e","scope":"profile email"}
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://localhost:5228/
# * Trying 127.0.0.1:5228...
# * Connected to localhost (127.0.0.1) port 5228 (#0)
# > GET / HTTP/1.1
# > Host: localhost:5228
# > User-Agent: curl/8.1.2
# > Accept: */*
# > Authorization: Bearer YOUR_ACCESS_TOKEN
# >
# < HTTP/1.1 200 OK
# < Content-Type: text/plain; charset=utf-8
# < Date: Thu, 25 Apr 2024 15:13:36 GMT
# < Server: Kestrel
# < Transfer-Encoding: chunked
# <
# Hello World!* Connection #0 to host localhost left intact
🙌 Hooray, congratulations! You know how to configure Web API Authentication we Keycloak using Keycloak.AuthServices.Authentication.
See sample source code: keycloak-authorization-services-dotnet/tree/main/samples/GettingStarted