Skip to content

Commit

Permalink
add auth
Browse files Browse the repository at this point in the history
  • Loading branch information
afedyanin committed Oct 18, 2023
1 parent eb88564 commit 4bbc4e9
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Keycloak4Blazor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B42857C-FD8
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9C59A7AC-5C91-4BC5-97B7-72B20D752B2C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp", "src\BlazorApp\BlazorApp.csproj", "{2E141D10-E6F4-45F6-94D1-BFE23D0B1B8F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorApp", "src\BlazorApp\BlazorApp.csproj", "{2E141D10-E6F4-45F6-94D1-BFE23D0B1B8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,72 @@ Keycloak for Blazor demo
- [C#/NetStandard OpenID Connect Client Library for native Applications](https://github.com/IdentityModel/IdentityModel.OidcClient)
- [Secure ASP.NET Core Blazor WebAssembly](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/?view=aspnetcore-7.0)

## Keycloak setup

```
docker pull keycloak/keycloak
docker run -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8080:8080 keycloak/keycloak:latest start-dev
http:https://localhost:8080/
```


- Create realm: myrealm
- Create user: afedyanin, set password
- Create group: mygroup
- Add user to group
- Create global role: myrole
- Assign role to user

```
http:https://localhost:8080/realms/myrealm/account
```

- Create Client: blazor-client
- AuthFlow: Standard flow, Direct access grants
- Client Auth - On

Add an audience to a client by using client scopes
- On the left side bar click on “Clients” item.
- Click “blazor-client”
- Open “Client scopes” tab
- Click on “blazor-client-dedicated”, should be on tope of the list of scopes
- From the “Mappers” tab, click “Create a new mapper”
- Pick “Audience” from the list
- specify name: Audience
- include client audience: “blazor-client”
- Click “Save”
Besides “Setup” sub-tab, “Client Scopes” tab has “Evaluate” sub-tab. It might come in handy when you need to figure out effective protocol mappers, effective role scope mappings, the content of access, and id tokens.


Add valid redirect urls: http:https://localhost:5278/*


- Download adapter config

```
{
"realm": "myrealm",
"auth-server-url": "http:https://localhost:8080/",
"ssl-required": "external",
"resource": "blazor-client",
"credentials": {
"secret": "aNZUREfcTwZjh1qiD095SGQnzL6SQWo0"
},
"confidential-port": 0
}
```

```
curl --data "grant_type=password&client_id=blazor-client&username=afedyanin&password=afedyanin&client_secret=aNZUREfcTwZjh1qiD095SGQnzL6SQWo0" localhost:8080/realms/myrealm/protocol/openid-connect/token
```


## App setup


```
dotnet add package Keycloak.AuthServices.Authentication
```
37 changes: 25 additions & 12 deletions src/BlazorApp/App.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true)
{
<RedirectToLogin />
}
else
{
<p role="alert">You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
6 changes: 4 additions & 2 deletions src/BlazorApp/BlazorApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.10" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.12" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
</ItemGroup>

</Project>
36 changes: 34 additions & 2 deletions src/BlazorApp/Client/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using BlazorApp;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

namespace BlazorApp;
Expand All @@ -11,8 +11,40 @@ public static async Task Main(string[] args)
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

var services = builder.Services;
RegisterHttpClient(builder, services);

builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.MetadataUrl = "http:https://localhost:8080/realms/myrealm/.well-known/openid-configuration";
options.ProviderOptions.Authority = "http:https://localhost:8080/realms/myrealm";
options.ProviderOptions.ClientId = "blazor-client";
options.ProviderOptions.ResponseType = "id_token token";
options.UserOptions.NameClaim = "preferred_username";
options.UserOptions.RoleClaim = "roles";
options.UserOptions.ScopeClaim = "scope";
});

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();
var app = builder.Build();

await app.RunAsync();
}

private static void RegisterHttpClient(
WebAssemblyHostBuilder builder,
IServiceCollection services)
{
var httpClientName = "Default";

services.AddHttpClient(httpClientName,
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

services.AddScoped(
sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient(httpClientName));
}
}
9 changes: 9 additions & 0 deletions src/BlazorApp/Pages/Authentication.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@page "/authentication/{action}"

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action" />

@code{
[Parameter] public string? Action { get; set; }
}
22 changes: 22 additions & 0 deletions src/BlazorApp/Shared/LoginDisplay.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

@inject NavigationManager Navigation

<AuthorizeView>
<Authorized>
<a href="authentication/profile">Hello, @context.User.Identity?.Name!</a>
<button class="nav-link btn btn-link" @onclick="BeginLogOut">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/register">Register</a>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>

@code {
private void BeginLogOut()
{
Navigation.NavigateToLogout("authentication/logout");
}
}
9 changes: 9 additions & 0 deletions src/BlazorApp/Shared/RedirectToLogin.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@inject NavigationManager Navigation

@code {
protected override void OnInitialized()
{
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}

3 changes: 2 additions & 1 deletion src/BlazorApp/_Imports.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@using System.Net.Http
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
Expand Down
27 changes: 14 additions & 13 deletions src/BlazorApp/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@
</head>

<body>
<div id="app">
<svg class="loading-progress">
<circle r="40%" cx="50%" cy="50%" />
<circle r="40%" cx="50%" cy="50%" />
</svg>
<div class="loading-progress-text"></div>
</div>
<div id="app">
<svg class="loading-progress">
<circle r="40%" cx="50%" cy="50%" />
<circle r="40%" cx="50%" cy="50%" />
</svg>
<div class="loading-progress-text"></div>
</div>

<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
</body>

</html>

0 comments on commit 4bbc4e9

Please sign in to comment.