Salmu alaykoum Mohammed
Hello
// Service Config
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.JsonWebTokens;
using Authentification.Keycloak.Extensions;
using Microsoft.AspNetCore.Components.Authorization;
namespace Authentification.Keycloak
{
public static class KeycloakConfiguration
{
const string authority = "http://localhost:8080/realms/OLP_Realm";
const string clientId = "local_olp_client";
const string policy = "MainPolicy";
const string MS_OIDC_SCHEME = "MicrosoftOidc";
public static IServiceCollection AddKeycloakAuthentificationService(this IServiceCollection services, ConfigurationManager configuration)
{
services.AddAuthentication(MS_OIDC_SCHEME)
.AddOpenIdConnect(MS_OIDC_SCHEME, oidcOptions =>
{
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
oidcOptions.Authority = authority;
oidcOptions.ClientId = clientId;
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
oidcOptions.MapInboundClaims = false;
oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
oidcOptions.SaveTokens = false;
oidcOptions.RequireHttpsMetadata = false;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
services.ConfigureCookieOidcRefresh(CookieAuthenticationDefaults.AuthenticationScheme, MS_OIDC_SCHEME);
services.AddAuthorization();
services.AddCascadingAuthenticationState();
services.AddScoped<AuthenticationStateProvider, PersistingAuthenticationStateProvider>();
return services;
}
}
}
Component
@using Microsoft.AspNetCore.Authentication.Cookies
@using Microsoft.AspNetCore.Authentication.OpenIdConnect
@using Microsoft.AspNetCore.Components.Authorization
@implements IDisposable
@inject NavigationManager NavigationManager
<div class="nav-item px-3">
<AuthorizeView>
<Authorized>
<a class="nav-link" href="authentication/logout">
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> Logout
</a>
</Authorized>
<NotAuthorized>
<a class="nav-link" href="authentication/login">
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> Login
</a>
</NotAuthorized>
</AuthorizeView>
</div>
@code {
private string? currentUrl;
protected override void OnInitialized()
{
currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
NavigationManager.LocationChanged += OnLocationChanged;
}
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
currentUrl = NavigationManager.ToBaseRelativePath(e.Location);
StateHasChanged();
}
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
}
// Class Handler
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System.Xml.Linq;
namespace Authentification.Keycloak.Extensions;
public static class LoginLogoutEndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapLoginAndLogout(this IEndpointRouteBuilder endpoints)
{
var group = endpoints.MapGroup("");
group.MapGet("/login", (string? returnUrl) => TypedResults.Challenge(GetAuthProperties(returnUrl)))
.AllowAnonymous();
// Sign out of the Cookie and OIDC handlers. If you do not sign out with the OIDC handler,
// the user will automatically be signed back in the next time they visit a page that requires authentication
// without being able to choose another account.
// group.MapGet("/logout", (string? returnUrl) => TypedResults.SignOut(GetAuthProperties(returnUrl), [CookieAuthenticationDefaults.AuthenticationScheme, "MicrosoftOidc"]));
group.MapGet("/logout", async (HttpContext context, string? returnUrl) =>
{
// Retrieve the authentication result for the current user
var result = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (result?.Principal != null)
{
// Retrieve the ID token from the properties
var idToken = result.Properties.GetTokenValue("id_token");
if (idToken != null)
{
// Create the logout URL with the id_token_hint
var logoutUri = $"http://localhost:8080/auth/realms/OLP_Realm/protocol/openid-connect/logout?redirect_uri={returnUrl}";
// Sign out from both the local session and the OIDC provider
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await context.SignOutAsync("MicrosoftOidc", new AuthenticationProperties { RedirectUri = logoutUri });
return Results.Redirect(logoutUri);
}
}
// If no ID token or principal, fallback to local sign out and redirect
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Results.Redirect(returnUrl ?? "/");
});
return group;
}
private static AuthenticationProperties GetAuthProperties(string? returnUrl)
{
// TODO: Use HttpContext.Request.PathBase instead.
const string pathBase = "/";
// Prevent open redirects.
if (string.IsNullOrEmpty(returnUrl))
{
returnUrl = pathBase;
}
else if (!Uri.IsWellFormedUriString(returnUrl, UriKind.Relative))
{
returnUrl = new Uri(returnUrl, UriKind.Absolute).PathAndQuery;
}
else if (returnUrl[0] != '/')
{
returnUrl = $"{pathBase}{returnUrl}";
}
return new AuthenticationProperties { RedirectUri = returnUrl };
}
}
// App builder
app.MapGroup("/authentication").MapLoginAndLogout();