Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit e07fafb
Merge: 06d5f2f f728973
Author: James Newton-King <james@newtonking.com>
Date:   Wed Apr 3 08:02:33 2024 +0800

    Merge branch 'main' into jamesnk/browser-token

commit 06d5f2f
Author: James Newton-King <james@newtonking.com>
Date:   Wed Apr 3 07:38:51 2024 +0800

    PR feedback

commit 70af53b
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Tue Apr 2 09:17:43 2024 -0700

    Fix link

commit 1ce4ba6
Author: James Newton-King <james@newtonking.com>
Date:   Tue Apr 2 17:16:15 2024 +0800

    PR feedback

commit 06d2eeb
Author: James Newton-King <james@newtonking.com>
Date:   Tue Apr 2 09:25:54 2024 +0800

    Validate auth mode in middleware. More tests

commit 7708564
Author: James Newton-King <james@newtonking.com>
Date:   Tue Apr 2 09:05:09 2024 +0800

    Disable validate token endpoint based on config

commit 8a0743b
Author: James Newton-King <james@newtonking.com>
Date:   Mon Apr 1 22:12:35 2024 +0800

    Fix tests

commit 3011bd7
Author: James Newton-King <james@newtonking.com>
Date:   Mon Apr 1 21:13:08 2024 +0800

    Clean up

commit 1f5cecb
Author: James Newton-King <james@newtonking.com>
Date:   Mon Apr 1 21:01:36 2024 +0800

    More tests

commit a3b62f3
Author: James Newton-King <james@newtonking.com>
Date:   Mon Apr 1 14:19:43 2024 +0800

    Token -> Login. Initial tests

commit 46a0071
Author: James Newton-King <james@newtonking.com>
Date:   Sat Mar 30 13:48:44 2024 +0800

    Fix

commit 0a48233
Author: James Newton-King <james@newtonking.com>
Date:   Fri Mar 29 22:24:47 2024 +0800

    Minor tweaks

commit 4acf521
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Thu Mar 28 13:24:47 2024 -0700

    Revert unintentional change

commit 745ddac
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Thu Mar 28 13:06:12 2024 -0700

    More UI tweaks

commit d189e74
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 22:06:17 2024 +0800

    Send token from host, support logging in via url, print url in host console

commit 6cc78f8
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 16:26:55 2024 +0800

    Use constant time compare method

commit ffa3428
Merge: f88bbf8 39191ca
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 15:57:00 2024 +0800

    Merge branch 'main' into jamesnk/browser-token

commit f88bbf8
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 15:44:10 2024 +0800

    Error handling

commit 15e2de8
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 15:26:54 2024 +0800

    Error message

commit 65c22ef
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 23:17:00 2024 -0700

    A few tweaks

commit a02db11
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 14:08:24 2024 +0800

    Fix test

commit e455412
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 13:56:36 2024 +0800

    Comment

commit 22161aa
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 13:54:56 2024 +0800

    Validate token

commit 4a6d32a
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 22:41:55 2024 -0700

    Add missed localization

commit ae4da9c
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 22:38:20 2024 -0700

    Feedback, localization, etc

commit d6d1734
Merge: 0e362bc 7641854
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 12:13:03 2024 +0800

    Merge

commit 0e362bc
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 10:42:08 2024 +0800

    Clean up

commit 1a17bf2
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 10:26:08 2024 +0800

    More

commit 399f6d5
Author: James Newton-King <james@newtonking.com>
Date:   Thu Mar 28 10:25:09 2024 +0800

    Undo main layout changes

commit f8fe507
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 18:04:48 2024 -0700

    Fix version color, tweak background color for more obvious shadow

commit 6778ee8
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 17:50:20 2024 -0700

    Less purple, add title

commit b8109dc
Author: Tim Mulholland <timulhol@microsoft.com>
Date:   Wed Mar 27 13:07:18 2024 -0700

    Add styling to token page

commit 402a84a
Author: James Newton-King <james@newtonking.com>
Date:   Wed Mar 27 21:03:49 2024 +0800

    Auth enabled authorization

