Organization Authorization
cs
using System.Security.Claims;
using Keycloak.AuthServices.Authorization;
using Keycloak.AuthServices.Authorization.Requirements;
using Keycloak.AuthServices.Common.Claims;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
var services = builder.Services;
services.AddKeycloakWebApiAuthentication(configuration);
services
.AddKeycloakAuthorization()
.AddAuthorizationBuilder()
.AddPolicy(
"AcmeOnly",
policy => policy.RequireOrganizationMembership("acme-corp").RequireRealmRoles("user")
);
var app = builder.Build();
app.UseAuthentication().UseAuthorization();
// Requires membership in any organization
app.MapGet(
"/orgs",
(ClaimsPrincipal user) =>
{
var orgs = user.GetOrganizations();
return Results.Ok(
orgs.Select(o => new
{
o.Alias,
o.Id,
o.Attributes,
})
);
}
)
.RequireOrganizationMembership();
// Requires membership in the specific organization (resolved from route)
app.MapGet(
"/orgs/{orgId}/projects",
(string orgId, ClaimsPrincipal user) =>
Results.Ok(new { Organization = orgId, Projects = new[] { "project-alpha" } })
)
.RequireOrganizationMembership("{orgId}");
// Requires membership in a specific organization (static)
app.MapGet("/acme/settings", () => Results.Ok(new { Theme = "dark", Tier = "enterprise" }))
.RequireOrganizationMembership("acme-corp");
// Policy-based: combines org membership with realm roles
app.MapGet("/acme/admin", () => Results.Ok(new { Message = "Acme admin area" }))
.RequireAuthorization("AcmeOnly");
// Header-based: org resolved from X-Organization header (custom resolver)
app.MapGet(
"/tenant/projects",
(ClaimsPrincipal user) =>
Results.Ok(new { Projects = new[] { "project-from-header-tenant" } })
)
.RequireOrganizationMembership<RouteHandlerBuilder, HeaderParameterResolver>(
"{X-Organization}"
);
// Imperative: check access to a specific org programmatically
app.MapGet(
"/check/{orgId}",
async (string orgId, ClaimsPrincipal user, IAuthorizationService authorizationService) =>
{
var result = await authorizationService.AuthorizeAsync(
user,
null,
new OrganizationRequirement(orgId)
);
return result.Succeeded
? Results.Ok(new { OrgId = orgId, Access = true })
: Results.Forbid();
}
);
// Imperative: filter a list of resources by org membership
app.MapGet(
"/workspaces",
async (ClaimsPrincipal user, IAuthorizationService authorizationService) =>
{
var allWorkspaces = new[]
{
new
{
Id = 1,
Name = "Acme Platform",
Org = "acme-corp",
},
new
{
Id = 2,
Name = "Partner Portal",
Org = "partner-inc",
},
new
{
Id = 3,
Name = "Startup MVP",
Org = "startup-co",
},
};
var accessible = new List<object>();
foreach (var workspace in allWorkspaces)
{
var result = await authorizationService.AuthorizeAsync(
user,
null,
new OrganizationRequirement(workspace.Org)
);
if (result.Succeeded)
{
accessible.Add(workspace);
}
}
return Results.Ok(accessible);
}
);
await app.RunAsync();See sample source code: keycloak-authorization-services-dotnet/tree/main/samples/OrganizationAuthorization