Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Aspire Hosting package #111

Merged
merged 6 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion dotnet-tools.json → .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -15,4 +21,4 @@
]
}
}
}
}
14 changes: 14 additions & 0 deletions KeycloakAuthorizationServicesDotNet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
12 changes: 11 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
]
}
]
Expand Down
122 changes: 122 additions & 0 deletions docs/devex/aspire.md
Original file line number Diff line number Diff line change
@@ -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<Projects.Api>("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<Projects.Api>("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.
143 changes: 143 additions & 0 deletions docs/devex/templates.md
Original file line number Diff line number Diff line change
@@ -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: <http://localhost:8080/>. Use `admin:admin` as credentials.
28 changes: 28 additions & 0 deletions docs/examples/aspire-web-api.md
Original file line number Diff line number Diff line change
@@ -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)
Loading
Loading