Integrated AuthenticationStateProvider using .NET Core Identity
This commit is contained in:
@ -1,9 +1,11 @@
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Client.Shared
|
||||
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
@code {
|
||||
private PageState PageState { get; set; }
|
||||
|
@ -1,45 +1,56 @@
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Modules
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Client.Modules.Controls
|
||||
@using Oqtane.Providers
|
||||
@inherits ModuleBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IUserService UserService
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
|
||||
<div class="container">
|
||||
@((MarkupString)Message)
|
||||
<div class="form-group">
|
||||
<label for="Username" class="control-label">Username: </label>
|
||||
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@Username" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="@Login">Login</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
You are already logged in
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<div class="container">
|
||||
@((MarkupString)Message)
|
||||
<div class="form-group">
|
||||
<label for="Username" class="control-label">Username: </label>
|
||||
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@Username" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="@Login">Login</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
|
||||
public string Message { get; set; } = "<div class=\"alert alert-info\" role=\"alert\">Use host/host For Demo Access</div>";
|
||||
public string Message { get; set; } = "<div class=\"alert alert-info\" role=\"alert\">Use host/password For Demo Access</div>";
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
private async Task Login()
|
||||
{
|
||||
List<User> users = await UserService.GetUsersAsync();
|
||||
User user = users.Where(item => item.Username == Username).FirstOrDefault();
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.Password = Password;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SetCookie("user", user.UserId.ToString(), 7);
|
||||
UriHelper.NavigateTo(NavigateUrl(""), true);
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,12 +1,10 @@
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Modules
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Client.Modules.Controls
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Services
|
||||
@inherits ModuleBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
|
||||
@inject IUserService UserService
|
||||
|
||||
<div class="container">
|
||||
<div class="form-group">
|
||||
@ -17,13 +15,25 @@
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">Register</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">Register</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
private async Task RegisterUser()
|
||||
{
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.DisplayName = Username;
|
||||
user.Roles = "Administrators;";
|
||||
user.IsSuperUser = false;
|
||||
user.Password = Password;
|
||||
await UserService.AddUserAsync(user);
|
||||
UriHelper.NavigateTo("");
|
||||
}
|
||||
}
|
||||
|
39
Oqtane.Client/Providers/ServerAuthenticationStateProvider.cs
Normal file
39
Oqtane.Client/Providers/ServerAuthenticationStateProvider.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class ServerAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
//private readonly IUserService UserService;
|
||||
private readonly IUriHelper urihelper;
|
||||
|
||||
public ServerAuthenticationStateProvider(IUriHelper urihelper)
|
||||
{
|
||||
//this.UserService = UserService;
|
||||
this.urihelper = urihelper;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
// hack: create a new HttpClient rather than relying on the registered service as the AuthenticationStateProvider is initialized prior to IUriHelper ( https://github.com/aspnet/AspNetCore/issues/11867 )
|
||||
HttpClient http = new HttpClient();
|
||||
Uri uri = new Uri(urihelper.GetAbsoluteUri());
|
||||
string apiurl = uri.Scheme + "://" + uri.Authority + "/~/api/User/authenticate";
|
||||
User user = await http.GetJsonAsync<User>(apiurl);
|
||||
var identity = user.IsAuthenticated
|
||||
? new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, user.Username) }, "Identity.Application")
|
||||
: new ClaimsIdentity();
|
||||
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||
}
|
||||
|
||||
public void NotifyAuthenticationChanged()
|
||||
{
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,12 @@ namespace Oqtane.Services
|
||||
|
||||
Task DeleteUserAsync(int UserId);
|
||||
|
||||
Task<User> GetCurrentUserAsync();
|
||||
|
||||
Task<User> LoginUserAsync(User user);
|
||||
|
||||
Task LogoutUserAsync();
|
||||
|
||||
bool IsAuthorized(User user, string accesscontrollist);
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ namespace Oqtane.Services
|
||||
{
|
||||
private readonly HttpClient http;
|
||||
private readonly SiteState sitestate;
|
||||
private readonly IUriHelper urihelper;
|
||||
|
||||
public UserService(HttpClient http, SiteState sitestate)
|
||||
public UserService(HttpClient http, SiteState sitestate, IUriHelper urihelper)
|
||||
{
|
||||
this.http = http;
|
||||
this.sitestate = sitestate;
|
||||
this.urihelper = urihelper;
|
||||
}
|
||||
|
||||
private string apiurl
|
||||
@ -35,7 +37,7 @@ namespace Oqtane.Services
|
||||
{
|
||||
List<User> users = await http.GetJsonAsync<List<User>>(apiurl);
|
||||
return users.Where(item => item.UserId == UserId).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddUserAsync(User user)
|
||||
{
|
||||
@ -51,6 +53,22 @@ namespace Oqtane.Services
|
||||
await http.DeleteAsync(apiurl + "/" + UserId.ToString());
|
||||
}
|
||||
|
||||
public async Task<User> GetCurrentUserAsync()
|
||||
{
|
||||
return await http.GetJsonAsync<User>(apiurl + "/current");
|
||||
}
|
||||
|
||||
public async Task<User> LoginUserAsync(User user)
|
||||
{
|
||||
return await http.PostJsonAsync<User>(apiurl + "/login", user);
|
||||
}
|
||||
|
||||
public async Task LogoutUserAsync()
|
||||
{
|
||||
// best practices recommend post is preferrable to get for logout
|
||||
await http.PostJsonAsync(apiurl + "/logout", null);
|
||||
}
|
||||
|
||||
// ACLs are stored in the format "!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]" where "!" designates Deny permissions
|
||||
public bool IsAuthorized(User user, string accesscontrollist)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
@using Oqtane.Shared
|
||||
@using Microsoft.JSInterop
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject SiteState SiteState
|
||||
@inject IUriHelper UriHelper
|
||||
@inject INavigationInterception NavigationInterception
|
||||
@ -106,15 +107,13 @@ private async Task Refresh()
|
||||
}
|
||||
if (site != null || reload == true)
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string userid = await interop.GetCookie("user");
|
||||
|
||||
user = null;
|
||||
if (PageState == null || reload == true)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
user = await UserService.GetCurrentUserAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -122,23 +121,6 @@ private async Task Refresh()
|
||||
user = PageState.User;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
if (user != null && user.UserId != int.Parse(userid))
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
}
|
||||
// this is a hack for server-side Blazor where JSInterop is not working OnInit() which means the userid is not being retrieved from the cookie on the initial render and is not being loaded into PageState
|
||||
if (user == null)
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
|
||||
string path = new Uri(_absoluteUri).PathAndQuery.Substring(1);
|
||||
if (path.EndsWith("/")) { path = path.Substring(0, path.Length - 1); }
|
||||
if (alias.Path != "")
|
||||
|
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Components;
|
||||
using System.Reflection;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Providers;
|
||||
using Microsoft.AspNetCore.Blazor.Http;
|
||||
|
||||
namespace Oqtane.Client
|
||||
{
|
||||
@ -27,6 +29,11 @@ namespace Oqtane.Client
|
||||
#if WASM
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// register auth services
|
||||
services.AddAuthorizationCore();
|
||||
services.AddScoped<ServerAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<ServerAuthenticationStateProvider>());
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
|
||||
@ -39,6 +46,7 @@ namespace Oqtane.Client
|
||||
services.AddScoped<IPageModuleService, PageModuleService>();
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
|
||||
|
||||
// dynamically register module contexts and repository services
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
@ -63,6 +71,7 @@ namespace Oqtane.Client
|
||||
|
||||
public void Configure(IComponentsApplicationBuilder app)
|
||||
{
|
||||
WebAssemblyHttpMessageHandler.DefaultCredentials = FetchCredentialsOption.Include;
|
||||
app.AddComponent<App>("app");
|
||||
}
|
||||
#endif
|
||||
|
@ -1,41 +1,34 @@
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Shared
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Providers
|
||||
@inherits ThemeObjectBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@LogoutUser">Logout</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@LoginUser">Login</button>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<button type="button" class="btn btn-primary" @onclick="@Click">@name</button>
|
||||
|
||||
@code {
|
||||
string name = "";
|
||||
|
||||
protected override async Task OnInitAsync()
|
||||
private void LoginUser()
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string user = await interop.GetCookie("user");
|
||||
|
||||
if (user == "")
|
||||
{
|
||||
name = "Login";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = "Logout";
|
||||
}
|
||||
UriHelper.NavigateTo(NavigateUrl("login"));
|
||||
}
|
||||
|
||||
private async Task Click()
|
||||
private async Task LogoutUser()
|
||||
{
|
||||
if (name == "Login")
|
||||
{
|
||||
UriHelper.NavigateTo(NavigateUrl("login"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SetCookie("user", "", 7);
|
||||
UriHelper.NavigateTo(NavigateUrl(""), true);
|
||||
}
|
||||
await UserService.LogoutUserAsync();
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,25 @@
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Services;
|
||||
@using Oqtane.Models;
|
||||
@using Microsoft.JSInterop
|
||||
@inject IJSRuntime jsRuntime
|
||||
@using Oqtane.Themes
|
||||
@inherits ThemeObjectBase
|
||||
@inject IUriHelper UriHelper
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">@context.User.Identity.Name</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">Register</button>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<NavLink class="btn btn-primary" href="@url">@name</NavLink>
|
||||
|
||||
@code {
|
||||
string name = "";
|
||||
string url = "";
|
||||
|
||||
protected override async Task OnInitAsync()
|
||||
private void RegisterUser()
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string userid = await interop.GetCookie("user");
|
||||
|
||||
if (userid == "")
|
||||
{
|
||||
name = "Register";
|
||||
url = "register";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
name = PageState.User.DisplayName;
|
||||
}
|
||||
}
|
||||
UriHelper.NavigateTo(NavigateUrl("register"));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user