commit 80c62a0
Author: James Newton-King <james@newtonking.com>
Date:   Wed Mar 27 18:40:32 2024 +0800

    Clean up

commit 59f3f05
Author: James Newton-King <james@newtonking.com>
Date:   Wed Mar 27 18:32:58 2024 +0800

    Add token auth to dashboard frontend
  • Loading branch information
JamesNK committed Apr 3, 2024
1 parent f728973 commit 4b34931
Show file tree
Hide file tree
Showing 67 changed files with 3,079 additions and 150 deletions.
15 changes: 15 additions & 0 deletions src/Aspire.Dashboard/Aspire.Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Resources\Login.Designer.cs">
<DependentUpon>Login.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\StructuredLogs.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
Expand Down Expand Up @@ -142,6 +147,13 @@
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Login.resx">
<XlfOutputItem>EmbeddedResource</XlfOutputItem>
<SubType>Designer</SubType>
<LastGenOutput>Login.Designer.cs</LastGenOutput>
<XlfSourceFormat>Resx</XlfSourceFormat>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\StructuredLogs.resx">
<XlfSourceFormat>Resx</XlfSourceFormat>
<XlfOutputItem>EmbeddedResource</XlfOutputItem>
Expand Down Expand Up @@ -186,5 +198,8 @@
<Compile Include="$(SharedDir)Model\KnownResourceTypes.cs" Link="Utils\KnownResourceTypes.cs" />
<Compile Include="$(SharedDir)CircularBuffer.cs" Link="Otlp\Storage\CircularBuffer.cs" />
<Compile Include="$(SharedDir)DashboardConfigNames.cs" Link="Utils\DashboardConfigNames.cs" />
<Compile Include="$(SharedDir)TokenGenerator.cs" Link="Utils\TokenGenerator.cs" />
<Compile Include="$(SharedDir)LoggingHelpers.cs" Link="Utils\LoggingHelpers.cs" />
<Compile Include="$(SharedDir)StringUtils.cs" Link="Utils\StringUtils.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using Aspire.Dashboard.Configuration;
using Aspire.Dashboard.Utils;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

Expand Down Expand Up @@ -34,9 +32,9 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
return Task.FromResult(AuthenticateResult.Fail($"Multiple '{ApiKeyHeaderName}' headers in request."));
}

if (!CompareApiKey(options.GetPrimaryApiKeyBytes(), apiKey.ToString()))
if (!CompareHelpers.CompareKey(options.GetPrimaryApiKeyBytes(), apiKey.ToString()))
{
if (options.GetSecondaryApiKeyBytes() is not { } secondaryBytes || !CompareApiKey(secondaryBytes, apiKey.ToString()))
if (options.GetSecondaryApiKeyBytes() is not { } secondaryBytes || !CompareHelpers.CompareKey(secondaryBytes, apiKey.ToString()))
{
return Task.FromResult(AuthenticateResult.Fail($"Incoming API key from '{ApiKeyHeaderName}' header doesn't match configured API key."));
}
Expand All @@ -49,50 +47,6 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()

return Task.FromResult(AuthenticateResult.NoResult());
}

// This method is used to compare two API keys in a way that avoids timing attacks.
private static bool CompareApiKey(byte[] expectedApiKeyBytes, string requestApiKey)
{
const int StackAllocThreshold = 256;

var requestByteCount = Encoding.UTF8.GetByteCount(requestApiKey);

// API key will never match if lengths are different. But still do all the work to avoid timing attacks.
var lengthsEqual = expectedApiKeyBytes.Length == requestByteCount;

var requestSpanLength = Math.Max(requestByteCount, expectedApiKeyBytes.Length);
byte[]? requestPooled = null;
var requestBytesSpan = (requestSpanLength <= StackAllocThreshold ?
stackalloc byte[StackAllocThreshold] :
(requestPooled = RentClearedArray(requestSpanLength))).Slice(0, requestSpanLength);

try
{
// Always succeeds because the byte span is always as big or bigger than required.
Encoding.UTF8.GetBytes(requestApiKey, requestBytesSpan);

// Trim request bytes to the same length as expected bytes. Need to be the same size for fixed time comparison.
var equals = CryptographicOperations.FixedTimeEquals(expectedApiKeyBytes, requestBytesSpan.Slice(0, expectedApiKeyBytes.Length));

return equals && lengthsEqual;
}
finally
{
if (requestPooled != null)
{
ArrayPool<byte>.Shared.Return(requestPooled);
}
}

static byte[] RentClearedArray(int byteCount)
{
// UTF8 bytes are copied into the array but remaining bytes are untouched.
// Because all bytes in the array are compared, clear the array to avoid comparing previous data.
var array = ArrayPool<byte>.Shared.Rent(byteCount);
Array.Clear(array);
return array;
}
}
}

public static class OtlpApiKeyAuthenticationDefaults
Expand Down
95 changes: 95 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/AspireLogo.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<svg width="@Height" height="@Width" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_449_831" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="22">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.39001 12C4.49001 12 3.67 12.4799 3.22 13.2499L6.67 7.27994L6.6817 7.25982L9.84001 1.79005C10.05 1.43005 10.36 1.11005 10.75 0.880049C11.14 0.650049 11.57 0.550049 12 0.550049C12.86 0.550049 13.7 0.990049 14.17 1.80005L17.33 7.28005L23.67 18.25C23.88 18.62 24 19.05 24 19.5C24 20.88 22.88 22 21.5 22H8.27002C8.27001 22 8.27002 22 8.27002 22H2.5C1.12 22 0 20.88 0 19.5C0 19.05 0.12 18.62 0.33 18.25L3.22 13.2499C3.67 12.4799 4.49001 12 5.39001 12C5.39002 12 5.39001 12 5.39001 12Z" fill="url(#paint0_linear_449_831)" />
</mask>
<g mask="url(#mask0_449_831)">
<path d="M20.06 12H13.72L11 7.28005C10.79 6.91005 10.48 6.59005 10.08 6.37005C8.88998 5.67005 7.35998 6.08005 6.66998 7.28005L9.83998 1.79005C10.05 1.43005 10.36 1.11005 10.75 0.880049C11.14 0.650049 11.57 0.550049 12 0.550049C12.86 0.550049 13.7 0.990049 14.17 1.80005L17.33 7.28005L20.06 12Z" fill="url(#paint1_linear_449_831)" />
<g filter="url(#filter0_dd_449_831)">
<path d="M5.38997 11.9999H13.72L11 7.27994C10.79 6.90994 10.48 6.58994 10.08 6.36994C8.88997 5.66994 7.35997 6.07994 6.66997 7.27994L3.21997 13.2499C3.66997 12.4799 4.48997 11.9999 5.38997 11.9999Z" fill="url(#paint2_linear_449_831)" />
<path d="M21.5 22C22.88 22 24 20.88 24 19.5C24 19.05 23.88 18.62 23.67 18.25L20.06 12L13.72 11.9999L17.33 18.25C17.55 18.62 17.67 19.05 17.67 19.5C17.67 20.88 16.55 22 15.17 22H21.5Z" fill="url(#paint3_linear_449_831)" />
</g>
<g filter="url(#filter1_dd_449_831)">
<path d="M17.67 19.5C17.67 20.88 16.55 22 15.17 22H8.27002C9.65002 22 10.77 20.88 10.77 19.5C10.77 19.05 10.65 18.62 10.44 18.25L7.55001 13.25C7.52002 13.19 7.48001 13.14 7.44001 13.08C6.99001 12.42 6.23001 12 5.39001 12H13.72L17.33 18.25C17.55 18.62 17.67 19.05 17.67 19.5Z" fill="url(#paint4_linear_449_831)" />
</g>
<g filter="url(#filter2_dd_449_831)">
<path d="M10.77 19.5C10.77 20.88 9.65 22 8.27 22H2.5C1.12 22 0 20.88 0 19.5C0 19.05 0.12 18.62 0.33 18.25L3.22 13.25C3.67 12.48 4.49 12 5.39 12C6.23 12 6.99 12.42 7.44 13.08C7.48 13.14 7.52 13.19 7.55 13.25L10.44 18.25C10.65 18.62 10.77 19.05 10.77 19.5Z" fill="url(#paint5_linear_449_831)" />
</g>
</g>
<defs>
<filter id="filter0_dd_449_831" x="1.21997" y="4.52808" width="24.78" height="19.9719" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.095" />
<feGaussianBlur stdDeviation="0.095" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_449_831" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.5" />
<feGaussianBlur stdDeviation="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0" />
<feBlend mode="normal" in2="effect1_dropShadow_449_831" result="effect2_dropShadow_449_831" />
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_449_831" result="shape" />
</filter>
<filter id="filter1_dd_449_831" x="3.39001" y="10.5" width="16.28" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.095" />
<feGaussianBlur stdDeviation="0.095" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_449_831" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.5" />
<feGaussianBlur stdDeviation="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0" />
<feBlend mode="normal" in2="effect1_dropShadow_449_831" result="effect2_dropShadow_449_831" />
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_449_831" result="shape" />
</filter>
<filter id="filter2_dd_449_831" x="-2" y="10.5" width="14.77" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.095" />
<feGaussianBlur stdDeviation="0.095" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_449_831" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="0.5" />
<feGaussianBlur stdDeviation="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0" />
<feBlend mode="normal" in2="effect1_dropShadow_449_831" result="effect2_dropShadow_449_831" />
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_449_831" result="shape" />
</filter>
<linearGradient id="paint0_linear_449_831" x1="1.88475" y1="11.1667" x2="10.31" y2="23.1443" gradientUnits="userSpaceOnUse">
<stop stop-color="#CBBFF2" />
<stop offset="1" stop-color="#B9AAEE" />
</linearGradient>
<linearGradient id="paint1_linear_449_831" x1="9.6127" y1="-0.685575" x2="16.8764" y2="13.8912" gradientUnits="userSpaceOnUse">
<stop stop-color="#7455DD" />
<stop stop-color="#6745DA" />
<stop offset="1" stop-color="#512BD4" />
</linearGradient>
<linearGradient id="paint2_linear_449_831" x1="7.90532" y1="3.78438" x2="19.1767" y2="23.0023" gradientUnits="userSpaceOnUse">
<stop stop-color="#856AE1" />
<stop offset="1" stop-color="#7455DD" />
</linearGradient>
<linearGradient id="paint3_linear_449_831" x1="7.90532" y1="3.78438" x2="19.1767" y2="23.0023" gradientUnits="userSpaceOnUse">
<stop stop-color="#856AE1" />
<stop offset="1" stop-color="#7455DD" />
</linearGradient>
<linearGradient id="paint4_linear_449_831" x1="5.4257" y1="9.22222" x2="13.2216" y2="21.4193" gradientUnits="userSpaceOnUse">
<stop stop-color="#A895E9" />
<stop offset="1" stop-color="#9780E5" />
</linearGradient>
<linearGradient id="paint5_linear_449_831" x1="1.88475" y1="11.1667" x2="10.31" y2="23.1443" gradientUnits="userSpaceOnUse">
<stop stop-color="#CBBFF2" />
<stop offset="1" stop-color="#B9AAEE" />
</linearGradient>
</defs>
</svg>

