feat: added docker stuff, started working on login system
This commit is contained in:
parent
c2d40c172a
commit
7c0cef6f65
15 changed files with 216 additions and 143 deletions
25
.dockerignore
Normal file
25
.dockerignore
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.idea
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
|
@ -1,16 +1,18 @@
|
||||||
<Router AppAssembly="@typeof(App).Assembly">
|
<CascadingAuthenticationState>
|
||||||
<Found Context="routeData">
|
<Router AppAssembly="@typeof(App).Assembly">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
<Found Context="routeData">
|
||||||
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
</Found>
|
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
|
||||||
<NotFound>
|
</Found>
|
||||||
<PageTitle>Not found</PageTitle>
|
<NotFound>
|
||||||
<LayoutView Layout="@typeof(MainLayout)">
|
<PageTitle>Not found</PageTitle>
|
||||||
<h3>404</h3>
|
<LayoutView Layout="@typeof(MainLayout)">
|
||||||
<div class="Error404">
|
<h3>404</h3>
|
||||||
<img src="img/static/err/1.jpeg" alt="noimageFound"/>
|
<div class="Error404">
|
||||||
<p role="alert">Sorry, nothing found. Please go back to the main page. Or watch the tree and find the hidden Cat..</p>
|
<img src="img/static/err/1.jpeg" alt="noimageFound"/>
|
||||||
</div>
|
<p role="alert">Sorry, nothing found. Please go back to the main page. Or watch the tree and find the hidden Cat..</p>
|
||||||
</LayoutView>
|
</div>
|
||||||
</NotFound>
|
</LayoutView>
|
||||||
</Router>
|
</NotFound>
|
||||||
|
</Router>
|
||||||
|
</CascadingAuthenticationState>
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using ImageBoardServerApp.Data;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
|
|
||||||
|
namespace ImageBoardServerApp.Auth;
|
||||||
|
|
||||||
|
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
||||||
|
{
|
||||||
|
private readonly ProtectedSessionStorage _sessionStorage;
|
||||||
|
private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
|
public CustomAuthenticationStateProvider(ProtectedSessionStorage sessionStorage)
|
||||||
|
{
|
||||||
|
_sessionStorage = sessionStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userSessionStorageResult = await _sessionStorage.GetAsync<UserData>("UserSession");
|
||||||
|
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
|
||||||
|
if (userSession == null)
|
||||||
|
return await Task.FromResult(new AuthenticationState(_anonymous));
|
||||||
|
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.Email, userSession.Email),
|
||||||
|
new Claim(ClaimTypes.Role, userSession.Role)
|
||||||
|
}, "CustomAuth"));
|
||||||
|
return await Task.FromResult(new AuthenticationState(claimsPrincipal));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return await Task.FromResult(new AuthenticationState(_anonymous));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAuthenticationStateAsync(UserData session)
|
||||||
|
{
|
||||||
|
ClaimsPrincipal claimsPrincipal;
|
||||||
|
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
await _sessionStorage.SetAsync("UserSession", session);
|
||||||
|
claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.Email, session.Email),
|
||||||
|
new Claim(ClaimTypes.Email, session.Role)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _sessionStorage.DeleteAsync("UserSession");
|
||||||
|
claimsPrincipal = _anonymous;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
|
||||||
|
}
|
||||||
|
}
|
7
ImageBoardServerApp/Auth/UserSession.cs
Normal file
7
ImageBoardServerApp/Auth/UserSession.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace ImageBoardServerApp.Auth;
|
||||||
|
|
||||||
|
public class UserSession
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Role { get; set; }
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace ImageBoardServerApp.Data;
|
|
||||||
|
|
||||||
public class AccountData
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
[DatabaseGenerated((DatabaseGeneratedOption.Identity))]
|
|
||||||
[Key]
|
|
||||||
public int AccountID { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public string Email { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public int PermissionInteger { get; set; }
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ internal sealed class AppDBContext : DbContext
|
||||||
public DbSet<PostData> Posts { get; set; }
|
public DbSet<PostData> Posts { get; set; }
|
||||||
public DbSet<ImageData> Images { get; set; }
|
public DbSet<ImageData> Images { get; set; }
|
||||||
public DbSet<CommentData> Comments { get; set; }
|
public DbSet<CommentData> Comments { get; set; }
|
||||||
public DbSet<AccountData> Accounts { get; set; }
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder.UseSqlite("Data Source=./Data/Nils.db");
|
=> optionsBuilder.UseSqlite("Data Source=./Data/Nils.db");
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace ImageBoardServerApp.Data.Repository;
|
|
||||||
|
|
||||||
public static class AccountsRepository
|
|
||||||
{
|
|
||||||
public static async Task<List<AccountData>> getAccountsAsync()
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
return await db.Accounts.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<AccountData>> getAccountsByMailAsync(string email)
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
return await db.Accounts
|
|
||||||
.Where(acc => acc.Email.Equals(email))
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<AccountData> getAccountByIdAsync(int accountId)
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
return await db.Accounts.FirstOrDefaultAsync(post => post.AccountID == accountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<int> createAccountAsync(AccountData accountToCreate)
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
await db.Accounts.AddAsync(accountToCreate);
|
|
||||||
if (await db.SaveChangesAsync() >= 1)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Created post with ID: {accountToCreate.AccountID}");
|
|
||||||
return accountToCreate.AccountID;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<bool> updateAccountAsync(AccountData accountToUpdate)
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
db.Accounts.Update(accountToUpdate);
|
|
||||||
return await db.SaveChangesAsync() >= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<bool> deleteAccountAsync(int postId)
|
|
||||||
{
|
|
||||||
await using var db = new AppDBContext();
|
|
||||||
AccountData accountToDelete = await getAccountByIdAsync(postId);
|
|
||||||
db.Remove(accountToDelete);
|
|
||||||
return await db.SaveChangesAsync() >= 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,10 +16,10 @@ public static class UsersRepository
|
||||||
return await db.Users.FirstOrDefaultAsync(user => user.UserID == userId);
|
return await db.Users.FirstOrDefaultAsync(user => user.UserID == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<UserData> getUserByIPAsync(string userIp)
|
public static async Task<UserData> getUserByEmailAsync(string email)
|
||||||
{
|
{
|
||||||
await using var db = new AppDBContext();
|
await using var db = new AppDBContext();
|
||||||
return await db.Users.FirstOrDefaultAsync(user => user.Ipv4Address == userIp);
|
return await db.Users.FirstOrDefaultAsync(user => user.Email == email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> createUserAsync(UserData userToCreate)
|
public static async Task<int> createUserAsync(UserData userToCreate)
|
||||||
|
|
|
@ -13,10 +13,7 @@ public class UserData
|
||||||
public int UserID { get; set; }
|
public int UserID { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string Ipv4Address { get; set; }
|
public long TimeBanned { get; set; }
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool Banned { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public long lastActionTimeStamp { get; set; }
|
public long lastActionTimeStamp { get; set; }
|
||||||
|
@ -24,4 +21,16 @@ public class UserData
|
||||||
public List<PostData> Posts { get; set; }
|
public List<PostData> Posts { get; set; }
|
||||||
|
|
||||||
public List<CommentData> Comments { get; set; }
|
public List<CommentData> Comments { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int PermissionInteger { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Role { get; set; }
|
||||||
}
|
}
|
20
ImageBoardServerApp/Dockerfile
Normal file
20
ImageBoardServerApp/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["ImageBoardServerApp/ImageBoardServerApp.csproj", "ImageBoardServerApp/"]
|
||||||
|
RUN dotnet restore "ImageBoardServerApp/ImageBoardServerApp.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/BlazorServerTest"
|
||||||
|
RUN dotnet build "BlazorServerTest.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "ImageBoardServerApp.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "ImageBoardServerApp.dll"]
|
|
@ -1,6 +1,12 @@
|
||||||
@page "/Login"
|
@page "/login"
|
||||||
@using ImageBoardServerApp.Data.Repository
|
@using ImageBoardServerApp.Data.Repository
|
||||||
<h3>Login</h3>
|
@using ImageBoardServerApp.Auth
|
||||||
|
@using System.Runtime.InteropServices
|
||||||
|
@inject IJSRuntime js
|
||||||
|
@inject AuthenticationStateProvider authStateProvider
|
||||||
|
@inject NavigationManager navManager
|
||||||
|
|
||||||
|
<h3>Login to bulletbroards</h3>
|
||||||
<div>
|
<div>
|
||||||
<form>
|
<form>
|
||||||
<div>
|
<div>
|
||||||
|
@ -11,48 +17,41 @@
|
||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input type="password" id="password" @bind="Password" />
|
<input type="password" id="password" @bind="Password" />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" @onclick="SubmitForm">Submit</button>
|
<button type="submit" href="javascript:void(0)" @onclick="login">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@if (tried)
|
|
||||||
{
|
|
||||||
@if (verified)
|
|
||||||
{
|
|
||||||
<span>Verifed!</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<span>False login</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<span>Plz login</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@code {
|
@code {
|
||||||
private string Email { get; set; }
|
private string Email { get; set; }
|
||||||
private string Password { get; set; }
|
private string Password { get; set; }
|
||||||
|
|
||||||
private bool verified = false;
|
private bool verified;
|
||||||
private bool tried = false;
|
|
||||||
|
|
||||||
private async Task SubmitForm()
|
private async Task login()
|
||||||
{
|
{
|
||||||
tried = true;
|
Console.WriteLine("loggin you in...");
|
||||||
AccountData target = (await AccountsRepository.getAccountsByMailAsync(Email))[0];
|
var user = await UsersRepository.getUserByEmailAsync(Email);
|
||||||
if (target == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
|
await js.InvokeVoidAsync("alert", "User does not exist");
|
||||||
verified = false;
|
verified = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verified = BCrypt.Net.BCrypt.Verify(Password, target.Password);
|
verified = BCrypt.Net.BCrypt.Verify(Password, user.Password);
|
||||||
if (verified)
|
if (verified)
|
||||||
{
|
{
|
||||||
verified = true;
|
verified = true;
|
||||||
|
var customAuthStateProvider = (CustomAuthenticationStateProvider)authStateProvider;
|
||||||
|
await customAuthStateProvider.UpdateAuthenticationStateAsync(user);
|
||||||
|
navManager.NavigateTo("/", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verified = false;
|
await js.InvokeVoidAsync("alert", $"Wrong creds:\n{BCrypt.Net.BCrypt.HashPassword(Password)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
UserData target = (await UsersRepository.getUserByEmailAsync(Email));
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
using ImageBoardServerApp.Data;
|
using ImageBoardServerApp.Auth;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
builder.Services.AddAuthenticationCore();
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddServerSideBlazor();
|
builder.Services.AddServerSideBlazor();
|
||||||
|
builder.Services.AddScoped<ProtectedSessionStorage>();
|
||||||
|
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
|
@ -87,13 +87,15 @@
|
||||||
{
|
{
|
||||||
var userToCreate = new UserData
|
var userToCreate = new UserData
|
||||||
{
|
{
|
||||||
Ipv4Address = "192.168.178.101",
|
Email = "dev@limited-dev.de",
|
||||||
Banned = false,
|
Password = "$2a$10$C/ZPY5aVGkImLGyIP0SySuQaEYIwnY0J99i/m6tqqf6tMkax89Eku",
|
||||||
|
PermissionInteger = 100,
|
||||||
|
TimeBanned = -1,
|
||||||
lastActionTimeStamp = DateTime.Now.Millisecond
|
lastActionTimeStamp = DateTime.Now.Millisecond
|
||||||
};
|
};
|
||||||
|
|
||||||
int userID;
|
int userID;
|
||||||
UserData foundusr = await UsersRepository.getUserByIPAsync(userToCreate.Ipv4Address);
|
UserData foundusr = await UsersRepository.getUserByEmailAsync(userToCreate.Email);
|
||||||
if (foundusr == null)
|
if (foundusr == null)
|
||||||
{
|
{
|
||||||
userID = await UsersRepository.createUserAsync(userToCreate);
|
userID = await UsersRepository.createUserAsync(userToCreate);
|
||||||
|
@ -101,9 +103,9 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
userID = foundusr.UserID;
|
userID = foundusr.UserID;
|
||||||
if(foundusr.Banned)
|
if(foundusr.TimeBanned != -1)
|
||||||
return;
|
return;
|
||||||
foundusr.lastActionTimeStamp = DateTime.Now.Millisecond;
|
foundusr.lastActionTimeStamp = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
|
||||||
await UsersRepository.updateUserAsync(foundusr);
|
await UsersRepository.updateUserAsync(foundusr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,13 +100,15 @@
|
||||||
{
|
{
|
||||||
var userToCreate = new UserData
|
var userToCreate = new UserData
|
||||||
{
|
{
|
||||||
Ipv4Address = "192.168.178.101",
|
Email = "test@mail.org",
|
||||||
Banned = false,
|
Password = "$2a$10$C/ZPY5aVGkImLGyIP0SySuQaEYIwnY0J99i/m6tqqf6tMkax89Eku",
|
||||||
|
PermissionInteger = 100,
|
||||||
|
TimeBanned = -1,
|
||||||
lastActionTimeStamp = DateTime.Now.Millisecond
|
lastActionTimeStamp = DateTime.Now.Millisecond
|
||||||
};
|
};
|
||||||
|
|
||||||
int userID;
|
int userID;
|
||||||
UserData foundusr = await UsersRepository.getUserByIPAsync(userToCreate.Ipv4Address);
|
UserData foundusr = await UsersRepository.getUserByEmailAsync(userToCreate.Email);
|
||||||
if (foundusr == null)
|
if (foundusr == null)
|
||||||
{
|
{
|
||||||
userID = await UsersRepository.createUserAsync(userToCreate);
|
userID = await UsersRepository.createUserAsync(userToCreate);
|
||||||
|
@ -114,9 +116,9 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
userID = foundusr.UserID;
|
userID = foundusr.UserID;
|
||||||
if(foundusr.Banned)
|
if(foundusr.TimeBanned != -1)
|
||||||
return;
|
return;
|
||||||
foundusr.lastActionTimeStamp = DateTime.Now.Millisecond;
|
foundusr.lastActionTimeStamp = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
|
||||||
await UsersRepository.updateUserAsync(foundusr);
|
await UsersRepository.updateUserAsync(foundusr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
@using ImageBoardServerApp.Auth
|
||||||
|
@inject AuthenticationStateProvider authStateProvder
|
||||||
|
@inject NavigationManager navManager
|
||||||
|
|
||||||
<PageTitle>BulletBoard</PageTitle>
|
<PageTitle>BulletBoard</PageTitle>
|
||||||
|
|
||||||
|
@ -11,6 +14,14 @@
|
||||||
<div class="top-row px-4">
|
<div class="top-row px-4">
|
||||||
<a href="/faq">[FAQ]</a>
|
<a href="/faq">[FAQ]</a>
|
||||||
<a href="/rules">[Rules]</a>
|
<a href="/rules">[Rules]</a>
|
||||||
|
<AuthorizeView>
|
||||||
|
<Authorized>
|
||||||
|
<a @onclick="logout" href="javascript:void(0)">[Logout]</a>
|
||||||
|
</Authorized>
|
||||||
|
<NotAuthorized>
|
||||||
|
<a href="/login">[Login]</a>
|
||||||
|
</NotAuthorized>
|
||||||
|
</AuthorizeView>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="content px-4">
|
<article class="content px-4">
|
||||||
|
@ -18,3 +29,13 @@
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private async Task logout()
|
||||||
|
{
|
||||||
|
var customAuthStateProvider = (CustomAuthenticationStateProvider)authStateProvder;
|
||||||
|
await customAuthStateProvider.UpdateAuthenticationStateAsync(null);
|
||||||
|
navManager.NavigateTo("/", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue