LANCommander/LANCommander.Server/UI/Pages/Settings/Authentication/Components/ExternalProviderEditor.razor
Pat Hartl f1fa66b632 Improve advanced auth provider editing, add role claim mapping
- Added auto discovery of scopes and claim mappings to auth provider editor
- Added the ability to map claims to roles
- Auto-provision users logged in over external auth

Ref #411
2026-06-21 02:03:51 -05:00

178 lines
7.8 KiB
Text

@using LANCommander.Server.Settings.Enums
@using LANCommander.Server.Settings.Models
@inject IJSRuntime JSRuntime
<Flex Vertical Gap="FlexGap.Large">
@if (!_providers.Any())
{
<Empty Description="@("No external authentication providers have been configured")" />
}
<Collapse>
@foreach (var externalProvider in _providers)
{
<Panel Header="@externalProvider.Name">
<ExtraTemplate>
<Button Type="ButtonType.Text" Icon="@IconType.Outline.Close" Size="ButtonSize.Small" Danger OnClick="() => Remove(externalProvider)"/>
</ExtraTemplate>
<ChildContent>
<Form Model="externalProvider" Layout="FormLayout.Vertical">
<FormItem Label="Name">
<Space Style="width: 100%">
<SpaceItem Style="width: 100%">
<Input @bind-Value="context.Name" BindOnInput/>
</SpaceItem>
<SpaceItem>
<Space>
@if (_templates.Any())
{
<SpaceItem>
<Dropdown>
<Overlay>
<Menu>
@foreach (var template in _templates)
{
<MenuItem OnClick="() => UseTemplate(externalProvider, template)">
@template.Name
</MenuItem>
}
</Menu>
</Overlay>
<ChildContent>
<Button>Templates</Button>
</ChildContent>
</Dropdown>
</SpaceItem>
}
@if (!String.IsNullOrWhiteSpace(externalProvider.Documentation))
{
<SpaceItem>
<Tooltip Title="Documentation">
<Button Type="ButtonType.Text" Icon="@IconType.Outline.Question" OnClick="() => OpenDocumentation(externalProvider)"/>
</Tooltip>
</SpaceItem>
}
</Space>
</SpaceItem>
</Space>
</FormItem>
<FormItem Label="Color">
<Input @bind-Value="context.Color" />
</FormItem>
<FormItem Label="Icon">
<Input @bind-Value="context.Icon" />
</FormItem>
<FormItem Label="Type">
<EnumSelect TEnum="AuthenticationProviderType" @bind-Value="context.Type"/>
</FormItem>
<FormItem Label="Client ID">
<Input @bind-Value="context.ClientId"/>
</FormItem>
<FormItem Label="Client Secret">
<InputPassword @bind-Value="context.ClientSecret"/>
</FormItem>
@if (context.Type == AuthenticationProviderType.OAuth2)
{
<FormItem Label="Authorization Endpoint">
<Input @bind-Value="context.AuthorizationEndpoint"/>
</FormItem>
<FormItem Label="Token Endpoint">
<Input @bind-Value="context.TokenEndpoint"/>
</FormItem>
<FormItem Label="User Info Endpoint">
<Input @bind-Value="context.UserInfoEndpoint"/>
</FormItem>
}
else if (context.Type == AuthenticationProviderType.OpenIdConnect)
{
<FormItem Label="Configuration Endpoint">
<Input @bind-Value="context.ConfigurationUrl"/>
</FormItem>
}
<FormItem Label="Scopes">
<ScopesEditor @bind-Values="context.Scopes"/>
</FormItem>
<FormItem Label="Claim Mappings">
<ClaimMappingsEditor @bind-Values="context.ClaimMappings" @bind-Scopes="context.Scopes" ConfigurationUrl="@context.ConfigurationUrl"/>
</FormItem>
</Form>
</ChildContent>
</Panel>
}
</Collapse>
<Flex Justify="FlexJustify.Center">
<Button OnClick="Add" Type="ButtonType.Primary">Add Provider</Button>
</Flex>
</Flex>
@code {
[Parameter] public IEnumerable<AuthenticationProvider> Values { get; set; }
[Parameter] public EventCallback<IEnumerable<AuthenticationProvider>> ValuesChanged { get; set; }
List<AuthenticationProvider> _templates = new();
List<AuthenticationProvider> _providers = new();
AuthenticationProvider _selectedTemplate;
protected override async Task OnInitializedAsync()
{
_templates = await AuthenticationService.GetAuthenticationProviderTemplatesAsync();
}
protected override void OnParametersSet()
{
_providers = Values.ToList();
}
void OpenDocumentation(AuthenticationProvider authenticationProvider)
{
JSRuntime.InvokeVoidAsync("open", authenticationProvider.Documentation, "_blank");
}
async Task Add()
{
_providers.Add(new AuthenticationProvider());
if (ValuesChanged.HasDelegate)
await ValuesChanged.InvokeAsync(_providers);
}
async Task Remove(AuthenticationProvider authenticationProvider)
{
_providers.Remove(authenticationProvider);
if (ValuesChanged.HasDelegate)
await ValuesChanged.InvokeAsync(_providers);
}
async Task UseTemplate(AuthenticationProvider authenticationProvider, AuthenticationProvider template)
{
authenticationProvider.ClaimMappings = template.ClaimMappings;
authenticationProvider.ClientId = template.ClientId;
authenticationProvider.ClientSecret = template.ClientSecret;
authenticationProvider.Color = template.Color;
authenticationProvider.Documentation = template.Documentation;
authenticationProvider.Icon = template.Icon;
authenticationProvider.Name = template.Name;
authenticationProvider.Scopes = template.Scopes;
authenticationProvider.ConfigurationUrl = template.ConfigurationUrl;
authenticationProvider.AuthorizationEndpoint = template.AuthorizationEndpoint;
authenticationProvider.TokenEndpoint = template.TokenEndpoint;
authenticationProvider.UserInfoEndpoint = template.UserInfoEndpoint;
await InvokeAsync(StateHasChanged);
}
}