Skip to content

Commit

Permalink
Allow configuring claim requirements for frontend authorization (#3619)
Browse files Browse the repository at this point in the history
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 <git@drewnoakes.com>
  • Loading branch information
github-actions[bot] and drewnoakes authored Apr 11, 2024
1 parent 455f6cd commit 5ca550a
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Validates that the the expected claim and value are present.
/// </summary>
/// <remarks>
/// Checks are controlled by configuration.
///
/// If <see cref="OpenIdConnectOptions.RequiredClaimType"/> is non-empty, a requirement for the claim is added.
///
/// If a claim is being checked and <see cref="OpenIdConnectOptions.RequiredClaimValue"/> is non-empty, then the
/// requirement is extended to also validate the specified value.
/// </remarks>
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;
}
}
14 changes: 14 additions & 0 deletions src/Aspire.Dashboard/Configuration/DashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ public sealed class OpenIdConnectOptions
public string NameClaimType { get; set; } = "name";
public string UsernameClaimType { get; set; } = "preferred_username";

/// <summary>
/// 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 <see cref="RequiredClaimValue"/>
/// is also specified, then the value of this claim must also match <see cref="RequiredClaimValue"/>.
/// </summary>
public string RequiredClaimType { get; set; } = "";

/// <summary>
/// Gets the optional value of the <see cref="RequiredClaimType"/> claim for users authenticated via
/// OpenID Connect. If specified, users not having this value for the corresponding claim type are
/// rejected.
/// </summary>
public string RequiredClaimValue { get; set; } = "";

public string[] GetNameClaimTypes()
{
Debug.Assert(_nameClaimTypes is not null, "Should have been parsed during validation.");
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/DashboardWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/Aspire.Dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit 5ca550a

Please sign in to comment.