From 5ca550a07e35537e775b40fe0d05fbcc40edf9ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 23:24:38 +0000 Subject: [PATCH] Allow configuring claim requirements for frontend authorization (#3619) Adds optional configuration that checks a given claim of a user authenticated via OIDC. Two new configuration values are: - `Dashboard:Frontend:OpenIdConnect:RequireClaimType` specifies the (optional) claim that be present for authorized users. Defaults to empty. - `Dashboard:Frontend:OpenIdConnect:RequireClaimValue` specifies the (optional) value of the required claim. Only used if `Dashboard:Frontend:OpenIdConnect:RequireClaimType` is also specified. Defaults to empty. Co-authored-by: Drew Noakes --- .../AuthorizationPolicyBuilderExtensions.cs | 41 +++++++++++++++++++ .../Configuration/DashboardOptions.cs | 14 +++++++ .../DashboardWebApplication.cs | 4 +- src/Aspire.Dashboard/README.md | 2 + 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/Aspire.Dashboard/Authentication/OpenIdConnect/AuthorizationPolicyBuilderExtensions.cs diff --git a/src/Aspire.Dashboard/Authentication/OpenIdConnect/AuthorizationPolicyBuilderExtensions.cs b/src/Aspire.Dashboard/Authentication/OpenIdConnect/AuthorizationPolicyBuilderExtensions.cs new file mode 100644 index 0000000000..fa3efd9b14 --- /dev/null +++ b/src/Aspire.Dashboard/Authentication/OpenIdConnect/AuthorizationPolicyBuilderExtensions.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Authorization; +using OpenIdConnectOptions = Aspire.Dashboard.Configuration.OpenIdConnectOptions; + +namespace Aspire.Dashboard.Authentication.OpenIdConnect; + +internal static class AuthorizationPolicyBuilderExtensions +{ + /// + /// Validates that the the expected claim and value are present. + /// + /// + /// Checks are controlled by configuration. + /// + /// If is non-empty, a requirement for the claim is added. + /// + /// If a claim is being checked and is non-empty, then the + /// requirement is extended to also validate the specified value. + /// + public static AuthorizationPolicyBuilder RequireOpenIdClaims(this AuthorizationPolicyBuilder builder, OpenIdConnectOptions options) + { + var claimType = options.RequiredClaimType; + var claimValue = options.RequiredClaimValue; + + if (!string.IsNullOrWhiteSpace(claimType)) + { + if (!string.IsNullOrWhiteSpace(claimValue)) + { + builder.RequireClaim(claimType, claimValue); + } + else + { + builder.RequireClaim(claimType); + } + } + + return builder; + } +} diff --git a/src/Aspire.Dashboard/Configuration/DashboardOptions.cs b/src/Aspire.Dashboard/Configuration/DashboardOptions.cs index 504fea49fb..c390cb06bc 100644 --- a/src/Aspire.Dashboard/Configuration/DashboardOptions.cs +++ b/src/Aspire.Dashboard/Configuration/DashboardOptions.cs @@ -180,6 +180,20 @@ public sealed class OpenIdConnectOptions public string NameClaimType { get; set; } = "name"; public string UsernameClaimType { get; set; } = "preferred_username"; + /// + /// Gets the optional name of a claim that users authenticated via OpenID Connect are required to have. + /// If specified, users without this claim will be rejected. If + /// is also specified, then the value of this claim must also match . + /// + public string RequiredClaimType { get; set; } = ""; + + /// + /// Gets the optional value of the claim for users authenticated via + /// OpenID Connect. If specified, users not having this value for the corresponding claim type are + /// rejected. + /// + public string RequiredClaimValue { get; set; } = ""; + public string[] GetNameClaimTypes() { Debug.Assert(_nameClaimTypes is not null, "Should have been parsed during validation."); diff --git a/src/Aspire.Dashboard/DashboardWebApplication.cs b/src/Aspire.Dashboard/DashboardWebApplication.cs index 2a07215038..6a5566ba38 100644 --- a/src/Aspire.Dashboard/DashboardWebApplication.cs +++ b/src/Aspire.Dashboard/DashboardWebApplication.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Security.Claims; using Aspire.Dashboard.Authentication; +using Aspire.Dashboard.Authentication.OpenIdConnect; using Aspire.Dashboard.Authentication.OtlpApiKey; using Aspire.Dashboard.Authentication.OtlpConnection; using Aspire.Dashboard.Components; @@ -509,11 +510,10 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder, Dashb switch (dashboardOptions.Frontend.AuthMode) { case FrontendAuthMode.OpenIdConnect: - // Frontend is secured with OIDC, so delegate to that authentication scheme. options.AddPolicy( name: FrontendAuthorizationDefaults.PolicyName, policy: new AuthorizationPolicyBuilder(FrontendAuthenticationDefaults.AuthenticationScheme) - .RequireAuthenticatedUser() + .RequireOpenIdClaims(options: dashboardOptions.Frontend.OpenIdConnect) .Build()); break; case FrontendAuthMode.BrowserToken: diff --git a/src/Aspire.Dashboard/README.md b/src/Aspire.Dashboard/README.md index f5aae25bd9..948eaa1be6 100644 --- a/src/Aspire.Dashboard/README.md +++ b/src/Aspire.Dashboard/README.md @@ -47,6 +47,8 @@ The dashboard's frontend supports OpenID Connect (OIDC). Set `Dashboard:Frontend - Other properties of [`OpenIdConnectOptions`](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.builder.openidconnectoptions) specified in configuration container `Authentication:Schemes:OpenIdConnect:*` - `Dashboard:Frontend:OpenIdConnect:NameClaimType` specifies the claim type(s) that should be used to display the authenticated user's full name. Can be a single claim type or a comma-delimited list of claim types. Defaults to `name`. - `Dashboard:Frontend:OpenIdConnect:UsernameClaimType` specifies the claim type(s) that should be used to display the authenticated user's username. Can be a single claim type or a comma-delimited list of claim types. Defaults to `preferred_username`. +- `Dashboard:Frontend:OpenIdConnect:RequireClaimType` specifies the (optional) claim that be present for authorized users. Defaults to empty. +- `Dashboard:Frontend:OpenIdConnect:RequireClaimValue` specifies the (optional) value of the required claim. Only used if `Dashboard:Frontend:OpenIdConnect:RequireClaimType` is also specified. Defaults to empty. It may also be run unsecured. Set `Dashboard:Frontend:AuthMode` to `Unsecured`. The frontend endpoint will allow anonymous access. This setting is used during local development, but is not recommended if you attempt to host the dashboard in other settings.