diff --git a/src/AliasVault.Client/Auth/Pages/Unlock.razor b/src/AliasVault.Client/Auth/Pages/Unlock.razor index c2c7bc81a..2537bd47c 100644 --- a/src/AliasVault.Client/Auth/Pages/Unlock.razor +++ b/src/AliasVault.Client/Auth/Pages/Unlock.razor @@ -1,4 +1,5 @@ @page "/unlock" +@page "/unlock/{SkipWebAuthn:bool}" @inherits AliasVault.Client.Auth.Pages.Base.LoginBase @inject ILogger Logger @layout Auth.Layout.MainLayout @@ -25,36 +26,63 @@ else

@Username

-

- Enter your master password in order to unlock your database. -

- - - - - - -
- - - + @if (ShowWebAuthnButton) + { +
+

+ Quickly unlock your vault using your fingerprint, face ID, or security key. Or login with your password as a fallback. +

+
+ + +
+ } + else + { +

+ Enter your master password to unlock your database. +

- + + -
- Switch accounts? Log out -
- + + +
+ + + +
+ + +
+ } + +
+ Switch accounts? Log out +
} @code { + /// + /// Skip automatic WebAuthn unlock during page load if set to true. + /// + [Parameter] + public bool SkipWebAuthn { get; set; } + private string? Username { get; set; } private bool IsLoading { get; set; } = true; - private bool IsWebAuthnLoading { get; set; } = true; + private bool IsWebAuthnLoading { get; set; } + private bool ShowWebAuthnButton { get; set; } private readonly UnlockModel _unlockModel = new(); private FullScreenLoadingIndicator _loadingIndicator = new(); private ServerValidationErrors _serverValidationErrors = new(); @@ -71,11 +99,16 @@ else StatusCheck() ); - // Try to unlock with WebAuthn if enabled. - await UnlockWithWebAuthn(); + // Always check if WebAuthn is enabled + ShowWebAuthnButton = await AuthService.IsWebAuthnEnabledAsync(); + + // Try to unlock with WebAuthn if enabled and not explicitly skipped + if (ShowWebAuthnButton && !SkipWebAuthn) + { + await UnlockWithWebAuthn(); + } IsLoading = false; - IsWebAuthnLoading = false; StateHasChanged(); } } @@ -243,6 +276,20 @@ else { Logger.LogError(ex, "An error occurred while trying to unlock the vault with WebAuthn."); } + finally + { + IsWebAuthnLoading = false; + StateHasChanged(); + } } } + + /// + /// Show the password login form. + /// + private void ShowPasswordLogin() + { + ShowWebAuthnButton = false; + StateHasChanged(); + } } diff --git a/src/AliasVault.Client/Main/Layout/DbLockButton.razor b/src/AliasVault.Client/Main/Layout/DbLockButton.razor index cfc08648f..691c3c609 100644 --- a/src/AliasVault.Client/Main/Layout/DbLockButton.razor +++ b/src/AliasVault.Client/Main/Layout/DbLockButton.razor @@ -22,7 +22,7 @@ // Initialize empty database which removes unencrypted data. DbService.InitializeEmptyDatabase(); - // Redirect to unlock page. - NavigationManager.NavigateTo("/unlock"); + // Redirect to unlock page with SkipWebAuthn parameter set to true. + NavigationManager.NavigateTo("/unlock/true"); } } diff --git a/src/AliasVault.Client/wwwroot/css/tailwind.css b/src/AliasVault.Client/wwwroot/css/tailwind.css index f55868ba1..e576ae7e1 100644 --- a/src/AliasVault.Client/wwwroot/css/tailwind.css +++ b/src/AliasVault.Client/wwwroot/css/tailwind.css @@ -1565,21 +1565,11 @@ video { padding-right: 1.75rem; } -.px-8 { - padding-left: 2rem; - padding-right: 2rem; -} - .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; } -.py-12 { - padding-top: 3rem; - padding-bottom: 3rem; -} - .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -1600,8 +1590,8 @@ video { padding-bottom: 1rem; } -.pb-0 { - padding-bottom: 0px; +.pb-28 { + padding-bottom: 7rem; } .pb-4 { @@ -1644,18 +1634,6 @@ video { padding-top: 2rem; } -.pb-10 { - padding-bottom: 2.5rem; -} - -.pb-20 { - padding-bottom: 5rem; -} - -.pb-28 { - padding-bottom: 7rem; -} - .text-left { text-align: left; } @@ -2203,6 +2181,11 @@ video { --tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity)); } +.focus\:ring-gray-100:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(243 244 246 / var(--tw-ring-opacity)); +} + .focus\:ring-offset-2:focus { --tw-ring-offset-width: 2px; } @@ -2596,12 +2579,22 @@ video { width: auto; } + .sm\:flex-row { + flex-direction: row; + } + .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } + .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + .sm\:rounded-lg { border-radius: 0.5rem; } @@ -2650,10 +2643,6 @@ video { } @media (min-width: 768px) { - .md\:relative { - position: relative; - } - .md\:col-span-1 { grid-column: span 1 / span 1; } @@ -2742,14 +2731,6 @@ video { margin-top: 0px; } - .lg\:mb-4 { - margin-bottom: 1rem; - } - - .lg\:mb-2 { - margin-bottom: 0.5rem; - } - .lg\:block { display: block; } @@ -2833,18 +2814,10 @@ video { padding-bottom: 4rem; } - .lg\:pb-0 { - padding-bottom: 0px; - } - .lg\:pb-4 { padding-bottom: 1rem; } - .lg\:pt-4 { - padding-top: 1rem; - } - .lg\:pt-6 { padding-top: 1.5rem; } diff --git a/src/AliasVault.Client/wwwroot/index.template.html b/src/AliasVault.Client/wwwroot/index.template.html index 03c273b8f..8f47be9a1 100644 --- a/src/AliasVault.Client/wwwroot/index.template.html +++ b/src/AliasVault.Client/wwwroot/index.template.html @@ -37,7 +37,7 @@
-
+