diff --git a/dotnet-tools.json b/.config/dotnet-tools.json similarity index 66% rename from dotnet-tools.json rename to .config/dotnet-tools.json index 1c5ff8f5..f5e66350 100644 --- a/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -2,6 +2,12 @@ "version": 1, "isRoot": true, "tools": { + "centralisedpackageconverter": { + "version": "1.0.52", + "commands": [ + "central-pkg-converter" + ] + }, "cake.tool": { "version": "2.0.0", "commands": [ @@ -15,4 +21,4 @@ ] } } -} \ No newline at end of file +} diff --git a/KeycloakAuthorizationServicesDotNet.sln b/KeycloakAuthorizationServicesDotNet.sln index 6aa0862b..48afcb44 100644 --- a/KeycloakAuthorizationServicesDotNet.sln +++ b/KeycloakAuthorizationServicesDotNet.sln @@ -78,6 +78,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keycloak.AuthServices.OpenT EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "samples\WebApp\WebApp.csproj", "{4AF4CE52-F007-4FEE-9324-7E52314398FF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keycloak.AuthServices.Aspire.Hosting", "src\Keycloak.AuthServices.Aspire.Hosting\Keycloak.AuthServices.Aspire.Hosting.csproj", "{0943D3CE-5B15-46F8-9B0C-0C2911FD70A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keycloak.AuthServices.Templates", "src\Keycloak.AuthServices.Templates\Keycloak.AuthServices.Templates.csproj", "{EFF07507-0C96-49BD-8D66-0E2B05DA0BDD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -172,6 +176,14 @@ Global {4AF4CE52-F007-4FEE-9324-7E52314398FF}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AF4CE52-F007-4FEE-9324-7E52314398FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {4AF4CE52-F007-4FEE-9324-7E52314398FF}.Release|Any CPU.Build.0 = Release|Any CPU + {0943D3CE-5B15-46F8-9B0C-0C2911FD70A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0943D3CE-5B15-46F8-9B0C-0C2911FD70A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0943D3CE-5B15-46F8-9B0C-0C2911FD70A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0943D3CE-5B15-46F8-9B0C-0C2911FD70A3}.Release|Any CPU.Build.0 = Release|Any CPU + {EFF07507-0C96-49BD-8D66-0E2B05DA0BDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EFF07507-0C96-49BD-8D66-0E2B05DA0BDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EFF07507-0C96-49BD-8D66-0E2B05DA0BDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EFF07507-0C96-49BD-8D66-0E2B05DA0BDD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -200,6 +212,8 @@ Global {B060EE8C-C76D-48A4-B209-4646070A7E0D} = {AEBE10B1-96B1-4060-B8C1-1F9BFA7A586C} {3FE98A91-BA4E-4D4F-A6A5-A43123644ACD} = {F9D5C5B8-9933-4AE0-ADAC-6B8C15F7552A} {4AF4CE52-F007-4FEE-9324-7E52314398FF} = {AEBE10B1-96B1-4060-B8C1-1F9BFA7A586C} + {0943D3CE-5B15-46F8-9B0C-0C2911FD70A3} = {F9D5C5B8-9933-4AE0-ADAC-6B8C15F7552A} + {EFF07507-0C96-49BD-8D66-0E2B05DA0BDD} = {F9D5C5B8-9933-4AE0-ADAC-6B8C15F7552A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E1907BFD-C144-4B48-AA40-972F499D4E08} diff --git a/README.md b/README.md index fd98b221..5b19e9b2 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Easy Authentication and Authorization with Keycloak in .NET. | `Keycloak.AuthServices.Authorization` | [![Nuget](https://img.shields.io/nuget/v/Keycloak.AuthServices.Authorization.svg)](https://nuget.org/packages/Keycloak.AuthServices.Authorization) | Authorization Services. Use Keycloak as authorization server | | `Keycloak.AuthServices.Sdk` | [![Nuget](https://img.shields.io/nuget/v/Keycloak.AuthServices.Sdk.svg)](https://nuget.org/packages/Keycloak.AuthServices.Sdk) | HTTP API integration with Keycloak | | `Keycloak.AuthServices.Sdk.Kiota` | [![Nuget](https://img.shields.io/nuget/v/Keycloak.AuthServices.Sdk.Kiota.svg)](https://nuget.org/packages/Keycloak.AuthServices.Sdk.Kiota) | HTTP API integration with Keycloak based on OpenAPI | +| `Keycloak.AuthServices.OpenTelemetry` | [![Nuget](https://img.shields.io/nuget/v/Keycloak.AuthServices.OpenTelemetry.svg)](https://nuget.org/packages/Keycloak.AuthServices.OpenTelemetry) | OpenTelemetry support | +| `Keycloak.AuthServices.Templates` | [![Nuget](https://img.shields.io/nuget/v/Keycloak.AuthServices.Templates.svg)](https://nuget.org/packages/Keycloak.AuthServices.Templates) | `dotnet new` templates | + [![GitHub Actions Build History](https://buildstats.info/github/chart/nikiforovall/keycloak-authorization-services-dotnet?branch=main&includeBuildsFromPullRequest=false)](https://github.com/NikiforovAll/keycloak-authorization-services-dotnet/actions) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ab9c4fb4..21a613a9 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -105,22 +105,32 @@ export default withMermaid({ }, { text: 'Maintenance👨‍🔬', + collapsed: true, items: [ { text: 'Q&A', link: '/qa/recipes' }, { text: 'Troubleshooting', link: '/qa/troubleshooting' }, { text: 'OpenTelemetry🔭', link: '/opentelemetry' } ] }, + { + text: 'Dev Experience 🛠️', + collapsed: true, + items: [ + { text: 'Templates', link: '/devex/templates' }, + { text: 'Aspire Support', link: '/devex/aspire' }, + ] + }, { text: 'Examples', collapsed: false, items: [ { text: 'Web API Getting Started', link: '/examples/auth-getting-started' }, + { text: 'Aspire + Web API', link: '/examples/aspire-web-api' }, { text: 'Authorization', link: '/examples/authorization-getting-started' }, { text: 'Resource Authorization ✨', link: '/examples/resource-authorization' }, { text: 'Clean Architecture', link: '/examples/auth-clean-arch' }, { text: 'Web App MVC', link: '/examples/web-app-mvc' }, - { text: 'Web API + Blazor', link: '/examples/web-api-blazor' } + { text: 'Web API + Blazor', link: '/examples/web-api-blazor' }, ] } ] diff --git a/docs/devex/aspire.md b/docs/devex/aspire.md new file mode 100644 index 00000000..c66fb5f2 --- /dev/null +++ b/docs/devex/aspire.md @@ -0,0 +1,122 @@ +# Aspire Support + +[.NET Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) is an opinionated, cloud ready stack for building observable, production ready, distributed applications. + +`Keycloak.AuthServices.Aspire.Hosting` adds a support to run Keycloak Instance as a component. It is intended to be used together with `Keycloak.AuthServices`. + +See working example here. [Examples/Aspire + Web API](/examples/aspire-web-api) + +## Add to existing application + +Install [Keycloak.AuthServices.Aspire.Hosting](https://www.nuget.org/packages/Keycloak.AuthServices.Aspire.Hosting) package to "AppHost": + +```bash +dotnet add package Keycloak.AuthServices.Aspire.Hosting +``` + +Modify the `AppHost/Program.cs`: + +```csharp +// AppHost/Program.cs +var builder = DistributedApplication.CreateBuilder(args); + +var keycloak = builder // [!code focus] + .AddKeycloakContainer("keycloak"); // [!code focus] + +var realm = keycloak.AddRealm("Test"); // [!code focus] + +builder.AddProject("api").WithReference(keycloak).WithReference(realm); // [!code focus] + +builder.Build().Run(); +``` + +Here is what it does: + +1. Starts the instance of Keycloak as docker container. +2. `WithReference(keycloak)` adds Keycloak server instance to Service Discovery. +3. `WithReference(realm)` adds `Keycloak__Realm` and `Keycloak__AuthServerUrl` environment variables. + +From this point you are almost finished, the only this is left is to configure Authentication missing parts: + +```csharp +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; +var configuration = builder.Configuration; + +builder.AddServiceDefaults(); + +services.AddKeycloakWebApiAuthentication( // [!code focus] + configuration, // [!code focus] + options => // [!code focus] + { // [!code focus] + options.Audience = "workspaces-client"; // [!code focus] + options.RequireHttpsMetadata = false; // [!code focus] + } // [!code focus] +); // [!code focus] +services.AddAuthorization(); // [!code focus] + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/hello", () => "Hello World!").RequireAuthorization(); + +app.Run(); + +``` + +Run: + +```bash +dotnet run --project ./AppHost +``` + +### Import configuration files + +You can reference import files and bind Keycloak data volumes to persist Keycloak configuration and share it with others. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var keycloak = builder + .AddKeycloakContainer("keycloak") + .WithDataVolume() + .WithImport("./KeycloakConfiguration/Test-realm.json") + .WithImport("./KeycloakConfiguration/Test-users-0.json"); + +var realm = keycloak.AddRealm("Test"); + +builder.AddProject("api").WithReference(keycloak).WithReference(realm); + +builder.Build().Run(); +``` + +> [!TIP] +> You can sync your current configuration via CLI + +Inside docker container run: + +```bash +/opt/keycloak/bin/kc.sh export --dir /opt/keycloak/data/import --realm Test +``` + +## Start from Template + +You can use [Keycloak.AuthServices.Templates](https://www.nuget.org/packages/Keycloak.AuthServices.Templates) to scaffold the new Aspire Project that has `Keycloak.AuthServices` integration configured. + +Install template: + +```bash +dotnet new install Keycloak.AuthServices.Templates +``` + +Scaffold a solution: + +```bash +dotnet new keycloak-aspire-starter -o $dev/KeycloakAspireStarter --EnableKeycloakImport +``` + +See [Aspire Template](/devex/templates#aspire-web-api) for more details. diff --git a/docs/devex/templates.md b/docs/devex/templates.md new file mode 100644 index 00000000..90f262e3 --- /dev/null +++ b/docs/devex/templates.md @@ -0,0 +1,143 @@ +# Templates + +You can use [dotnet-new](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new) command to scaffold various solution items. `Keyclaok.AuthServices` provides you with templates to get started with Keycloak and .NET + +## Getting Started + +Install: + +```bash +dotnet new install Keycloak.AuthServices.Templates +``` + +List installed packages: + +```bash +❯ dotnet new list keycloak +# These templates matched your input: 'keycloak' + +# Template Name Short Name Language Tags +# ----------------------- ----------------------- -------- ------------------------------------- +# Keycloak Aspire Starter keycloak-aspire-starter [C#] Common/.NET Aspire/Cloud/API/Keycloak +# Keycloak WebApi keycloak-webapi [C#] Common/API/Keycloak +``` + +> [!NOTE] +> To successfully run commands below - replace `$dev` with the actual working directory. +> +> You can set shell variable by running next command: `export dev=~/projects` + +## Web API + +Use `dotnet new keycloak-webapi -h` to discover how to use this template. + +### Example + +Verify the output of the command by using `--dry-run` option: + +```bash +❯ dotnet new keycloak-webapi -o $dev/KeycloakWebApi --dry-run +# File actions would have been taken: +# Create: $dev\KeycloakWebApi\Directory.Build.props +# Create: $dev\KeycloakWebApi\Directory.Packages.props +# Create: $dev\KeycloakWebApi\Extensions.OpenApi.cs +# Create: $dev\KeycloakWebApi\KeycloakWebApi.csproj +# Create: $dev\KeycloakWebApi\Program.cs +# Create: $dev\KeycloakWebApi\Properties\launchSettings.json +# Create: $dev\KeycloakWebApi\README.md +# Create: $dev\KeycloakWebApi\appsettings.Development.json +# Create: $dev\KeycloakWebApi\appsettings.json +``` + +Generate: + +```bash +❯ dotnet new keycloak-webapi -o $dev/KeycloakWebApi +# The template "Keycloak WebApi" was created successfully. +``` + +Run: + +```bash +❯ dotnet run --project $dev/KeycloakWebApi +# Building... +# info: Microsoft.Hosting.Lifetime[14] +# Now listening on: https://localhost:7107 +# info: Microsoft.Hosting.Lifetime[14] +# Now listening on: http://localhost:5064 +# 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: $dev\KeycloakWebApi +``` + +⚠️To finish the configuration process you will need to: + +1. Start Keycloak instance, see the generated `README.md` file. +2. Create a *Realm* +3. Register a *Client* +4. Update `appsettings.Development.json` correspondingly. + +## Aspire + Web API + +Use `dotnet new keycloak-aspire-starter -h` to discover how to use this template. + +### Example + +Verify the output of the command by using `--dry-run` option: + +```bash +❯ dotnet new keycloak-aspire-starter -o $dev/KeycloakAspireStarter --EnableKeycloakImport --dry-run +# File actions would have been taken: +# Create: $dev\KeycloakAspireStarter\.gitignore +# Create: $dev\KeycloakAspireStarter\Api\Api.csproj +# Create: $dev\KeycloakAspireStarter\Api\Extensions.OpenApi.cs +# Create: $dev\KeycloakAspireStarter\Api\Program.cs +# Create: $dev\KeycloakAspireStarter\Api\Properties\launchSettings.json +# Create: $dev\KeycloakAspireStarter\Api\appsettings.Development.json +# Create: $dev\KeycloakAspireStarter\Api\appsettings.json +# Create: $dev\KeycloakAspireStarter\AppHost\AppHost.csproj +# Create: $dev\KeycloakAspireStarter\AppHost\KeycloakConfiguration\Test-realm.json +# Create: $dev\KeycloakAspireStarter\AppHost\KeycloakConfiguration\Test-users-0.json +# Create: $dev\KeycloakAspireStarter\AppHost\Program.cs +# Create: $dev\KeycloakAspireStarter\AppHost\Properties\launchSettings.json +# Create: $dev\KeycloakAspireStarter\AppHost\appsettings.Development.json +# Create: $dev\KeycloakAspireStarter\AppHost\appsettings.json +# Create: $dev\KeycloakAspireStarter\Directory.Build.props +# Create: $dev\KeycloakAspireStarter\Directory.Packages.props +# Create: $dev\KeycloakAspireStarter\KeycloakAspireStarter.sln +# Create: $dev\KeycloakAspireStarter\README.md +# Create: $dev\KeycloakAspireStarter\ServiceDefaults\Extensions.cs +# Create: $dev\KeycloakAspireStarter\ServiceDefaults\ServiceDefaults.csproj +# Create: $dev\KeycloakAspireStarter\global.json +``` + +Generate: + +```bash +❯ dotnet new keycloak-aspire-starter -o $dev/KeycloakAspireStarter --EnableKeycloakImport +# The template "Keycloak Aspire Starter" was created successfully. +``` + +We want to enable automatically imported realm by adding `--EnableKeycloakImport` option. + +Run: + +```bash +❯ dotnet run --project $dev/KeycloakAspireStarter/AppHost/ +# Building... +# info: Aspire.Hosting.DistributedApplication[0] +# Aspire version: 8.0.1+a6e341ebbf956bbcec0dda304109815fcbae70c9 +# info: Aspire.Hosting.DistributedApplication[0] +# Distributed application starting. +# info: Aspire.Hosting.DistributedApplication[0] +# Application host directory is: $dev\KeycloakAspireStarter\AppHost +# info: Aspire.Hosting.DistributedApplication[0] +# Now listening on: http://localhost:15056 +# info: Aspire.Hosting.DistributedApplication[0] +# Distributed application started. Press Ctrl+C to shut down. +``` + +Additionally, open Keycloak master realm by navigating: . Use `admin:admin` as credentials. diff --git a/docs/examples/aspire-web-api.md b/docs/examples/aspire-web-api.md new file mode 100644 index 00000000..98dac626 --- /dev/null +++ b/docs/examples/aspire-web-api.md @@ -0,0 +1,28 @@ +# Aspire + Web API + +This samples contains Keycloak installation configured via configuration files. + +Here is what it does: + +1. Starts a Keycloak Instance as part of Aspire Integration +2. Imports realm and test users (`test1:test`, `test2:test`) + +The Keycloak is already configured, all you need to do is to run sample and try to retrieve token via Swagger UI. + +Run: + +```bash +dotnet run --project ./AppHost +``` + +## Code + +`AppHost`: + +<<< @/../samples/GettingStartedAndAspire/AppHost/Program.cs + +`Api`: + +<<< @/../samples/GettingStartedAndAspire/Api/Program.cs + +See sample source code: [keycloak-authorization-services-dotnet/tree/main/samples/GettingStartedAndAspire](https://github.com/NikiforovAll/keycloak-authorization-services-dotnet/tree/main/samples/GettingStartedAndAspire) diff --git a/docs/qa/recipes.md b/docs/qa/recipes.md index 024715b2..fb8ac63b 100644 --- a/docs/qa/recipes.md +++ b/docs/qa/recipes.md @@ -40,8 +40,16 @@ var keycloakAuthenticationOptions = serviceProvider Sometimes you need to resolve options before the DI container is built. E.g: application startup. ```csharp +using Keycloak.AuthServices.Common; + var keycloakOptions = configuration.GetKeycloakOptions()!; -// OR +``` + +Or: + +```csharp +using Keycloak.AuthServices.Common; + KeycloakAuthorizationOptions options = new(); configuration.BindKeycloakOptions(options); ``` diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index ec03408c..5801b268 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -3,7 +3,8 @@ - + + @@ -23,4 +24,11 @@ + + + + + + + diff --git a/samples/GettingStartedAndAspire/Api/Api.csproj b/samples/GettingStartedAndAspire/Api/Api.csproj new file mode 100644 index 00000000..23ee4e70 --- /dev/null +++ b/samples/GettingStartedAndAspire/Api/Api.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + + + + + + + + + + + + + + + diff --git a/samples/GettingStartedAndAspire/Api/Program.cs b/samples/GettingStartedAndAspire/Api/Program.cs new file mode 100644 index 00000000..895a5d04 --- /dev/null +++ b/samples/GettingStartedAndAspire/Api/Program.cs @@ -0,0 +1,74 @@ +using Keycloak.AuthServices.Authentication; +using Keycloak.AuthServices.Common; +using Microsoft.OpenApi.Models; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; +var configuration = builder.Configuration; + +builder.AddServiceDefaults(); + +var clientName = "workspaces-client"; + +services.AddEndpointsApiExplorer(); +services.AddSwaggerGen(c => +{ + var keycloakOptions = configuration.GetKeycloakOptions()!; + + c.AddSecurityDefinition( + "oidc", + new OpenApiSecurityScheme + { + Name = "oauth2", + Type = SecuritySchemeType.OpenIdConnect, + OpenIdConnectUrl = new Uri(keycloakOptions.OpenIdConnectUrl!) + } + ); + + c.AddSecurityRequirement( + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "oidc" + } + }, + Array.Empty() + } + } + ); + + c.SwaggerDoc("v1", new OpenApiInfo { Title = $"API (v1)", Version = "v1" }); +}); + +services.AddKeycloakWebApiAuthentication( + configuration, + options => + { + options.Audience = clientName; + options.RequireHttpsMetadata = false; + } +); +services.AddAuthorization(); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.RoutePrefix = string.Empty; +}); + +app.UseHttpsRedirection(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/hello", () => "Hello World!").RequireAuthorization(); + +app.Run(); diff --git a/samples/GettingStartedAndAspire/Api/Properties/launchSettings.json b/samples/GettingStartedAndAspire/Api/Properties/launchSettings.json new file mode 100644 index 00000000..72e95944 --- /dev/null +++ b/samples/GettingStartedAndAspire/Api/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7107;http://localhost:5064", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/GettingStartedAndAspire/Api/appsettings.Development.json b/samples/GettingStartedAndAspire/Api/appsettings.Development.json new file mode 100644 index 00000000..33e24053 --- /dev/null +++ b/samples/GettingStartedAndAspire/Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + } +} diff --git a/samples/GettingStartedAndAspire/Api/appsettings.json b/samples/GettingStartedAndAspire/Api/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/samples/GettingStartedAndAspire/Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/samples/GettingStartedAndAspire/AppHost/AppHost.csproj b/samples/GettingStartedAndAspire/AppHost/AppHost.csproj new file mode 100644 index 00000000..976e80bf --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/AppHost.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + true + + + + + + + + + + + + + diff --git a/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-realm.json b/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-realm.json new file mode 100644 index 00000000..5e3fa22d --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-realm.json @@ -0,0 +1,2070 @@ +{ + "id" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "realm" : "Test", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxTemporaryLockouts" : 0, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "cd51aea4-227d-4cf6-a2d5-6da10283e3b0", + "name" : "Reader", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "9ce9d3eb-e6df-45c7-a42f-00c3d4b7df4b", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "manage-account", "view-profile" ] + } + }, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "7e32ca69-a697-4629-9da8-f527be233897", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "bfe2766a-1944-4697-aeec-0d887e35b4e7", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "340f0954-8dc9-4916-a00c-daf4ad6660e8", + "name" : "Admin", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "6a4ac0ac-3caf-4787-aeeb-9b36f5b956c9", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "734857fd-09a5-4bff-93dc-36382f42ae01", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "d1cd3c0a-28fd-46d1-a4aa-02e923c95194", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "d61bee46-e6e3-4c81-8278-8ebb63becc84", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "create-client", "view-authorization", "manage-identity-providers", "query-clients", "manage-realm", "manage-events", "manage-clients", "query-users", "query-realms", "view-identity-providers", "manage-users", "view-events", "view-users", "view-realm", "view-clients", "impersonation", "query-groups", "manage-authorization" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "1e17ff87-497f-402f-912e-1c1b012b816d", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "7cfcf0d4-8e5c-4b6b-99a4-1a9e2eb5392d", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "7111f4fe-4511-47e1-a624-e02c00660eb4", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "668ee3c6-99d6-4658-a338-d095ed688c34", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "5b5147a6-f12d-42c7-add7-c8500e269826", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "37839b9d-e5e3-4485-99f2-25c557555aa5", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "6009a8a8-5da5-4d4d-806f-6f98a1a647ec", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "50bdbf26-8275-453b-bc2b-37268f257a20", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "2dbde0a0-e091-445e-9d3a-436b4009c7e7", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "6bd35863-e81d-4df1-b008-e40ccb2536ae", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-users", "query-groups" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "306357e8-1522-4846-9b2c-27df34c58af6", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "eeb0c9a0-40f1-4021-84d1-07cd062e79c1", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "0df36ea5-6998-4c9c-b093-230f7410301c", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "09202eff-20dd-4cdc-9b7e-da4c6af41e5e", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "1be68123-2c31-444d-be99-f3e7dd00f1d3", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "workspaces-client" : [ { + "id" : "aab1ef9c-ae9d-4787-96e4-25ef92eb869b", + "name" : "uma_protection", + "composite" : false, + "clientRole" : true, + "containerId" : "9d0ab563-c720-4955-b2dd-7836497ce2c0", + "attributes" : { } + } ], + "account-console" : [ ], + "broker" : [ { + "id" : "49b9ba28-76ec-4f45-b088-6c8bc17bf9d6", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "e9024018-8578-4582-ba6c-b90852ffbfbd", + "attributes" : { } + } ], + "account" : [ { + "id" : "634fcad3-6d8c-4357-ae62-eee0daafec9b", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "5adcf7a8-7131-4e63-b47c-955564092053", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "d26263ff-1ac9-4225-b03f-59b5a1466939", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "ee5fe344-bf86-4dec-a66a-3fe99215208d", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "ac915a4f-e96d-4cba-80b2-dd0c9b76d7df", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "5b87eec5-7ae7-40b6-aa44-f6def312bf36", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "4894f5b9-f0c2-48ce-a1b6-371573a6c3a0", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "d766234b-6121-4c80-8311-af77f190d490", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + } ] + } + }, + "groups" : [ { + "id" : "39130ba7-a609-4f88-a657-a824de18f9bd", + "name" : "main", + "path" : "/main", + "subGroups" : [ ], + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { } + }, { + "id" : "ba2daa3f-5373-4ccb-8a2c-f831a81b52f8", + "name" : "public", + "path" : "/public", + "subGroups" : [ ], + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { } + } ], + "defaultRole" : { + "id" : "9ce9d3eb-e6df-45c7-a42f-00c3d4b7df4b", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/Test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/Test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "2072b7ea-1457-4e2b-8696-7d10f6acf966", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/Test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/Test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "7c35a8dd-561c-49c5-a855-4404652cad6c", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "6e9e0501-e6b3-4d1b-8dcf-cabe090450c2", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "e9024018-8578-4582-ba6c-b90852ffbfbd", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "0463dc7e-afed-4314-95a9-e561fb707d3a", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/Test/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/Test/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "0d3dee76-25f5-424f-ac42-24638d76bdce", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "9d0ab563-c720-4955-b2dd-7836497ce2c0", + "clientId" : "workspaces-client", + "name" : "", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "ze4SQDpbyBlB72kdTCTv8ecSWsJHf2Js", + "redirectUris" : [ "*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : true, + "authorizationServicesEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1715201249", + "backchannel.logout.session.required" : "true", + "oauth2.device.authorization.grant.enabled" : "false", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "48678e96-25e8-4a72-8134-e8a9cee40dc2", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "client_id", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "client_id", + "jsonType.label" : "String" + } + }, { + "id" : "c68d08da-3d9b-4abf-920c-e6ab826992e5", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "49a458a3-7ce7-40ad-b125-92f812d1b592", + "name" : "audience", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "included.client.audience" : "workspaces-client", + "id.token.claim" : "false", + "lightweight.claim" : "false", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "b34c35d9-cb80-4b65-9b47-c2ccf601c626", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ], + "authorizationSettings" : { + "allowRemoteResourceManagement" : true, + "policyEnforcementMode" : "ENFORCING", + "resources" : [ { + "name" : "workspaces", + "type" : "urn:workspaces", + "ownerManagedAccess" : false, + "displayName" : "", + "attributes" : { }, + "_id" : "14b70534-d3f3-43ad-bdb4-99eb19e19b97", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:create" + } ], + "icon_uri" : "" + }, { + "name" : "workspaces__public", + "type" : "urn:workspaces", + "ownerManagedAccess" : false, + "attributes" : { }, + "_id" : "581c2315-7097-4961-aa98-4ea1c0a808ef", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:add-user" + }, { + "name" : "workspace:create" + }, { + "name" : "workspace:remove-user" + }, { + "name" : "workspace:list-users" + }, { + "name" : "workspace:delete" + }, { + "name" : "workspace:read" + } ] + }, { + "name" : "workspaces__main", + "type" : "urn:workspaces", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "3e23a12e-9ecd-4453-867d-760c7b296f0c", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:add-user" + }, { + "name" : "workspace:create" + }, { + "name" : "workspace:remove-user" + }, { + "name" : "workspace:list-users" + }, { + "name" : "workspace:delete" + }, { + "name" : "workspace:read" + } ] + } ], + "policies" : [ { + "id" : "303e03b1-f5bf-403f-bacc-578a5d62bc87", + "name" : "Is Admin", + "description" : "", + "type" : "role", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "roles" : "[{\"id\":\"Admin\",\"required\":true}]" + } + }, { + "id" : "a14be734-9e9a-4ccb-a6ff-32bcb840b42e", + "name" : "Is Reader", + "description" : "", + "type" : "role", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "roles" : "[{\"id\":\"Reader\",\"required\":true}]" + } + }, { + "id" : "9b546455-6ffb-4df7-9806-429b0b67f296", + "name" : "Can Create Workspace", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"workspaces\"]", + "scopes" : "[\"workspace:create\"]", + "applyPolicies" : "[\"Is Admin\"]" + } + }, { + "id" : "8535849b-d637-4a60-8b9a-3f045f3f23e9", + "name" : "Can List Workspaces", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "AFFIRMATIVE", + "config" : { + "resources" : "[\"workspaces\"]", + "scopes" : "[\"workspace:list\"]", + "applyPolicies" : "[\"Is Admin\",\"Is Reader\"]" + } + }, { + "id" : "64782bb2-79bd-4904-8299-5012f66c2c85", + "name" : "Can Manage Workspaces", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "defaultResourceType" : "urn:workspaces", + "applyPolicies" : "[\"Is Admin\"]", + "scopes" : "[\"workspace:delete\",\"workspace:read\",\"workspace:add-user\",\"workspace:remove-user\",\"workspace:list-users\"]" + } + } ], + "scopes" : [ { + "id" : "5e5817b9-23ec-4422-aa1c-521aedee90e2", + "name" : "workspace:read", + "iconUri" : "" + }, { + "id" : "bb5b4947-607e-4095-bec6-1b5350a3aa31", + "name" : "workspace:delete", + "iconUri" : "" + }, { + "id" : "5d8af7a9-ab34-4e35-842f-bd5e1c79ecf0", + "name" : "workspace:list", + "iconUri" : "" + }, { + "id" : "b838e1d0-f65b-41ca-a601-a442df709ffd", + "name" : "workspace:list-users", + "iconUri" : "" + }, { + "id" : "c4df45cf-8dee-48ce-afc1-ae595387c6a5", + "name" : "workspace:add-user", + "iconUri" : "" + }, { + "id" : "0f7ae0fb-2645-4ef0-9f1a-55e59d45ca97", + "name" : "workspace:remove-user", + "iconUri" : "" + }, { + "id" : "9e76de21-b0e7-43aa-b959-e72ec7d2d14e", + "name" : "workspace:create", + "iconUri" : "" + }, { + "id" : "e9ef9eae-7ff0-44d7-9052-dd884044485c", + "name" : "workspace:list-user" + } ], + "decisionStrategy" : "AFFIRMATIVE" + } + } ], + "clientScopes" : [ { + "id" : "f7d6e1fa-a7e0-48d7-baea-17a0c21158d9", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "f553fcf3-78bd-4035-9585-29346b5a6841", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "254db54a-2884-4a36-b540-a4dd6a092b8d", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "990999a7-2722-4f22-b3e7-24a601c29961", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "38e4f86f-ec27-4fae-97a1-e9f554a75813", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "35fb3270-789e-4c49-8423-c187c3d8e26c", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "bf515914-8543-48be-8664-ee24c0c05f44", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "e548fddd-d49e-4b38-98f0-7dd2ef0a7d4a", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "acb11301-b3be-4491-a776-a9411e9cc961", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "6cae2880-89c6-4d25-8954-0bee3eff7444", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "f2f1b995-28d0-41e6-9dd8-3af7d165bf6a", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "2ff6562d-5000-4d0b-963e-2920f5a9d84e", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "d1c5a687-19f1-471f-8f4b-c897b6e98921", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "f950235b-fbd7-4b51-8fbc-edde1548ea56", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "165b4eb7-0792-4344-8e5d-421b770c91ec", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + } ] + }, { + "id" : "ea33567c-451e-4253-9fb9-6889a372fa61", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "ef846180-f315-40be-b516-c8c6a73552e8", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + }, { + "id" : "68142aaf-c631-42d0-a9a2-ea0e09f8e58a", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "44ddb4f0-f261-4488-9506-d5dea89cc91a", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "ce0d2ba9-af75-4f4b-b1d3-23fe878293d8", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "introspection.token.claim" : "true", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "948573d2-2329-4441-89ab-5d8b851d0f2a", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "569bff90-0217-4510-9266-7946a5c64511", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "72fb1cfa-5316-4ce7-864c-7ddd566e0085", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String" + } + }, { + "id" : "8e1c66bb-8a19-424e-a18a-be50d002f2b4", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "c1ec3fbb-6f87-4cd2-b982-0c2c09418d48", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "70ab5041-ca9a-4113-8529-274693385d6a", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "9c2edef4-a94d-4535-93c0-ae87cc0eec91", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "4cf7f4cf-3a58-4505-8c03-164909203701", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "e04d54b4-39c5-4ca1-aa0c-8577eb09d74d", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "573d32be-0621-4799-95c7-3f2e70b453fd", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "46553e59-0d4b-4cf2-b496-7e88f6d301a4", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "8bbf3d78-8732-4d09-9f3e-8291471914ff", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "e09746c3-a929-41e6-8a9a-2f8daf70ef4e", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "c6c24701-ace2-48b0-b372-3cc514f0d9d9", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "fdfd912f-3e40-4037-bf05-7bf6d37e1e17", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "621b2709-5c6f-4391-9259-45eb1b0eadb2", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "1138cffa-eb8f-47c1-b039-ce03dcf1efe5", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "referrerPolicy" : "no-referrer", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "0a948d9f-8b69-4087-b1db-eeee7e96ea15", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "1a4bd18e-a618-4708-908b-48eb22c67b7b", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "2f48e794-e1e4-414c-94ec-c88dad310092", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "1f8de95c-4693-4df9-8a49-edc54e0b7d4b", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] + } + }, { + "id" : "17fc94ab-dcf8-403a-9e56-1873dd74d636", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "ae333b0a-d1dc-49a5-b594-c5fd07c38093", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "a719cacd-64e8-4294-9d92-8ad3f5ae3c2a", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "b6b733c0-afaa-4969-8953-1d9c7ab87c74", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-full-name-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "4d4b122e-26b4-4601-b067-0109cfe10a01", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "f98f11d0-0a06-454f-b75a-b82f5de62b1e" ], + "secret" : [ "QmDOEAptTRUECf7Nejo6uQ" ], + "priority" : [ "100" ] + } + }, { + "id" : "d0d0a0db-284b-494d-a839-14702b9913c6", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEogIBAAKCAQEAxP2Qo5vojdFv32/yTdu6olaqJ2tvj6sPw//7VFSvksi7lb7hQU0mHrW39DWYcKbfa3ZPLSVy+hcO/MtZ39ZlqqdBS4m3eoxJa70mOCkrJlbgHfJCjWb7g4C/tvqj2urFWaRcX/f12lly7mDyklwScfR/FNt7ZPbCBYG1EriehynZ3jvy0YAxs+XR52k6tPMI0lCihi4dgtbF0G1HDQCR2zyuJUORlkHCVy/BqIJxTsIHdWLGYYrJ9w6nzEWWXevtTpIErG7QU1WDvdKWZZyHYn4CHyz4lhtSiU/Z5b5Py7SJCsQdOQes2RJXvd7dnq1WVbzBBkfxFZNcArcI0HzhKwIDAQABAoIBACr+SnSi4PEe3kNOW/xpMgUfVBixZpJdG95DvrdLmlw3alWG6QPHEkROldz7W49upEGAe0xXqY3yvWcn/LARS3Qg9+vEHWLmPloA2NgWCCBggp6Wkrbo3Ij5bx5A+sUbS0JC0JRzelmS/zYOu7J83Nu5KIlFU9uXzYXFJt90osjN0//hlvQ8tg/pBm+uUndw8iH2pmhjkWmCtp8VPirrNXvmr9xfDrdLRzxjYod0qOyb1s570c50Bu0VEtEnUrGyDwbN/qDMfk93XS1qvFsIqwQdhVwaeXOMsfztaoSRAFIIsawBlqJyl+HToo7Q0EZoc3XQ5YLYl7hsABCzFuhNYWkCgYEA5NXr8hWkdoHsXdqSYcdquy7f99zpX+GQ+bBl1EaM00Obhuop9+NmJM5ZQLIlPXYEGL7nd+fzaQ1tNnIUpP91vD6jJCf+NprGvzDxKRwRSN8MKA2AeyDasXfqaElJ9p+aDWWc6AYjoMOQJn1iSN5Fbq9qC3UtJyG+pDTVXhLyMhkCgYEA3F/m//nstfeHImfxZWAfx38lvXrefqDr4Hp8kQdFWvi9TToHENQaXT4xfQA46sP1POk/DHaa+mkSQ0nZGoP4hNN00yIwKq8og/qxjfj+rLRiqDi/WnDR9T0c98yyi4AB9+fIHV4Jwr/itTi68/amWjVNMyjiZqNE9G3dzt5uveMCgYBXaxFzrajiTMfLItCA4fl4AHQ28AGHS1hiymxcJ4dsvYsQgyv9MrW2oc2e1W88h3URLvyl5coq+6IZBoO0dJK5kwQnr8+BdKcwKrY2Ci7gM7UlJ+NJJkflO/YkWLsUp+vl6keCq7W4mHzc91EwnRjRK9nK/rx9EnsNp9FK2toDIQKBgGnMJNNJQgoOHrM24Z5AqqEU+qJf5Yc3bGZlh3381qAZAJCdxhyfJ1UScaHIRIr4rriiZeFu43JKlTj2sCLZrqrd9Z0TMbqcQsAxz6IQIvv40dBfBga+WR36S/jvLdxpBQjmHp1ysw7rHkTQSnirNivioQBppV+PDKCoSi9xfArvAoGAI7lezPxB8tCjRlgGeLCs3Qc2oeCOfJMlOkC7DzrlqQLDWK+IhQc2LHtyuw8ixFIxy67oSX+3MIhL1WWgRE0HTC08+I0jS3m/5Et/orv1CyeDUxMTodbIwKziOGMltVQkvx/QgecK2uyB89XK7cNtqdleBZ/34Mziwji6eVRHtYo=" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIIClzCCAX8CBgGPVuvjdjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTI0MDUwODA2MzU1MFoXDTM0MDUwODA2MzczMFowDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMT9kKOb6I3Rb99v8k3buqJWqidrb4+rD8P/+1RUr5LIu5W+4UFNJh61t/Q1mHCm32t2Ty0lcvoXDvzLWd/WZaqnQUuJt3qMSWu9JjgpKyZW4B3yQo1m+4OAv7b6o9rqxVmkXF/39dpZcu5g8pJcEnH0fxTbe2T2wgWBtRK4nocp2d478tGAMbPl0edpOrTzCNJQooYuHYLWxdBtRw0Akds8riVDkZZBwlcvwaiCcU7CB3VixmGKyfcOp8xFll3r7U6SBKxu0FNVg73SlmWch2J+Ah8s+JYbUolP2eW+T8u0iQrEHTkHrNkSV73e3Z6tVlW8wQZH8RWTXAK3CNB84SsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAR5WxCCF/P9raxN6dTFcuYa7Q9cSI7Jzkv9ZxoFN4q2/tzhNFHcy1OrJ4I4fKGylBRhMw9FprtMQtjha/il54vD3Enx8w7SScwhj/QSsnHkY/fnm4vqPb/7vIyR6addR6FQOlHHRxaZ5LhprmRWz9twjH6V8bSrlHYDcnc0xZkFY4v2MVZyZZxu4XHHCdBmpMZs3SMKBTSlqJhbjilNMEZJhqjXfdvTmGjK5J6VhVWGSecaLShQlheLxIby1ibGPBvUJKFrIAVeAd7IA8V8LGT4DhyzqKJA65ay4j3BOy25nrZdpQ3Lif8vutAVd/kYE9LLD8hKSJrL0kXwLJT0UyYw==" ], + "priority" : [ "100" ] + } + }, { + "id" : "85ba91f5-99ec-49f2-a2d5-1e5db7f53341", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAm3rPa5MqyNWDJ9ikksteU9iTVPs7VH8OfoUUuutAe6dQaHLUsyQFnAi0+laM+RwhRMn7WgUIMuLqwI+QdO4qAZYVcXCw5JOPLah9dzBe9zWKGgYkMc0+A7jDAt43x2ERWZmKr4DEAeuPL/GaqZRZJJqfmSmMUJCTmGZyIi2bLByHjgxR9DJC700TFCFz73s6nVVLKRmJk3YPa8Kv7Gxf76J6bX9F7tECfINKrHP2k+pEvfsEcqEMIljUfaT22pWMW3vEYEdy/+f37y039JqRFJzZlERLWf5r31eSsYJoxYyjqHn12UgTERvykRKzDSl+LAh08Ad80xa0xKYwq4/IdQIDAQABAoIBACorDD96WhO/KMOxU61Gf+5QomDdhJFdpmI1GNK7RDFlRv8VCdAOOIFBsUfmGnTjprg/6a5YCAm5Fn8kUMMN5xGoEWfDbw6HYME63dBqK49zk2GMgduJ9URQI3/PIiSulGSPKph/3WlB6INRzScqB2VQlHGXXalqFMW0H0daxCeZpuBJdJgCQY4MQYxqP2n2b2IC+qlj4FvN6jmyNVN/rJsna8OGUQv8c6LSyIW0zm0DQNVu5FZSwwuGTjnOmnNAum+fIbcC3aHF49boDEN1sXd+BNqf8htFyrKdjP1s9Ms/uEMYafzJaZ9EwllKI5JJs9g77CBryR8QEW9DjElZHm8CgYEA0nuZplp+dnWNbbVa3mPigD2GFd5uZ0PNYOWmpkYAmhCWn40Zoa6x4r/mWrifc5jA/KyCTSYoukjRd9bvuvC/uzabomn1QaL3YxFqLxPZ2HCRQ8hmLjCSeMPdo21yGcd8W5MwYsFtfTDLwNty7PRN9dg57ZGMDbtAoxwvY1yhcvcCgYEAvRo4eIz60Ts1MvXTlcP4g95AlzA7W8+omEqxPAcQE7D4yXxy2uHxUIds2VyvFZ385uXmd7GIMX9Kg6i/lfms8lvVx43+iG6rS8ENQZ35vDeZxTcMQbZs1fjbH0q38BrTVFIkrlU5meCnRzRpLrvDFQVHlcnTkC2eFCUa2s4bmPMCgYEAv05T8t0hQC3L48EH3EXQ4loI+ut2USlRDBbvBwVtGpqlLFMkzs1nJP7iMebIFlrqXT22GMTsE5SHvnJ+5dxXh6M0hcPkhEnIrCRx7GQQhiaaAY+tP6IqHtV3J0kcgc3Me7cmLTw9daHaL2nF5RSPnzluPT0inIBrWVUOxTK2EzUCgYBDm8dK2mSmP7vib19pcTo9s012gGkIhzLo1/OL2Z6W27PnHZZWyWckenahM/DpEqYjy1hdvNxLjg4PClsM/MonkEvbX9n7301g9TGPHzL1Zw+UIf1A1T8ONT3qbyVaM80QnZm9tny0lkxYOdXNPpYdwth4JkN8jcKxwx5Z0kytnwKBgGYr0Io5RyDG0MZe0HTZX6TM7QcKh03tUOxJe4w/sfzcWJJ9E8f0dfiS1zQ9LlDLyXDDoc0XMfuvI87EIIVEn0K3UHBOv9V5OxNIDNp5yyHAxHgJTvURxzN7uNSxPGIvzfnhPZJrzbwHW5EW2DplYxjC9QsfZwJPggcov0pbo2c+" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIIClzCCAX8CBgGPVuvkczANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTI0MDUwODA2MzU1MVoXDTM0MDUwODA2MzczMVowDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJt6z2uTKsjVgyfYpJLLXlPYk1T7O1R/Dn6FFLrrQHunUGhy1LMkBZwItPpWjPkcIUTJ+1oFCDLi6sCPkHTuKgGWFXFwsOSTjy2ofXcwXvc1ihoGJDHNPgO4wwLeN8dhEVmZiq+AxAHrjy/xmqmUWSSan5kpjFCQk5hmciItmywch44MUfQyQu9NExQhc+97Op1VSykZiZN2D2vCr+xsX++iem1/Re7RAnyDSqxz9pPqRL37BHKhDCJY1H2k9tqVjFt7xGBHcv/n9+8tN/SakRSc2ZRES1n+a99XkrGCaMWMo6h59dlIExEb8pESsw0pfiwIdPAHfNMWtMSmMKuPyHUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEALLbxXb8KIPq82JSItnGYqLN1C2a4qNOsg/7LccX7y4rpi4DB3AqDWfBxz5lZ8/PRzKID70vw/kSHVhKREhEoT7+UVWmkSsUxP6u47A0yF5LECyu/5QrZXdIiPr2wBl6CSfY2jNkusRWFZfu4BLY+8HIM0RNPhCze+KBGAeilLDEwOMWrqQq9zPKgMZNnzbxQgmSsXAtj2lWeGo+YR3GinNZ6uEstGmG5uadMZIXkyNBRiFSEskRt3zq7DcueyUhK0KCJUpOZ41MTve0fwYxyYX84+oWonO6mfRTCvJ3i79pMBap+1BycoZxJOqJMaYpYXRhiuco1B7Mixxd8frgpgg==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "b44dbfc8-30a6-4812-851f-32b16272dc45", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "f995225a-e140-4eb8-8d1e-f2f13e5b6e18" ], + "secret" : [ "nelR3YME2fLe0PXEPD6maDZMhrMK3y_XLxvOoZPBxOJBDsMCJBR5R6kq0Og5sql-U2J8Ifar9yRu9PQzT0Fn5CuDfT7U_CDpHUBzs9Oj4cY3VbUlJRhuCiPP5mvbINnStw1clC52TiJkaoB4MiIb5LJiW0NtqfevRMepi5m_Il8" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "76f3bc5a-a341-4d6b-9b97-c8b449850a80", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "e89ba416-bfc4-49ac-8e11-31da2f3c24cc", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "a9f19e1b-680f-4560-9c3c-93f16307bca2", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "7ac105b0-925e-4595-ab41-8b5fd1b6a85d", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "d8a13270-002f-4d44-8fc0-dcc21f8ef7df", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "b45a7f2a-af41-484c-adb0-026778e0c69c", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "55b70c25-88b1-490c-9387-9df0af9e3e5d", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "a14450f8-a0c1-4a2e-b620-7ed866f96b2d", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "db9fdfdc-42a1-4a96-85e1-f99fd3db7325", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "35080eb3-e9b0-4b91-88a9-d29f0f54092e", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "3f940eec-d9bd-4983-ad60-1a87701294e9", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "8e2ea393-802a-4051-b618-e7c161aedaea", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "b83f0b49-d4aa-4a3f-a28a-00e0867f6ac1", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + } ] + }, { + "id" : "32a7f7b7-0e25-40f9-aa2c-2aa06ee82d6b", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "9cecb7a6-18a7-4dc4-8db2-63d283a41278", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "5e424577-45ae-481c-b923-7cd8250d1b64", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-terms-and-conditions", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 70, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "ef6e9dd6-6d16-45c3-854b-cb2d0ec6b1e1", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "8dc35842-da02-4826-a1c8-67bee1bd2d69", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "1534fdde-0429-47e2-9632-d1f5316878af", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "bd7146a1-85e7-43c5-860b-bb15dc0b8756", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "TERMS_AND_CONDITIONS", + "name" : "Terms and Conditions", + "providerId" : "TERMS_AND_CONDITIONS", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "firstBrokerLoginFlow" : "first broker login", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "oauth2DevicePollingInterval" : "5", + "parRequestUriLifespan" : "60", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "24.0.3", + "userManagedAccessAllowed" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} \ No newline at end of file diff --git a/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-users-0.json b/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-users-0.json new file mode 100644 index 00000000..47dcaf87 --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/KeycloakConfiguration/Test-users-0.json @@ -0,0 +1,135 @@ +{ + "realm" : "Test", + "users" : [ { + "id" : "2520cfe4-cbd9-4427-900e-454ac427ae07", + "username" : "admin", + "firstName" : "Admin", + "lastName" : "Root", + "email" : "admin@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203376448, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "9e002386-b0ff-4d1b-a051-5f684f1baac6", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203387396, + "secretData" : "{\"value\":\"3JgcmK4MZgKQtEIcqfrrR+INIhDJdOJ/FFrM0JAhtcazpQkvd2s5BCAEmmBlVAHH40JpP8zD/uZQEqTBHZvssQ==\",\"salt\":\"CJMklLYKN68Efch0O+p+mg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test", "Admin" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "76e9fe29-9f8d-405a-8f07-8c91c64c53e2", + "username" : "service-account-workspaces-client", + "emailVerified" : false, + "createdTimestamp" : 1715201249758, + "enabled" : true, + "totp" : false, + "serviceAccountClientId" : "workspaces-client", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test" ], + "clientRoles" : { + "workspaces-client" : [ "uma_protection" ] + }, + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "988744c3-2248-4cba-b55d-31b3d1530a61", + "username" : "stranger1", + "firstName" : "Stranger", + "lastName" : "1", + "email" : "stranger@other.com", + "emailVerified" : true, + "createdTimestamp" : 1715248374124, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "e4d239d6-97ef-410b-a99d-e6eae578bd1e", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715248380363, + "secretData" : "{\"value\":\"eZAGUIFM+hMORHI8BDcsX/O8FHZ21yKorigTuqxIhg59exPvjw5zUYIirGydATLWW2a2ogXLYPqMNgw6vFB/dA==\",\"salt\":\"HIiYwMOlQJn83wzoWMEqpQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "5d0dfaeb-373a-4f28-96a1-eac7629c15ce", + "username" : "test1", + "firstName" : "Test", + "lastName" : "1", + "email" : "test1@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715153965857, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "7c9ed3c9-389b-46f3-9cbf-874071f3561e", + "type" : "password", + "createdDate" : 1715153986510, + "secretData" : "{\"value\":\"ipj7W9FVJiGcIsoY4KkHzQDP4KAlqDcF/t6RZPEPZxpgVqkRrqWzyISCF3MBM4HOO9gsBGLD7VGeO2QsVj36rQ==\",\"salt\":\"BR5J5ojOI+1WuXZcWoAedA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ "/main" ] + }, { + "id" : "0a1f6355-6b7e-49e9-8117-f4d1c53149ff", + "username" : "test2", + "firstName" : "Test", + "lastName" : "2", + "email" : "test2@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203404596, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "f3018661-b319-4c6b-8cb4-133edacc5817", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203414451, + "secretData" : "{\"value\":\"RNo8Ltsr1hsSRgbGzwEHHcA6m0pGHloJ2gEkbcqInYqCpIOlsfaA3MzE2GFaQc9UYja2yN+VUxxPOTuLzv02EQ==\",\"salt\":\"+/vCVU/zzZtJ9jZc+AomMw==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ "/main" ] + }, { + "id" : "05ae40a3-b90f-4a3f-bf72-a12221769b24", + "username" : "test3", + "firstName" : "Test", + "lastName" : "3", + "email" : "test3@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203783049, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "15918187-5945-4106-8666-8377458b18b9", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203795249, + "secretData" : "{\"value\":\"V1Acf5D37cdktf8vSEfvgxktjX6flegkSPETY6B0NSK2I/ji0tGLnxXuvbKXCO5C1thMFhs08+2qRT+LMrXN7w==\",\"salt\":\"M7c0T4JBP6L/8I5Sejij/Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ ] + } ] +} \ No newline at end of file diff --git a/samples/GettingStartedAndAspire/AppHost/Program.cs b/samples/GettingStartedAndAspire/AppHost/Program.cs new file mode 100644 index 00000000..978efcad --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/Program.cs @@ -0,0 +1,13 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var keycloak = builder + .AddKeycloakContainer("keycloak") + .WithDataVolume() + .WithImport("./KeycloakConfiguration/Test-realm.json") + .WithImport("./KeycloakConfiguration/Test-users-0.json"); + +var realm = keycloak.AddRealm("Test"); + +builder.AddProject("api").WithReference(keycloak).WithReference(realm); + +builder.Build().Run(); diff --git a/samples/GettingStartedAndAspire/AppHost/Properties/launchSettings.json b/samples/GettingStartedAndAspire/AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..acf4a4cd --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/Properties/launchSettings.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:15056", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16070", + "DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + } + } +} diff --git a/samples/GettingStartedAndAspire/AppHost/appsettings.Development.json b/samples/GettingStartedAndAspire/AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/GettingStartedAndAspire/AppHost/appsettings.json b/samples/GettingStartedAndAspire/AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/samples/GettingStartedAndAspire/AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/samples/GettingStartedAndAspire/GettingStartedAndAspire.sln b/samples/GettingStartedAndAspire/GettingStartedAndAspire.sln new file mode 100644 index 00000000..706a7c20 --- /dev/null +++ b/samples/GettingStartedAndAspire/GettingStartedAndAspire.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "Api\Api.csproj", "{D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppHost", "AppHost\AppHost.csproj", "{2D658505-DDE3-49DF-AF39-2E921C7A984C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDefaults", "ServiceDefaults\ServiceDefaults.csproj", "{6C56AC38-F4CE-492F-BFE5-8444CE785FE4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Release|Any CPU.Build.0 = Release|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Release|Any CPU.Build.0 = Release|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/samples/GettingStartedAndAspire/ServiceDefaults/Extensions.cs b/samples/GettingStartedAndAspire/ServiceDefaults/Extensions.cs new file mode 100644 index 00000000..830c4b5a --- /dev/null +++ b/samples/GettingStartedAndAspire/ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +namespace Microsoft.Extensions.Hosting; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.UseServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry( + this IHostApplicationBuilder builder + ) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder + .Services.AddOpenTelemetry() + .WithMetrics(metrics => + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddProcessInstrumentation() + .AddRuntimeInstrumentation() + .AddKeycloakAuthServicesInstrumentation() + ) + .WithTracing(tracing => + { + if (builder.Environment.IsDevelopment()) + { + // We want to view all traces in development + tracing.SetSampler(new AlwaysOnSampler()); + } + + tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddKeycloakAuthServicesInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters( + this IHostApplicationBuilder builder + ) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace( + builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"] + ); + + if (useOtlpExporter) + { + builder.Services.Configure(logging => + logging.AddOtlpExporter() + ); + builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => + metrics.AddOtlpExporter() + ); + builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => + tracing.AddOtlpExporter() + ); + } + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks( + this IHostApplicationBuilder builder + ) + { + builder + .Services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + app.MapHealthChecks("/health"); + + app.MapHealthChecks( + "/alive", + new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") } + ); + + return app; + } +} diff --git a/samples/GettingStartedAndAspire/ServiceDefaults/ServiceDefaults.csproj b/samples/GettingStartedAndAspire/ServiceDefaults/ServiceDefaults.csproj new file mode 100644 index 00000000..a2b41c26 --- /dev/null +++ b/samples/GettingStartedAndAspire/ServiceDefaults/ServiceDefaults.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + true + + + + + + + + + + + + + + + + + + + + diff --git a/samples/WebApp/KeycloakConfiguration/test-client.json b/samples/WebApp/KeycloakConfiguration/test-client.json index 72cc0785..019d1306 100644 --- a/samples/WebApp/KeycloakConfiguration/test-client.json +++ b/samples/WebApp/KeycloakConfiguration/test-client.json @@ -19,7 +19,7 @@ "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, - "implicitFlowEnabled": true, + "implicitFlowEnabled": false, "directAccessGrantsEnabled": true, "serviceAccountsEnabled": false, "publicClient": true, @@ -36,24 +36,6 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "name": "realm_access", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "introspection.token.claim": "true", - "multivalued": "true", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "lightweight.claim": "false", - "access.token.claim": "true", - "claim.name": "realm_roles", - "jsonType.label": "String" - } - } - ], "defaultClientScopes": [ "web-origins", "acr", diff --git a/samples/WebApp/Program.cs b/samples/WebApp/Program.cs index 332cf827..589ce564 100644 --- a/samples/WebApp/Program.cs +++ b/samples/WebApp/Program.cs @@ -1,6 +1,7 @@ using Keycloak.AuthServices.Authentication; using Keycloak.AuthServices.Authorization; using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; var builder = WebApplication.CreateBuilder(args); @@ -18,7 +19,7 @@ { // we need this for front-channel sign-out options.SaveTokens = true; - options.ResponseType = "code"; + options.ResponseType = OpenIdConnectResponseType.Code; options.Events = new OpenIdConnectEvents { OnSignedOutCallbackRedirect = context => diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index e5a80dfb..a070695f 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,5 +1,6 @@ + @@ -26,4 +27,4 @@ - \ No newline at end of file + diff --git a/src/Keycloak.AuthServices.Aspire.Hosting/Keycloak.AuthServices.Aspire.Hosting.csproj b/src/Keycloak.AuthServices.Aspire.Hosting/Keycloak.AuthServices.Aspire.Hosting.csproj new file mode 100644 index 00000000..0e4df9b6 --- /dev/null +++ b/src/Keycloak.AuthServices.Aspire.Hosting/Keycloak.AuthServices.Aspire.Hosting.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + + + + Keycloak.AuthServices.Aspire.Hosting + Aspire support for Keycloak + $(PackageTags);aspire;cloud-native + true + 0.1.0 + + + + + + + + diff --git a/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakBuilderExtensions.cs b/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakBuilderExtensions.cs new file mode 100644 index 00000000..42aad1ed --- /dev/null +++ b/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakBuilderExtensions.cs @@ -0,0 +1,228 @@ +namespace Aspire.Hosting; + +using Aspire.Hosting.ApplicationModel; + +/// +/// Provides extension methods for adding Keycloak resources to an . +/// +public static class KeycloakBuilderExtensions +{ + private const string UserEnvVarName = "KEYCLOAK_ADMIN"; + private const string PasswordEnvVarName = "KEYCLOAK_ADMIN_PASSWORD"; + private const int DefaultContainerPort = 8080; + + /// + /// Adds a Keycloak resource to the application model. A container is used for local development. + /// + /// The . + /// The name of the resource. + /// + /// The parameter used to provide the user name for the Keycloak resource. + /// The parameter used to provide the administrator password for the Keycloak resource. + /// The host port used when launching the container. + /// A reference to the . + public static IResourceBuilder AddKeycloakContainer( + this IDistributedApplicationBuilder builder, + string name, + string? tag = null, + IResourceBuilder? userName = null, + IResourceBuilder? password = null, + int? port = null + ) + { + ArgumentNullException.ThrowIfNull(builder); + + var server = new KeycloakResource(name, userName?.Resource, password?.Resource); + + return builder + .AddResource(server) + .WithHttpEndpoint( + port: port ?? DefaultContainerPort, + targetPort: DefaultContainerPort, + name: KeycloakResource.PrimaryEndpointName + ) + .WithImage(KeycloakContainerImageTags.Image, tag ?? KeycloakContainerImageTags.Tag) + .WithImageRegistry(KeycloakContainerImageTags.Registry) + .WithEnvironment(context => + { + context.EnvironmentVariables[UserEnvVarName] = server.UserNameReference; + context.EnvironmentVariables[PasswordEnvVarName] = server.PasswordReference; + }) + .WithAnnotation( + new CommandLineArgsCallbackAnnotation(args => + { + args.Clear(); + + if (builder.ExecutionContext.IsRunMode) + { + args.Add("start-dev"); + } + else + { + args.Add("start"); + } + args.Add("--import-realm"); + }) + ); + } + + /// + /// Adds a Realm to the application model. + /// + /// The Keycloak server resource builder. + /// The name of the resource. + /// The name of the Realm. + /// A reference to the . + public static IResourceBuilder AddRealm( + this IResourceBuilder builder, + string name, + string? realm = null + ) + { + ArgumentNullException.ThrowIfNull(builder); + + var realmResource = new KeycloakRealmResource(name, realm ?? name, builder.Resource); + + return builder.ApplicationBuilder.AddResource(realmResource).ExcludeFromManifest(); + } + + /// + /// Adds a Realm to the application model. + /// + /// The Keycloak server resource builder. + /// + /// + /// A reference to the . + public static IResourceBuilder WithImport( + this IResourceBuilder builder, + string import, + bool isReadOnly = false + ) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.Resource.WithImport(import); + + if (Directory.Exists(import)) + { + builder.WithBindMount(import, "/opt/keycloak/data/import", isReadOnly); + } + else + { + var fileName = Path.GetFileName(import); + + builder.WithBindMount(import, $"/opt/keycloak/data/import/{fileName}", isReadOnly); + } + + return builder; + } + + /// + /// Adds a named volume for the data folder to a PostgreSQL container resource. + /// + /// The resource builder. + /// The name of the volume. Defaults to an auto-generated name based on the application and resource names. + /// A flag that indicates if this is a read-only volume. + /// The . + public static IResourceBuilder WithDataVolume( + this IResourceBuilder builder, + string? name = null, + bool isReadOnly = false + ) + { + ArgumentNullException.ThrowIfNull(builder); + + return builder.WithVolume( + name ?? VolumeNameGenerator.CreateVolumeName(builder, "data"), + "/opt/keycloak/data", + isReadOnly + ); + } + + /// + /// Adds a reference to a Keycloak resource to the specified resource builder. + /// + /// The type of the resource builder. + /// The resource builder to add the reference to. + /// The Keycloak resource builder to reference. + /// + /// The resource builder with the added reference. + public static IResourceBuilder WithReference( + this IResourceBuilder builder, + IResourceBuilder keycloakBuilder, + string configurationPrefix = "Keycloak" + ) + where TResource : IResourceWithEnvironment + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(keycloakBuilder); + + builder.WithEnvironment( + $"{configurationPrefix}__AuthServerUrl", + keycloakBuilder.Resource.Parent.PrimaryEndpoint + ); + + builder.WithEnvironment($"{configurationPrefix}__Realm", keycloakBuilder.Resource.Realm); + + return builder; + } +} + +internal static class KeycloakContainerImageTags +{ + public const string Registry = "quay.io"; + public const string Image = "keycloak/keycloak"; + public const string Tag = "24.0.3"; +} + +internal static class VolumeNameGenerator +{ + /// + /// Creates a volume name with the form $"{applicationName}-{resourceName}-{suffix}, e.g. "myapplication-postgres-data". + /// + /// + /// If the application name contains chars that are invalid for a volume name, the prefix "volume-" will be used instead. + /// + public static string CreateVolumeName(IResourceBuilder builder, string suffix) + where T : IResource + { + if (!HasOnlyValidChars(suffix)) + { + throw new ArgumentException( + $"The suffix '{suffix}' contains invalid characters. Only [a-zA-Z0-9_.-] are allowed.", + nameof(suffix) + ); + } + + // Create volume name like "myapplication-postgres-data" + var applicationName = builder.ApplicationBuilder.Environment.ApplicationName; + var resourceName = builder.Resource.Name; + return $"{(HasOnlyValidChars(applicationName) ? applicationName : "volume")}-{resourceName}-{suffix}"; + } + + private static bool HasOnlyValidChars(string name) + { + // According to the error message from docker CLI, volume names must be of form "[a-zA-Z0-9][a-zA-Z0-9_.-]" + var nameSpan = name.AsSpan(); + + for (var i = 0; i < nameSpan.Length; i++) + { + var c = nameSpan[i]; + + if (i == 0 && !(char.IsAsciiLetter(c) || char.IsNumber(c))) + { + // First char must be a letter or number + return false; + } + else if ( + !(char.IsAsciiLetter(c) || char.IsNumber(c) || c == '_' || c == '.' || c == '-') + ) + { + // Subsequent chars must be a letter, number, underscore, period, or hyphen + return false; + } + } + + return true; + } +} diff --git a/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakResource.cs b/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakResource.cs new file mode 100644 index 00000000..d50cab5d --- /dev/null +++ b/src/Keycloak.AuthServices.Aspire.Hosting/KeycloakResource.cs @@ -0,0 +1,93 @@ +namespace Aspire.Hosting; + +using Aspire.Hosting.ApplicationModel; + +/// +/// A resource that represents a PostgreSQL container. +/// +public class KeycloakResource + : ContainerResource, + IResourceWithEnvironment, + IResourceWithServiceDiscovery +{ + internal const string PrimaryEndpointName = "http"; + private const string DefaultUserName = "admin"; + private const string DefaultPassword = "admin"; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the resource. + /// A parameter that contains the Keycloak user name + /// A parameter that contains the Keycloak server password. + public KeycloakResource(string name, ParameterResource? userName, ParameterResource? password) + : base(name) + { + this.PrimaryEndpoint = new(this, PrimaryEndpointName); + this.UserNameParameter = userName; + this.PasswordParameter = password; + } + + /// + /// Gets the primary endpoint for the Keycloak server. + /// + public EndpointReference PrimaryEndpoint { get; } + + /// + /// Gets the parameter that contains the Keycloak server user name. + /// + public ParameterResource? UserNameParameter { get; } + + internal ReferenceExpression UserNameReference => + this.UserNameParameter is not null + ? ReferenceExpression.Create($"{this.UserNameParameter}") + : ReferenceExpression.Create($"{DefaultUserName}"); + + /// + /// Gets the parameter that contains the Keycloak server password. + /// + public ParameterResource? PasswordParameter { get; } + + internal ReferenceExpression PasswordReference => + this.PasswordParameter is not null + ? ReferenceExpression.Create($"{this.PasswordParameter}") + : ReferenceExpression.Create($"{DefaultPassword}"); + + /// + /// + public IList Imports { get; } = []; + + /// + /// Adds an import to the list of imports for this Keycloak realm. + /// + /// The import to add. + /// The current Keycloak realm instance. + public KeycloakResource WithImport(string import) + { + this.Imports.Add(import); + + return this; + } +} + +/// +/// A resource that represents a Realm. This is a child resource of a . +/// +/// The name of the resource. +/// The Realm name. +/// The PostgreSQL parent resource associated with this database. +public class KeycloakRealmResource( + string name, + string realmName, + KeycloakResource postgresParentResource +) : Resource(name), IResourceWithParent +{ + /// + /// Gets the parent PostgresSQL container resource. + /// + public KeycloakResource Parent { get; } = postgresParentResource; + + /// + /// + public string Realm { get; } = realmName; +} diff --git a/src/Keycloak.AuthServices.Aspire.Hosting/README.md b/src/Keycloak.AuthServices.Aspire.Hosting/README.md new file mode 100644 index 00000000..e1807c58 --- /dev/null +++ b/src/Keycloak.AuthServices.Aspire.Hosting/README.md @@ -0,0 +1,10 @@ +# Keycloak Aspire Support + +## TODO: + +1. Create realm programmatically +2. Production scenarios support for publish mode + +## References + +* diff --git a/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs b/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs index 6654466d..cbae6f6c 100644 --- a/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs @@ -272,6 +272,14 @@ private static void AddKeycloakWebAppInternal( RoleClaimType = keycloakOptions.RoleClaimType, }; + options.Scope.Add("openid"); + + options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email"); + options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub"); + options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name"); + options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name"); + options.ClaimActions.MapJsonKey(ClaimTypes.Role, "roles"); + if (options.MapInboundClaims) { options.ClaimActions.MapUniqueJsonKey( diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.gitignore b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.gitignore new file mode 100644 index 00000000..104b5441 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.template.config/template.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.template.config/template.json new file mode 100644 index 00000000..d2c3b156 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/.template.config/template.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Oleksii Nikiforov", + "classifications": [ + "Common", + ".NET Aspire", + "Cloud", + "API", + "Keycloak" + ], + "identity": "Keycloak.AuthServices.Templates.AspireStarter", + "name": "Keycloak Aspire Starter", + "description": "Creates a solution based on Aspire integrated with Keycloak", + "shortName": "keycloak-aspire-starter", + "sourceName": "KeycloakAspireStarter", + "preferNameDirectory": true, + "defaultName": "KeycloakAspireStarter", + "tags": { + "language": "C#", + "type": "solution" + }, + "guids": [ + "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC", + "D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40", + "2D658505-DDE3-49DF-AF39-2E921C7A984C", + "6C56AC38-F4CE-492F-BFE5-8444CE785FE4" + ], + "postActions": [], + "symbols": { + "EnableKeycloakImport": { + "type": "parameter", + "dataType": "bool", + "defaultValue": "false" + } + }, + "sources": [ + { + "modifiers": [ + { + "condition": "(!EnableKeycloakImport)", + "exclude": [ + "AppHost/KeycloakConfiguration/Test-realm.json", + "AppHost/KeycloakConfiguration/Test-users-0.json" + ] + } + ] + } + ] +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Api.csproj b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Api.csproj new file mode 100644 index 00000000..64ff75ca --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Api.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + + + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Extensions.OpenApi.cs b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Extensions.OpenApi.cs new file mode 100644 index 00000000..6cbe27cc --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Extensions.OpenApi.cs @@ -0,0 +1,64 @@ +namespace Api; + +using Keycloak.AuthServices.Authentication; +using Keycloak.AuthServices.Common; +using Microsoft.OpenApi.Models; + +public static class ExtensionsOpenApi +{ + public static IServiceCollection AddApplicationOpenApi( + this IServiceCollection services, + IConfiguration configuration + ) + { + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(c => + { + var keycloakOptions = + configuration.GetKeycloakOptions()!; + + c.AddSecurityDefinition( + "oidc", + new OpenApiSecurityScheme + { + Name = "OIDC", + Type = SecuritySchemeType.OpenIdConnect, + OpenIdConnectUrl = new Uri(keycloakOptions.OpenIdConnectUrl!) + } + ); + + c.AddSecurityRequirement( + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "oidc" + } + }, + Array.Empty() + } + } + ); + + c.SwaggerDoc("v1", new OpenApiInfo { Title = $"API (v1)", Version = "v1" }); + }); + + return services; + } + + public static IApplicationBuilder UseApplicationOpenApi(this IApplicationBuilder app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.RoutePrefix = string.Empty; + }); + + return app; + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Program.cs b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Program.cs new file mode 100644 index 00000000..14a280e2 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Program.cs @@ -0,0 +1,32 @@ +using Api; +using Keycloak.AuthServices.Authentication; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; +var configuration = builder.Configuration; + +builder.AddServiceDefaults(); +services.AddApplicationOpenApi(configuration); + +services.AddKeycloakWebApiAuthentication( + configuration, + options => + { + options.Audience = "workspaces-client"; + options.RequireHttpsMetadata = false; + } +); +services.AddAuthorization(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.UseApplicationOpenApi(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/hello", () => "Hello World!").RequireAuthorization(); + +app.Run(); diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Properties/launchSettings.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Properties/launchSettings.json new file mode 100644 index 00000000..72e95944 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7107;http://localhost:5064", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.Development.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/AppHost.csproj b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/AppHost.csproj new file mode 100644 index 00000000..86f599aa --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/AppHost.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + true + + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-realm.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-realm.json new file mode 100644 index 00000000..5e3fa22d --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-realm.json @@ -0,0 +1,2070 @@ +{ + "id" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "realm" : "Test", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxTemporaryLockouts" : 0, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "cd51aea4-227d-4cf6-a2d5-6da10283e3b0", + "name" : "Reader", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "9ce9d3eb-e6df-45c7-a42f-00c3d4b7df4b", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "manage-account", "view-profile" ] + } + }, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "7e32ca69-a697-4629-9da8-f527be233897", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "bfe2766a-1944-4697-aeec-0d887e35b4e7", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + }, { + "id" : "340f0954-8dc9-4916-a00c-daf4ad6660e8", + "name" : "Admin", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "6a4ac0ac-3caf-4787-aeeb-9b36f5b956c9", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "734857fd-09a5-4bff-93dc-36382f42ae01", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "d1cd3c0a-28fd-46d1-a4aa-02e923c95194", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "d61bee46-e6e3-4c81-8278-8ebb63becc84", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "create-client", "view-authorization", "manage-identity-providers", "query-clients", "manage-realm", "manage-events", "manage-clients", "query-users", "query-realms", "view-identity-providers", "manage-users", "view-events", "view-users", "view-realm", "view-clients", "impersonation", "query-groups", "manage-authorization" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "1e17ff87-497f-402f-912e-1c1b012b816d", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "7cfcf0d4-8e5c-4b6b-99a4-1a9e2eb5392d", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "7111f4fe-4511-47e1-a624-e02c00660eb4", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "668ee3c6-99d6-4658-a338-d095ed688c34", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "5b5147a6-f12d-42c7-add7-c8500e269826", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "37839b9d-e5e3-4485-99f2-25c557555aa5", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "6009a8a8-5da5-4d4d-806f-6f98a1a647ec", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "50bdbf26-8275-453b-bc2b-37268f257a20", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "2dbde0a0-e091-445e-9d3a-436b4009c7e7", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "6bd35863-e81d-4df1-b008-e40ccb2536ae", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-users", "query-groups" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "306357e8-1522-4846-9b2c-27df34c58af6", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "eeb0c9a0-40f1-4021-84d1-07cd062e79c1", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "0df36ea5-6998-4c9c-b093-230f7410301c", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "09202eff-20dd-4cdc-9b7e-da4c6af41e5e", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + }, { + "id" : "1be68123-2c31-444d-be99-f3e7dd00f1d3", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "workspaces-client" : [ { + "id" : "aab1ef9c-ae9d-4787-96e4-25ef92eb869b", + "name" : "uma_protection", + "composite" : false, + "clientRole" : true, + "containerId" : "9d0ab563-c720-4955-b2dd-7836497ce2c0", + "attributes" : { } + } ], + "account-console" : [ ], + "broker" : [ { + "id" : "49b9ba28-76ec-4f45-b088-6c8bc17bf9d6", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "e9024018-8578-4582-ba6c-b90852ffbfbd", + "attributes" : { } + } ], + "account" : [ { + "id" : "634fcad3-6d8c-4357-ae62-eee0daafec9b", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "5adcf7a8-7131-4e63-b47c-955564092053", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "d26263ff-1ac9-4225-b03f-59b5a1466939", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "ee5fe344-bf86-4dec-a66a-3fe99215208d", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "ac915a4f-e96d-4cba-80b2-dd0c9b76d7df", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "5b87eec5-7ae7-40b6-aa44-f6def312bf36", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "4894f5b9-f0c2-48ce-a1b6-371573a6c3a0", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + }, { + "id" : "d766234b-6121-4c80-8311-af77f190d490", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "attributes" : { } + } ] + } + }, + "groups" : [ { + "id" : "39130ba7-a609-4f88-a657-a824de18f9bd", + "name" : "main", + "path" : "/main", + "subGroups" : [ ], + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { } + }, { + "id" : "ba2daa3f-5373-4ccb-8a2c-f831a81b52f8", + "name" : "public", + "path" : "/public", + "subGroups" : [ ], + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { } + } ], + "defaultRole" : { + "id" : "9ce9d3eb-e6df-45c7-a42f-00c3d4b7df4b", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "072063d0-5eb0-4c4b-8df2-e36c9ca6d058" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "eb630d5f-5434-483d-abb2-335b8dbd80b7", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/Test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/Test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "2072b7ea-1457-4e2b-8696-7d10f6acf966", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/Test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/Test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "7c35a8dd-561c-49c5-a855-4404652cad6c", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "6e9e0501-e6b3-4d1b-8dcf-cabe090450c2", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "e9024018-8578-4582-ba6c-b90852ffbfbd", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "fc7da4ff-8a55-4f5e-b3bc-fbdad36d6c55", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "0463dc7e-afed-4314-95a9-e561fb707d3a", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/Test/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/Test/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "0d3dee76-25f5-424f-ac42-24638d76bdce", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "9d0ab563-c720-4955-b2dd-7836497ce2c0", + "clientId" : "workspaces-client", + "name" : "", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "ze4SQDpbyBlB72kdTCTv8ecSWsJHf2Js", + "redirectUris" : [ "*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : true, + "authorizationServicesEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1715201249", + "backchannel.logout.session.required" : "true", + "oauth2.device.authorization.grant.enabled" : "false", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "48678e96-25e8-4a72-8134-e8a9cee40dc2", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "client_id", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "client_id", + "jsonType.label" : "String" + } + }, { + "id" : "c68d08da-3d9b-4abf-920c-e6ab826992e5", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "49a458a3-7ce7-40ad-b125-92f812d1b592", + "name" : "audience", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "included.client.audience" : "workspaces-client", + "id.token.claim" : "false", + "lightweight.claim" : "false", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "b34c35d9-cb80-4b65-9b47-c2ccf601c626", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "introspection.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ], + "authorizationSettings" : { + "allowRemoteResourceManagement" : true, + "policyEnforcementMode" : "ENFORCING", + "resources" : [ { + "name" : "workspaces", + "type" : "urn:workspaces", + "ownerManagedAccess" : false, + "displayName" : "", + "attributes" : { }, + "_id" : "14b70534-d3f3-43ad-bdb4-99eb19e19b97", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:create" + } ], + "icon_uri" : "" + }, { + "name" : "workspaces__public", + "type" : "urn:workspaces", + "ownerManagedAccess" : false, + "attributes" : { }, + "_id" : "581c2315-7097-4961-aa98-4ea1c0a808ef", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:add-user" + }, { + "name" : "workspace:create" + }, { + "name" : "workspace:remove-user" + }, { + "name" : "workspace:list-users" + }, { + "name" : "workspace:delete" + }, { + "name" : "workspace:read" + } ] + }, { + "name" : "workspaces__main", + "type" : "urn:workspaces", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "3e23a12e-9ecd-4453-867d-760c7b296f0c", + "uris" : [ ], + "scopes" : [ { + "name" : "workspace:list" + }, { + "name" : "workspace:add-user" + }, { + "name" : "workspace:create" + }, { + "name" : "workspace:remove-user" + }, { + "name" : "workspace:list-users" + }, { + "name" : "workspace:delete" + }, { + "name" : "workspace:read" + } ] + } ], + "policies" : [ { + "id" : "303e03b1-f5bf-403f-bacc-578a5d62bc87", + "name" : "Is Admin", + "description" : "", + "type" : "role", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "roles" : "[{\"id\":\"Admin\",\"required\":true}]" + } + }, { + "id" : "a14be734-9e9a-4ccb-a6ff-32bcb840b42e", + "name" : "Is Reader", + "description" : "", + "type" : "role", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "roles" : "[{\"id\":\"Reader\",\"required\":true}]" + } + }, { + "id" : "9b546455-6ffb-4df7-9806-429b0b67f296", + "name" : "Can Create Workspace", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"workspaces\"]", + "scopes" : "[\"workspace:create\"]", + "applyPolicies" : "[\"Is Admin\"]" + } + }, { + "id" : "8535849b-d637-4a60-8b9a-3f045f3f23e9", + "name" : "Can List Workspaces", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "AFFIRMATIVE", + "config" : { + "resources" : "[\"workspaces\"]", + "scopes" : "[\"workspace:list\"]", + "applyPolicies" : "[\"Is Admin\",\"Is Reader\"]" + } + }, { + "id" : "64782bb2-79bd-4904-8299-5012f66c2c85", + "name" : "Can Manage Workspaces", + "description" : "", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "defaultResourceType" : "urn:workspaces", + "applyPolicies" : "[\"Is Admin\"]", + "scopes" : "[\"workspace:delete\",\"workspace:read\",\"workspace:add-user\",\"workspace:remove-user\",\"workspace:list-users\"]" + } + } ], + "scopes" : [ { + "id" : "5e5817b9-23ec-4422-aa1c-521aedee90e2", + "name" : "workspace:read", + "iconUri" : "" + }, { + "id" : "bb5b4947-607e-4095-bec6-1b5350a3aa31", + "name" : "workspace:delete", + "iconUri" : "" + }, { + "id" : "5d8af7a9-ab34-4e35-842f-bd5e1c79ecf0", + "name" : "workspace:list", + "iconUri" : "" + }, { + "id" : "b838e1d0-f65b-41ca-a601-a442df709ffd", + "name" : "workspace:list-users", + "iconUri" : "" + }, { + "id" : "c4df45cf-8dee-48ce-afc1-ae595387c6a5", + "name" : "workspace:add-user", + "iconUri" : "" + }, { + "id" : "0f7ae0fb-2645-4ef0-9f1a-55e59d45ca97", + "name" : "workspace:remove-user", + "iconUri" : "" + }, { + "id" : "9e76de21-b0e7-43aa-b959-e72ec7d2d14e", + "name" : "workspace:create", + "iconUri" : "" + }, { + "id" : "e9ef9eae-7ff0-44d7-9052-dd884044485c", + "name" : "workspace:list-user" + } ], + "decisionStrategy" : "AFFIRMATIVE" + } + } ], + "clientScopes" : [ { + "id" : "f7d6e1fa-a7e0-48d7-baea-17a0c21158d9", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "f553fcf3-78bd-4035-9585-29346b5a6841", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "254db54a-2884-4a36-b540-a4dd6a092b8d", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "990999a7-2722-4f22-b3e7-24a601c29961", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "38e4f86f-ec27-4fae-97a1-e9f554a75813", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "35fb3270-789e-4c49-8423-c187c3d8e26c", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "bf515914-8543-48be-8664-ee24c0c05f44", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "e548fddd-d49e-4b38-98f0-7dd2ef0a7d4a", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "acb11301-b3be-4491-a776-a9411e9cc961", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "6cae2880-89c6-4d25-8954-0bee3eff7444", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "f2f1b995-28d0-41e6-9dd8-3af7d165bf6a", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "2ff6562d-5000-4d0b-963e-2920f5a9d84e", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "d1c5a687-19f1-471f-8f4b-c897b6e98921", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "f950235b-fbd7-4b51-8fbc-edde1548ea56", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "165b4eb7-0792-4344-8e5d-421b770c91ec", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + } ] + }, { + "id" : "ea33567c-451e-4253-9fb9-6889a372fa61", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "ef846180-f315-40be-b516-c8c6a73552e8", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + }, { + "id" : "68142aaf-c631-42d0-a9a2-ea0e09f8e58a", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "44ddb4f0-f261-4488-9506-d5dea89cc91a", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "ce0d2ba9-af75-4f4b-b1d3-23fe878293d8", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "introspection.token.claim" : "true", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "948573d2-2329-4441-89ab-5d8b851d0f2a", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "569bff90-0217-4510-9266-7946a5c64511", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "72fb1cfa-5316-4ce7-864c-7ddd566e0085", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String" + } + }, { + "id" : "8e1c66bb-8a19-424e-a18a-be50d002f2b4", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "c1ec3fbb-6f87-4cd2-b982-0c2c09418d48", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "70ab5041-ca9a-4113-8529-274693385d6a", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "9c2edef4-a94d-4535-93c0-ae87cc0eec91", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "4cf7f4cf-3a58-4505-8c03-164909203701", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "e04d54b4-39c5-4ca1-aa0c-8577eb09d74d", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "573d32be-0621-4799-95c7-3f2e70b453fd", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "46553e59-0d4b-4cf2-b496-7e88f6d301a4", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "8bbf3d78-8732-4d09-9f3e-8291471914ff", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "e09746c3-a929-41e6-8a9a-2f8daf70ef4e", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "c6c24701-ace2-48b0-b372-3cc514f0d9d9", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "fdfd912f-3e40-4037-bf05-7bf6d37e1e17", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "621b2709-5c6f-4391-9259-45eb1b0eadb2", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "1138cffa-eb8f-47c1-b039-ce03dcf1efe5", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "referrerPolicy" : "no-referrer", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "0a948d9f-8b69-4087-b1db-eeee7e96ea15", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "1a4bd18e-a618-4708-908b-48eb22c67b7b", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "2f48e794-e1e4-414c-94ec-c88dad310092", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "1f8de95c-4693-4df9-8a49-edc54e0b7d4b", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] + } + }, { + "id" : "17fc94ab-dcf8-403a-9e56-1873dd74d636", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "ae333b0a-d1dc-49a5-b594-c5fd07c38093", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "a719cacd-64e8-4294-9d92-8ad3f5ae3c2a", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "b6b733c0-afaa-4969-8953-1d9c7ab87c74", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-full-name-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "4d4b122e-26b4-4601-b067-0109cfe10a01", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "f98f11d0-0a06-454f-b75a-b82f5de62b1e" ], + "secret" : [ "QmDOEAptTRUECf7Nejo6uQ" ], + "priority" : [ "100" ] + } + }, { + "id" : "d0d0a0db-284b-494d-a839-14702b9913c6", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEogIBAAKCAQEAxP2Qo5vojdFv32/yTdu6olaqJ2tvj6sPw//7VFSvksi7lb7hQU0mHrW39DWYcKbfa3ZPLSVy+hcO/MtZ39ZlqqdBS4m3eoxJa70mOCkrJlbgHfJCjWb7g4C/tvqj2urFWaRcX/f12lly7mDyklwScfR/FNt7ZPbCBYG1EriehynZ3jvy0YAxs+XR52k6tPMI0lCihi4dgtbF0G1HDQCR2zyuJUORlkHCVy/BqIJxTsIHdWLGYYrJ9w6nzEWWXevtTpIErG7QU1WDvdKWZZyHYn4CHyz4lhtSiU/Z5b5Py7SJCsQdOQes2RJXvd7dnq1WVbzBBkfxFZNcArcI0HzhKwIDAQABAoIBACr+SnSi4PEe3kNOW/xpMgUfVBixZpJdG95DvrdLmlw3alWG6QPHEkROldz7W49upEGAe0xXqY3yvWcn/LARS3Qg9+vEHWLmPloA2NgWCCBggp6Wkrbo3Ij5bx5A+sUbS0JC0JRzelmS/zYOu7J83Nu5KIlFU9uXzYXFJt90osjN0//hlvQ8tg/pBm+uUndw8iH2pmhjkWmCtp8VPirrNXvmr9xfDrdLRzxjYod0qOyb1s570c50Bu0VEtEnUrGyDwbN/qDMfk93XS1qvFsIqwQdhVwaeXOMsfztaoSRAFIIsawBlqJyl+HToo7Q0EZoc3XQ5YLYl7hsABCzFuhNYWkCgYEA5NXr8hWkdoHsXdqSYcdquy7f99zpX+GQ+bBl1EaM00Obhuop9+NmJM5ZQLIlPXYEGL7nd+fzaQ1tNnIUpP91vD6jJCf+NprGvzDxKRwRSN8MKA2AeyDasXfqaElJ9p+aDWWc6AYjoMOQJn1iSN5Fbq9qC3UtJyG+pDTVXhLyMhkCgYEA3F/m//nstfeHImfxZWAfx38lvXrefqDr4Hp8kQdFWvi9TToHENQaXT4xfQA46sP1POk/DHaa+mkSQ0nZGoP4hNN00yIwKq8og/qxjfj+rLRiqDi/WnDR9T0c98yyi4AB9+fIHV4Jwr/itTi68/amWjVNMyjiZqNE9G3dzt5uveMCgYBXaxFzrajiTMfLItCA4fl4AHQ28AGHS1hiymxcJ4dsvYsQgyv9MrW2oc2e1W88h3URLvyl5coq+6IZBoO0dJK5kwQnr8+BdKcwKrY2Ci7gM7UlJ+NJJkflO/YkWLsUp+vl6keCq7W4mHzc91EwnRjRK9nK/rx9EnsNp9FK2toDIQKBgGnMJNNJQgoOHrM24Z5AqqEU+qJf5Yc3bGZlh3381qAZAJCdxhyfJ1UScaHIRIr4rriiZeFu43JKlTj2sCLZrqrd9Z0TMbqcQsAxz6IQIvv40dBfBga+WR36S/jvLdxpBQjmHp1ysw7rHkTQSnirNivioQBppV+PDKCoSi9xfArvAoGAI7lezPxB8tCjRlgGeLCs3Qc2oeCOfJMlOkC7DzrlqQLDWK+IhQc2LHtyuw8ixFIxy67oSX+3MIhL1WWgRE0HTC08+I0jS3m/5Et/orv1CyeDUxMTodbIwKziOGMltVQkvx/QgecK2uyB89XK7cNtqdleBZ/34Mziwji6eVRHtYo=" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIIClzCCAX8CBgGPVuvjdjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTI0MDUwODA2MzU1MFoXDTM0MDUwODA2MzczMFowDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMT9kKOb6I3Rb99v8k3buqJWqidrb4+rD8P/+1RUr5LIu5W+4UFNJh61t/Q1mHCm32t2Ty0lcvoXDvzLWd/WZaqnQUuJt3qMSWu9JjgpKyZW4B3yQo1m+4OAv7b6o9rqxVmkXF/39dpZcu5g8pJcEnH0fxTbe2T2wgWBtRK4nocp2d478tGAMbPl0edpOrTzCNJQooYuHYLWxdBtRw0Akds8riVDkZZBwlcvwaiCcU7CB3VixmGKyfcOp8xFll3r7U6SBKxu0FNVg73SlmWch2J+Ah8s+JYbUolP2eW+T8u0iQrEHTkHrNkSV73e3Z6tVlW8wQZH8RWTXAK3CNB84SsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAR5WxCCF/P9raxN6dTFcuYa7Q9cSI7Jzkv9ZxoFN4q2/tzhNFHcy1OrJ4I4fKGylBRhMw9FprtMQtjha/il54vD3Enx8w7SScwhj/QSsnHkY/fnm4vqPb/7vIyR6addR6FQOlHHRxaZ5LhprmRWz9twjH6V8bSrlHYDcnc0xZkFY4v2MVZyZZxu4XHHCdBmpMZs3SMKBTSlqJhbjilNMEZJhqjXfdvTmGjK5J6VhVWGSecaLShQlheLxIby1ibGPBvUJKFrIAVeAd7IA8V8LGT4DhyzqKJA65ay4j3BOy25nrZdpQ3Lif8vutAVd/kYE9LLD8hKSJrL0kXwLJT0UyYw==" ], + "priority" : [ "100" ] + } + }, { + "id" : "85ba91f5-99ec-49f2-a2d5-1e5db7f53341", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAm3rPa5MqyNWDJ9ikksteU9iTVPs7VH8OfoUUuutAe6dQaHLUsyQFnAi0+laM+RwhRMn7WgUIMuLqwI+QdO4qAZYVcXCw5JOPLah9dzBe9zWKGgYkMc0+A7jDAt43x2ERWZmKr4DEAeuPL/GaqZRZJJqfmSmMUJCTmGZyIi2bLByHjgxR9DJC700TFCFz73s6nVVLKRmJk3YPa8Kv7Gxf76J6bX9F7tECfINKrHP2k+pEvfsEcqEMIljUfaT22pWMW3vEYEdy/+f37y039JqRFJzZlERLWf5r31eSsYJoxYyjqHn12UgTERvykRKzDSl+LAh08Ad80xa0xKYwq4/IdQIDAQABAoIBACorDD96WhO/KMOxU61Gf+5QomDdhJFdpmI1GNK7RDFlRv8VCdAOOIFBsUfmGnTjprg/6a5YCAm5Fn8kUMMN5xGoEWfDbw6HYME63dBqK49zk2GMgduJ9URQI3/PIiSulGSPKph/3WlB6INRzScqB2VQlHGXXalqFMW0H0daxCeZpuBJdJgCQY4MQYxqP2n2b2IC+qlj4FvN6jmyNVN/rJsna8OGUQv8c6LSyIW0zm0DQNVu5FZSwwuGTjnOmnNAum+fIbcC3aHF49boDEN1sXd+BNqf8htFyrKdjP1s9Ms/uEMYafzJaZ9EwllKI5JJs9g77CBryR8QEW9DjElZHm8CgYEA0nuZplp+dnWNbbVa3mPigD2GFd5uZ0PNYOWmpkYAmhCWn40Zoa6x4r/mWrifc5jA/KyCTSYoukjRd9bvuvC/uzabomn1QaL3YxFqLxPZ2HCRQ8hmLjCSeMPdo21yGcd8W5MwYsFtfTDLwNty7PRN9dg57ZGMDbtAoxwvY1yhcvcCgYEAvRo4eIz60Ts1MvXTlcP4g95AlzA7W8+omEqxPAcQE7D4yXxy2uHxUIds2VyvFZ385uXmd7GIMX9Kg6i/lfms8lvVx43+iG6rS8ENQZ35vDeZxTcMQbZs1fjbH0q38BrTVFIkrlU5meCnRzRpLrvDFQVHlcnTkC2eFCUa2s4bmPMCgYEAv05T8t0hQC3L48EH3EXQ4loI+ut2USlRDBbvBwVtGpqlLFMkzs1nJP7iMebIFlrqXT22GMTsE5SHvnJ+5dxXh6M0hcPkhEnIrCRx7GQQhiaaAY+tP6IqHtV3J0kcgc3Me7cmLTw9daHaL2nF5RSPnzluPT0inIBrWVUOxTK2EzUCgYBDm8dK2mSmP7vib19pcTo9s012gGkIhzLo1/OL2Z6W27PnHZZWyWckenahM/DpEqYjy1hdvNxLjg4PClsM/MonkEvbX9n7301g9TGPHzL1Zw+UIf1A1T8ONT3qbyVaM80QnZm9tny0lkxYOdXNPpYdwth4JkN8jcKxwx5Z0kytnwKBgGYr0Io5RyDG0MZe0HTZX6TM7QcKh03tUOxJe4w/sfzcWJJ9E8f0dfiS1zQ9LlDLyXDDoc0XMfuvI87EIIVEn0K3UHBOv9V5OxNIDNp5yyHAxHgJTvURxzN7uNSxPGIvzfnhPZJrzbwHW5EW2DplYxjC9QsfZwJPggcov0pbo2c+" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIIClzCCAX8CBgGPVuvkczANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTI0MDUwODA2MzU1MVoXDTM0MDUwODA2MzczMVowDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJt6z2uTKsjVgyfYpJLLXlPYk1T7O1R/Dn6FFLrrQHunUGhy1LMkBZwItPpWjPkcIUTJ+1oFCDLi6sCPkHTuKgGWFXFwsOSTjy2ofXcwXvc1ihoGJDHNPgO4wwLeN8dhEVmZiq+AxAHrjy/xmqmUWSSan5kpjFCQk5hmciItmywch44MUfQyQu9NExQhc+97Op1VSykZiZN2D2vCr+xsX++iem1/Re7RAnyDSqxz9pPqRL37BHKhDCJY1H2k9tqVjFt7xGBHcv/n9+8tN/SakRSc2ZRES1n+a99XkrGCaMWMo6h59dlIExEb8pESsw0pfiwIdPAHfNMWtMSmMKuPyHUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEALLbxXb8KIPq82JSItnGYqLN1C2a4qNOsg/7LccX7y4rpi4DB3AqDWfBxz5lZ8/PRzKID70vw/kSHVhKREhEoT7+UVWmkSsUxP6u47A0yF5LECyu/5QrZXdIiPr2wBl6CSfY2jNkusRWFZfu4BLY+8HIM0RNPhCze+KBGAeilLDEwOMWrqQq9zPKgMZNnzbxQgmSsXAtj2lWeGo+YR3GinNZ6uEstGmG5uadMZIXkyNBRiFSEskRt3zq7DcueyUhK0KCJUpOZ41MTve0fwYxyYX84+oWonO6mfRTCvJ3i79pMBap+1BycoZxJOqJMaYpYXRhiuco1B7Mixxd8frgpgg==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "b44dbfc8-30a6-4812-851f-32b16272dc45", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "f995225a-e140-4eb8-8d1e-f2f13e5b6e18" ], + "secret" : [ "nelR3YME2fLe0PXEPD6maDZMhrMK3y_XLxvOoZPBxOJBDsMCJBR5R6kq0Og5sql-U2J8Ifar9yRu9PQzT0Fn5CuDfT7U_CDpHUBzs9Oj4cY3VbUlJRhuCiPP5mvbINnStw1clC52TiJkaoB4MiIb5LJiW0NtqfevRMepi5m_Il8" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "76f3bc5a-a341-4d6b-9b97-c8b449850a80", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "e89ba416-bfc4-49ac-8e11-31da2f3c24cc", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "a9f19e1b-680f-4560-9c3c-93f16307bca2", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "7ac105b0-925e-4595-ab41-8b5fd1b6a85d", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "d8a13270-002f-4d44-8fc0-dcc21f8ef7df", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "b45a7f2a-af41-484c-adb0-026778e0c69c", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "55b70c25-88b1-490c-9387-9df0af9e3e5d", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "a14450f8-a0c1-4a2e-b620-7ed866f96b2d", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "db9fdfdc-42a1-4a96-85e1-f99fd3db7325", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "35080eb3-e9b0-4b91-88a9-d29f0f54092e", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "3f940eec-d9bd-4983-ad60-1a87701294e9", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "8e2ea393-802a-4051-b618-e7c161aedaea", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "b83f0b49-d4aa-4a3f-a28a-00e0867f6ac1", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + } ] + }, { + "id" : "32a7f7b7-0e25-40f9-aa2c-2aa06ee82d6b", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "9cecb7a6-18a7-4dc4-8db2-63d283a41278", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "5e424577-45ae-481c-b923-7cd8250d1b64", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-terms-and-conditions", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 70, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "ef6e9dd6-6d16-45c3-854b-cb2d0ec6b1e1", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "8dc35842-da02-4826-a1c8-67bee1bd2d69", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "1534fdde-0429-47e2-9632-d1f5316878af", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "bd7146a1-85e7-43c5-860b-bb15dc0b8756", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "TERMS_AND_CONDITIONS", + "name" : "Terms and Conditions", + "providerId" : "TERMS_AND_CONDITIONS", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "firstBrokerLoginFlow" : "first broker login", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "oauth2DevicePollingInterval" : "5", + "parRequestUriLifespan" : "60", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "24.0.3", + "userManagedAccessAllowed" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} \ No newline at end of file diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-users-0.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-users-0.json new file mode 100644 index 00000000..47dcaf87 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/KeycloakConfiguration/Test-users-0.json @@ -0,0 +1,135 @@ +{ + "realm" : "Test", + "users" : [ { + "id" : "2520cfe4-cbd9-4427-900e-454ac427ae07", + "username" : "admin", + "firstName" : "Admin", + "lastName" : "Root", + "email" : "admin@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203376448, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "9e002386-b0ff-4d1b-a051-5f684f1baac6", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203387396, + "secretData" : "{\"value\":\"3JgcmK4MZgKQtEIcqfrrR+INIhDJdOJ/FFrM0JAhtcazpQkvd2s5BCAEmmBlVAHH40JpP8zD/uZQEqTBHZvssQ==\",\"salt\":\"CJMklLYKN68Efch0O+p+mg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test", "Admin" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "76e9fe29-9f8d-405a-8f07-8c91c64c53e2", + "username" : "service-account-workspaces-client", + "emailVerified" : false, + "createdTimestamp" : 1715201249758, + "enabled" : true, + "totp" : false, + "serviceAccountClientId" : "workspaces-client", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test" ], + "clientRoles" : { + "workspaces-client" : [ "uma_protection" ] + }, + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "988744c3-2248-4cba-b55d-31b3d1530a61", + "username" : "stranger1", + "firstName" : "Stranger", + "lastName" : "1", + "email" : "stranger@other.com", + "emailVerified" : true, + "createdTimestamp" : 1715248374124, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "e4d239d6-97ef-410b-a99d-e6eae578bd1e", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715248380363, + "secretData" : "{\"value\":\"eZAGUIFM+hMORHI8BDcsX/O8FHZ21yKorigTuqxIhg59exPvjw5zUYIirGydATLWW2a2ogXLYPqMNgw6vFB/dA==\",\"salt\":\"HIiYwMOlQJn83wzoWMEqpQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "5d0dfaeb-373a-4f28-96a1-eac7629c15ce", + "username" : "test1", + "firstName" : "Test", + "lastName" : "1", + "email" : "test1@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715153965857, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "7c9ed3c9-389b-46f3-9cbf-874071f3561e", + "type" : "password", + "createdDate" : 1715153986510, + "secretData" : "{\"value\":\"ipj7W9FVJiGcIsoY4KkHzQDP4KAlqDcF/t6RZPEPZxpgVqkRrqWzyISCF3MBM4HOO9gsBGLD7VGeO2QsVj36rQ==\",\"salt\":\"BR5J5ojOI+1WuXZcWoAedA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ "/main" ] + }, { + "id" : "0a1f6355-6b7e-49e9-8117-f4d1c53149ff", + "username" : "test2", + "firstName" : "Test", + "lastName" : "2", + "email" : "test2@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203404596, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "f3018661-b319-4c6b-8cb4-133edacc5817", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203414451, + "secretData" : "{\"value\":\"RNo8Ltsr1hsSRgbGzwEHHcA6m0pGHloJ2gEkbcqInYqCpIOlsfaA3MzE2GFaQc9UYja2yN+VUxxPOTuLzv02EQ==\",\"salt\":\"+/vCVU/zzZtJ9jZc+AomMw==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ "/main" ] + }, { + "id" : "05ae40a3-b90f-4a3f-bf72-a12221769b24", + "username" : "test3", + "firstName" : "Test", + "lastName" : "3", + "email" : "test3@test.com", + "emailVerified" : true, + "createdTimestamp" : 1715203783049, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "15918187-5945-4106-8666-8377458b18b9", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1715203795249, + "secretData" : "{\"value\":\"V1Acf5D37cdktf8vSEfvgxktjX6flegkSPETY6B0NSK2I/ji0tGLnxXuvbKXCO5C1thMFhs08+2qRT+LMrXN7w==\",\"salt\":\"M7c0T4JBP6L/8I5Sejij/Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "Reader", "default-roles-test" ], + "notBefore" : 0, + "groups" : [ ] + } ] +} \ No newline at end of file diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Program.cs b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Program.cs new file mode 100644 index 00000000..5608d1e4 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Program.cs @@ -0,0 +1,19 @@ +var builder = DistributedApplication.CreateBuilder(args); + +#if EnableKeycloakImport +var keycloak = builder + .AddKeycloakContainer("keycloak") + .WithDataVolume() + .WithImport("./KeycloakConfiguration/Test-realm.json") + .WithImport("./KeycloakConfiguration/Test-users-0.json"); +#else +var keycloak = builder + .AddKeycloakContainer("keycloak") + .WithDataVolume(); +#endif + +var realm = keycloak.AddRealm("Test"); + +builder.AddProject("api").WithReference(keycloak).WithReference(realm); + +builder.Build().Run(); diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Properties/launchSettings.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..acf4a4cd --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/Properties/launchSettings.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:15056", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16070", + "DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.Development.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Build.props b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Build.props new file mode 100644 index 00000000..7f52c0c5 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Build.props @@ -0,0 +1,11 @@ + + + net8.0 + enable + enable + + + + true + + diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Packages.props b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Packages.props new file mode 100644 index 00000000..a3c452e5 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/Directory.Packages.props @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/KeycloakAspireStarter.sln b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/KeycloakAspireStarter.sln new file mode 100644 index 00000000..706a7c20 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/KeycloakAspireStarter.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "Api\Api.csproj", "{D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppHost", "AppHost\AppHost.csproj", "{2D658505-DDE3-49DF-AF39-2E921C7A984C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDefaults", "ServiceDefaults\ServiceDefaults.csproj", "{6C56AC38-F4CE-492F-BFE5-8444CE785FE4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C22C1E-C3BD-4BBE-8D9F-33DAF1B5CB40}.Release|Any CPU.Build.0 = Release|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D658505-DDE3-49DF-AF39-2E921C7A984C}.Release|Any CPU.Build.0 = Release|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C56AC38-F4CE-492F-BFE5-8444CE785FE4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/README.md b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/README.md new file mode 100644 index 00000000..d36223d2 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/README.md @@ -0,0 +1,46 @@ +# Aspire Keycloak Starter + +## Getting Started + +```csharp +// AppHost/Program.cs +var builder = DistributedApplication.CreateBuilder(args); + +var keycloak = builder.AddKeycloakContainer("keycloak"); + +var realm = keycloak.AddRealm("Test"); + +builder.AddProject("api").WithReference(keycloak).WithReference(realm); + +builder.Build().Run(); +``` + +## Run + +```csharp +dotnet run --project ./AppHost/ +``` + +What it does: + +1. Starts a Keycloak Instance +2. Imports realm and test users (`test1:test`, `test2:test`, `test3:test` (username:password)) +3. Reference to Keycloak adds Keycloak to service discovery +4. Reference to Realm adds `Keycloak__Realm` and `Keycloak__AuthServerUrl` environment variables. + +The only thing is left is to configure the client: + +```csharp +services.AddKeycloakWebApiAuthentication( + configuration, + options => + { + options.Audience = "workspaces-client"; + options.RequireHttpsMetadata = false; + } +); +``` + +## Known Issues + +1. Keycloak container takes about 1 minute to start. In order to use Swagger UI, please reload the page once Keycloak is ready to load `OpenApiSecurityScheme` from discovery endpoint. diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/Extensions.cs b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/Extensions.cs new file mode 100644 index 00000000..e19f9e4f --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +namespace Microsoft.Extensions.Hosting; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry( + this IHostApplicationBuilder builder + ) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder + .Services.AddOpenTelemetry() + .WithMetrics(metrics => + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddProcessInstrumentation() + .AddRuntimeInstrumentation() + .AddKeycloakAuthServicesInstrumentation() + ) + .WithTracing(tracing => + { + if (builder.Environment.IsDevelopment()) + { + // We want to view all traces in development + tracing.SetSampler(new AlwaysOnSampler()); + } + + tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddKeycloakAuthServicesInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters( + this IHostApplicationBuilder builder + ) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace( + builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"] + ); + + if (useOtlpExporter) + { + builder.Services.Configure(logging => + logging.AddOtlpExporter() + ); + builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => + metrics.AddOtlpExporter() + ); + builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => + tracing.AddOtlpExporter() + ); + } + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks( + this IHostApplicationBuilder builder + ) + { + builder + .Services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + app.MapHealthChecks("/health"); + + app.MapHealthChecks( + "/alive", + new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") } + ); + + return app; + } +} diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/ServiceDefaults.csproj b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/ServiceDefaults.csproj new file mode 100644 index 00000000..9a4632df --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/ServiceDefaults/ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + true + + + + + + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/global.json b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/global.json new file mode 100644 index 00000000..bf38f6cd --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/AspireStarterTemplate/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.204", + "rollForward": "latestMajor", + "allowPrerelease": false + } +} diff --git a/src/Keycloak.AuthServices.Templates/Keycloak.AuthServices.Templates.csproj b/src/Keycloak.AuthServices.Templates/Keycloak.AuthServices.Templates.csproj new file mode 100644 index 00000000..5cbeb01d --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/Keycloak.AuthServices.Templates.csproj @@ -0,0 +1,27 @@ + + + + Template + net8.0 + + true + false + content + true + + + + Keycloak.AuthServices.Templates + dotnetnew;templates;keycloak;productivity + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/README.md b/src/Keycloak.AuthServices.Templates/README.md new file mode 100644 index 00000000..0fc6c989 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/README.md @@ -0,0 +1,47 @@ +# Keycloak.AuthServices.Templates + +## AspireStarter + +### Installation + +From source: + +```bash +dotnet new install . +``` + +```bash +dotnet new install Keycloak.AuthServices.Templates +``` + +### Run + +```bash +dotnet new list keycloak +``` + +```bash +❯ dotnet new keycloak-aspire-starter -o $dev/KeycloakAspireStarter --EnableKeycloakImport --dry-run +File actions would have been taken: +# Create: $dev\KeycloakAspireStarer\.gitignore +# Create: $dev\KeycloakAspireStarer\Api\Api.csproj +# Create: $dev\KeycloakAspireStarer\Api\Extensions.OpenApi.cs +# Create: $dev\KeycloakAspireStarer\Api\Program.cs +# Create: $dev\KeycloakAspireStarer\Api\Properties\launchSettings.json +# Create: $dev\KeycloakAspireStarer\Api\appsettings.Development.json +# Create: $dev\KeycloakAspireStarer\Api\appsettings.json +# Create: $dev\KeycloakAspireStarer\AppHost\AppHost.csproj +# Create: $dev\KeycloakAspireStarer\AppHost\KeycloakConfiguration\Test-realm.json +# Create: $dev\KeycloakAspireStarer\AppHost\KeycloakConfiguration\Test-users-0.json +# Create: $dev\KeycloakAspireStarer\AppHost\Program.cs +# Create: $dev\KeycloakAspireStarer\AppHost\Properties\launchSettings.json +# Create: $dev\KeycloakAspireStarer\AppHost\appsettings.Development.json +# Create: $dev\KeycloakAspireStarer\AppHost\appsettings.json +# Create: $dev\KeycloakAspireStarer\KeycloakAspireStarter.sln +# Create: $dev\KeycloakAspireStarer\Directory.Build.props +# Create: $dev\KeycloakAspireStarer\Directory.Packages.props +# Create: $dev\KeycloakAspireStarer\README.md +# Create: $dev\KeycloakAspireStarer\ServiceDefaults\Extensions.cs +# Create: $dev\KeycloakAspireStarer\ServiceDefaults\ServiceDefaults.csproj +# Create: $dev\KeycloakAspireStarer\global.json +``` diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/.template.config/template.json b/src/Keycloak.AuthServices.Templates/WebApiTemplate/.template.config/template.json new file mode 100644 index 00000000..7a00b7fa --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/.template.config/template.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Oleksii Nikiforov", + "classifications": [ + "Common", + "API", + "Keycloak" + ], + "identity": "Keycloak.AuthServices.Templates.WebApi", + "name": "Keycloak WebApi", + "description": "Creates a project integrated with Keycloak", + "shortName": "keycloak-webapi", + "sourceName": "WebApiTemplate", + "preferNameDirectory": true, + "defaultName": "WebApi", + "tags": { + "language": "C#", + "type": "project" + }, + "guids": [], + "postActions": [], + "symbols": {}, + "sources": [] +} diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Build.props b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Build.props new file mode 100644 index 00000000..7f52c0c5 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Build.props @@ -0,0 +1,11 @@ + + + net8.0 + enable + enable + + + + true + + diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Packages.props b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Packages.props new file mode 100644 index 00000000..a3c452e5 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Directory.Packages.props @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/Extensions.OpenApi.cs b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Extensions.OpenApi.cs new file mode 100644 index 00000000..6cbe27cc --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Extensions.OpenApi.cs @@ -0,0 +1,64 @@ +namespace Api; + +using Keycloak.AuthServices.Authentication; +using Keycloak.AuthServices.Common; +using Microsoft.OpenApi.Models; + +public static class ExtensionsOpenApi +{ + public static IServiceCollection AddApplicationOpenApi( + this IServiceCollection services, + IConfiguration configuration + ) + { + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(c => + { + var keycloakOptions = + configuration.GetKeycloakOptions()!; + + c.AddSecurityDefinition( + "oidc", + new OpenApiSecurityScheme + { + Name = "OIDC", + Type = SecuritySchemeType.OpenIdConnect, + OpenIdConnectUrl = new Uri(keycloakOptions.OpenIdConnectUrl!) + } + ); + + c.AddSecurityRequirement( + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "oidc" + } + }, + Array.Empty() + } + } + ); + + c.SwaggerDoc("v1", new OpenApiInfo { Title = $"API (v1)", Version = "v1" }); + }); + + return services; + } + + public static IApplicationBuilder UseApplicationOpenApi(this IApplicationBuilder app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.RoutePrefix = string.Empty; + }); + + return app; + } +} diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/Program.cs b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Program.cs new file mode 100644 index 00000000..6b25e3c1 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Program.cs @@ -0,0 +1,31 @@ +using Api; +using Keycloak.AuthServices.Authentication; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; +var configuration = builder.Configuration; + +services.AddApplicationOpenApi(configuration); + +services.AddKeycloakWebApiAuthentication( + configuration, + options => + { + options.Audience = "workspaces-client"; + options.RequireHttpsMetadata = false; + } +); +services.AddAuthorization(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.UseApplicationOpenApi(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/hello", () => "Hello World!").RequireAuthorization(); + +app.Run(); diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/Properties/launchSettings.json b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Properties/launchSettings.json new file mode 100644 index 00000000..72e95944 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7107;http://localhost:5064", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/README.md b/src/Keycloak.AuthServices.Templates/WebApiTemplate/README.md new file mode 100644 index 00000000..d3a3c019 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/README.md @@ -0,0 +1,11 @@ +# Keycloak + WebApi + +## Configure Keycloak + +Run docker image locally, as described in the [documentation](https://www.keycloak.org/getting-started/getting-started-docker). + +```bash +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](/configuration/configuration-keycloak) diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/WebApiTemplate.csproj b/src/Keycloak.AuthServices.Templates/WebApiTemplate/WebApiTemplate.csproj new file mode 100644 index 00000000..703bb6a5 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/WebApiTemplate.csproj @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.Development.json b/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.Development.json new file mode 100644 index 00000000..dd898030 --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Keycloak": { + "realm": "Test", + "auth-server-url": "http://localhost:8080/", + "ssl-required": "none", + "resource": "workspaces-client", + "verify-token-audience": true, + "credentials": { + "secret": "ze4SQDpbyBlB72kdTCTv8ecSWsJHf2Js" + } + } +} diff --git a/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.json b/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/src/Keycloak.AuthServices.Templates/WebApiTemplate/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}