@code {
[Parameter]
public int Height { get; set; } = 24;

[Parameter]
public int Width { get; set; } = 24;
}
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Dialogs/SettingsDialog.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Utils
@inject IStringLocalizer<Dialogs> Loc

<FluentStack Orientation="Orientation.Vertical" Style="display: contents">
Expand All @@ -8,5 +9,5 @@
<FluentRadio Value="@Aspire.Dashboard.Model.ThemeManager.ThemeSettingDark">@Loc[nameof(Dialogs.SettingsDialogDarkTheme)]</FluentRadio>
</FluentRadioGroup>
<div style="height: 100%;"></div>
<div class="version">@string.Format(Loc[nameof(Dialogs.SettingsDialogVersion)], s_version)</div>
<div class="version">@string.Format(Loc[nameof(Dialogs.SettingsDialogVersion)], VersionHelpers.DashboardDisplayVersion)</div>
</FluentStack>
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Dashboard.Extensions;
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Utils;
using Microsoft.AspNetCore.Components;
Expand All @@ -13,7 +12,6 @@ namespace Aspire.Dashboard.Components.Dialogs;
public partial class SettingsDialog : IDialogContentComponent, IAsyncDisposable
{
private string _currentSetting = ThemeManager.ThemeSettingSystem;
private static readonly string? s_version = typeof(SettingsDialog).Assembly.GetDisplayVersion();

private IJSObjectReference? _jsModule;
private IDisposable? _themeChangedSubscription;
Expand Down
3 changes: 3 additions & 0 deletions src/Aspire.Dashboard/Components/Layout/EmptyLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@inherits LayoutComponentBase
@Body
<FluentTooltipProvider />
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Layout/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
IconRest="ResourcesIcon()"
IconActive="ResourcesIcon(active: true)"
Text="@Loc[nameof(Layout.NavMenuResourcesTab)]" />
<FluentAppBarItem Href="@DashboardUrls.ConsoleLogsUrl()"
<FluentAppBarItem Href="@DashboardUrls.ConsoleLogsUrl()"
IconRest="ConsoleLogsIcon()"
IconActive="ConsoleLogsIcon(active: true)"
Text="@Loc[nameof(Layout.NavMenuConsoleLogsTab)]" />
Expand Down
49 changes: 49 additions & 0 deletions src/Aspire.Dashboard/Components/Pages/Login.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@page "/login"
@using Aspire.Dashboard.Utils
@layout EmptyLayout
@attribute [AllowAnonymous]
@inject IStringLocalizer<Dashboard.Resources.Login> Loc

<PageTitle><ApplicationName ResourceName="@nameof(Dashboard.Resources.Login.PageTitle)" Loc="@Loc" /></PageTitle>

<div class="token-backdrop">
<EditForm EditContext="@EditContext" OnValidSubmit="@SubmitAsync" FormName="token_validation">
<DataAnnotationsValidator />
<div class="token-form-container">
<div class="token-logo">
<AspireLogo Height="128" Width="128" />
</div>
<div class="token-entry-container">
<div class="token-entry-header">
<h1><ApplicationName ResourceName="@nameof(Dashboard.Resources.Login.Header)" Loc="@Loc" /></h1>
</div>
<div class="token-entry">
<FluentTextField @ref="_tokenTextField" Id="token-text-field" @bind-Value="_formModel.Token"
Placeholder="@Loc[nameof(Dashboard.Resources.Login.TextFieldPlaceholder)]"
TextFieldType="TextFieldType.Password" Class="token-entry-text" />
</div>
<div class="token-validation">
<FluentValidationMessage For="() => _formModel.Token" />
</div>
<div class="token-entry-footer">
<a href="javascript:;" id="helpLink">@Loc[nameof(Dashboard.Resources.Login.WhereIsMyTokenLinkText)]</a>
<FluentButton Appearance="Appearance.Accent" Type="ButtonType.Submit">@Loc[nameof(Dashboard.Resources.Login.LogInButtonText)]</FluentButton>
<FluentTooltip Anchor="helpLink" Position="TooltipPosition.Bottom">
<div class="token-help-container">
<div class="token-help-text">@Loc[nameof(Dashboard.Resources.Login.HelpPopupText)]</div>
<img class="token-help-image" src="/img/TokenExample.png"
alt="@Loc[nameof(Dashboard.Resources.Login.HelpScreenshotAltText)]" />
<FluentAnchor Href="https://go.microsoft.com/fwlink/?linkid=2265718" Target="_blank"
Appearance="Appearance.Hypertext">
@Loc[nameof(Dashboard.Resources.Login.MoreInfoLinkText)]
</FluentAnchor>
</div>
</FluentTooltip>
</div>
</div>
</div>
</EditForm>
<div class="version-info">
@VersionHelpers.DashboardDisplayVersion
</div>
</div>
Loading

0 comments on commit 4b34931

Please sign in to comment.