I will shorten my question. xD
So, I use VS2022, C#.
Created blazorserverapp with individual accounts using existing VS 2022 template - OK.
DB migration to my postgres DB using EF - done.
Login into created website - OK.
Login/logout works - OK.
Questions:
How can I avoid seeing all elements on the created blazorserver app webpage while I am not logged in?
How can I avoid accessing of the webpage elements through entering of the url, for example: https://localhost:7164/counter while I am not logged in?
What has to be changed in the code to get the showing/accessing (while not logged in) forbidden?
A simple empty (white) page without any elements, except a little window with user/password fields a and a button inside and in the middle of that empty login page, would be nice.
But something like that, which is actually simple, is not available anywhere – I looked for that for weeks in google…
Everybody is showing/presenting a blazorserver login (individual accounts, authentication to SQL Server or - for me ideally - PostgreSQL) example, but: you can see/access all elements while you are not logged in.
Can somebody provide example of that, pls?
Thx best
P.S.: Sorry for my bad English (I am from Austria)
That is my MainLayout.razor:
@inherits LayoutComponentBase
<PageTitle>msgsolutions</PageTitle>
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4 auth">
<LoginDisplay />
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
And here my Index.razor:
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
This is Shared/LoginDisplay.razor:
<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @context.User.Identity?.Name!</a>
<form method="post" action="Identity/Account/LogOut">
<button type="submit" class="nav-link btn btn-link">Log out</button>
</form>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
And here Areas/Identity/Pages/shared/_LoginPartial.cshtml:
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity?.Name!</a>
</li>
<li class="nav-item">
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
Nice would be to get this displayed in the middle of a login page. And this login page has to appear first and no other elements have to be shown on this first page until the user is logged in (example):
<style>
#login .container #login-row #login-column #login-box {
margin-top: 120px;
max-width: 400px;
height: 280px;
border: 1px solid #9C9C9C;
background-color: #EAEAEA;
margin-left: auto;
margin-right: auto;
}
input[type=submit] {
background: #00BBFF;
}
#login .container #login-row #login-column #login-box #login-form {
padding: 20px;
}
#login .container #login-row #login-column #login-box #login-form #register-link {
margin-top: -85px;
}
</style>
<div id="login">
<h3 class="text-center text-white pt-5">Login form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="" method="post">
<h3 class="text-center text-info">Login</h3>
<EditForm Model=@login>
<div class="form-group">
<label for="Username" class="text-info">Username:</label><br>
<InputText @bind-Value=@login.user type="text" name="username" id="username" class="form-control" />
</div>
<div class="form-group">
<label for="Password" class="text-info">Password:</label><br>
<InputText @bind-Value=@login.password type="password" name="password" id="password" class="form-control" />
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-primary btn-outline-primary" value="Authenticate" @onclick="@(()=>IsLoginValid())"></input>
</div>
</EditForm>
</form>
</div>
</div>
</div>
</div>
</div>
Edit (Solved): We were able to manage that using this way: used Index.razor (which contains: "/") and replaced the content with that:
@page "/"
@inject NavigationManager NavigationManager
@inject IJSRuntime jsRuntime
@inject IHttpContextAccessor httpContext
@using System.Net;
<PageTitle>OurLogin</PageTitle>
<style>
#login .container #login-row #login-column #login-box {
margin-top: 120px;
max-width: 400px;
height: 280px;
border: 1px solid #9C9C9C;
background-color: #EAEAEA;
margin-left: auto;
margin-right: auto;
}
input[type=submit] {
background: #00BBFF;
}
#login .container #login-row #login-column #login-box #login-form {
padding: 20px;
}
#login .container #login-row #login-column #login-box #login-form #register-link {
margin-top: -85px;
}
</style>
<div id="login">
<h3 class="text-center text-white pt-5">Login form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="" method="post">
<h3 class="text-center text-info">Login</h3>
<EditForm Model=@login>
<div class="form-group">
<label for="Username" class="text-info">Username:</label><br>
<InputText @bind-Value=@login.user type="text" name="username" id="username" class="form-control" />
</div>
<div class="form-group">
<label for="Password" class="text-info">Password:</label><br>
<InputText @bind-Value=@login.password type="password" name="password" id="password" class="form-control" />
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-primary btn-outline-primary" value="Authenticate" @onclick="@(()=>IsLoginValid())"></input>
</div>
</EditForm>
</form>
</div>
</div>
</div>
</div>
</div>
<p>@login.successmsg</p>
@code
{
public async Task IsLoginValid()
{
string hashedpassword = await hashTask;
string status = await UserAuth(hashedpassword, login.user);
string successmsg = "";
if (status == "Authenticated!")
{
successmsg = "Authentication successful! Redirecting...";
Redirect();
}
else if (status == "Not Authenticated!")
{
successmsg = "Wrong customer ID, username or password! Please try again!";
}
else
{
successmsg = status;
}
Thread.Sleep(500);
login.successmsg = successmsg;
}
[CascadingParameter]
public ErrorHandler? ErrorHandler { get; set; }
}
Maybe it helps somebody who could have similar question. So the case can be closed.
I had the same requirement, on a Blazor Server app, and I solved it this way, I hope it will fit your needs. I was inspired by https://askianoor.medium.com/different-ways-of-authorization-in-blazor-redirect-to-login-blazor-46120d629387.
First, create a distinct layout for the login page, let's call it "LoginLayout.razor"; example:
<div class="container py-3">
<link href="css/login.css" rel="stylesheet">
@Body
<LoginError visible="false"/>
</div>
Then, create a login page, using the login layout; let's call it "Login.razor":
@layout LoginLayout
@page "/login"
@attribute [AllowAnonymous]
<div class="text-center">
... your logo or whatever
<EditForm class="form-signin mb-4" Model="@_credential" OnSubmit="@Authenticate">
<div class="form-group">
<label for="user-name">User name</label>
<InputText class="form-control" id="user-name" aria-describedby="emailHelp" placeholder="Indirizzo e-mail" @bind-Value=@_credential.UserName />
</div>
<div class="form-group">
<label for="password">Password</label>
<InputText type="password" class="form-control" id="password" placeholder="Password" @bind-Value=@_credential.Password />
</div>
<div class="form-group">
<button type="submit" id="login" name="login" class="btn btn-primary">Login</button>
</div>
... other stuff, like "forgot my password", etc...
</EditForm>
</div>
@code {
public class Credential { public string UserName { get; set; } = ""; public string Password { get; set; } = ""; }
private Credential _credential = new Credential();
private void Authenticate()
{
.... perform authentication using _credential fields
}
}
Then, prepare a component which purpose is redirecting to the login page, let's call it "RedirectToLogin.razor":
@inject NavigationManager NavigationManager
@code {
[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; } = null;
protected override async Task OnInitializedAsync()
{
var authState = await AuthState!;
if (authState?.User?.Identity == null || !authState.User.Identity.IsAuthenticated)
{
var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
if (string.IsNullOrEmpty(returnUrl))
NavigationManager.NavigateTo("/login", true);
else
NavigationManager.NavigateTo("/login?returnUrl=" + returnUrl, true);
}
}
}
Finally, most important, modify your App.razor, so that a redirection to the login page is performed, in case the user isn't authenticated:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" >
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
<Authorizing>
<p>Logging in...</p>
</Authorizing>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>